use rayon::prelude::*; use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::os::unix; use std::os::unix::fs::MetadataExt; use std::sync::mpsc; use std::thread; fn hardcopy( source: &Path, destination: &Path, conflict_sender: Option, mpsc::Sender)>>, ) -> io::Result<()> { let metadata = fs::symlink_metadata(source)?; if metadata.file_type().is_file() { match fs::hard_link(source, destination) { Ok(_) => {} Err(_) => { if let Ok(dest_metadata) = fs::metadata(destination) { if dest_metadata.ino() == metadata.ino() { return Ok(()); } } match crate::utils::parser::get_index_conflict(destination) { Ok(index_source) => { if index_source == source { fs::remove_file(destination)?; fs::hard_link(source, destination)?; } else { return Ok(()); } } Err(_) => { let conflict_list = find_files_with_location(&destination); let count = conflict_list.len(); if count == 1 { fs::remove_file(destination)?; fs::hard_link(source, destination)?; } else if count >= 1 { let (response_tx, response_rx) = mpsc::channel(); if let Some(sender) = &conflict_sender { sender.send((conflict_list.clone(), response_tx)).unwrap(); } let selected_source = response_rx.recv().unwrap(); append_index_block(&selected_source, &destination)?; if selected_source == source { fs::remove_file(destination)?; fs::hard_link(source, destination)?; } else { return Ok(()); } } } } } } } else if metadata.file_type().is_dir() { fs::create_dir_all(destination)?; let entries: Vec<_> = fs::read_dir(source)?.collect::>>()?; entries.par_iter().try_for_each(|entry| { let path_source = entry.path(); if let Some(file_name) = path_source.file_name() { let dest_path = destination.join(file_name); hardcopy(&path_source, &dest_path, conflict_sender.clone()) } else { Ok(()) } })?; } else if metadata.file_type().is_symlink() { let symlink_value = fs::read_link(source)?; if destination.exists() { fs::remove_file(destination)? } unix::fs::symlink(symlink_value, destination)?; } Ok(()) } pub fn hardcopy_handler( source: &Path, destination: &Path, ) -> io::Result<()> { let (tx, rx): ( mpsc::Sender<(Vec, mpsc::Sender)>, mpsc::Receiver<(Vec, mpsc::Sender)>, ) = mpsc::channel(); thread::spawn(move || { for (conflict_list, response_tx) in rx { let selected_source = choise_index_conflict(conflict_list); response_tx.send(selected_source).unwrap(); } }); hardcopy(source, destination, Some(tx)) } fn find_files_with_location(destination: &Path) -> Vec { let mut found_files = Vec::new(); let mut components = destination.components(); let prefix = match (components.next(), components.next(), components.next()) { (Some(first), Some(second), Some(third)) => { PathBuf::from(first.as_os_str()) .join(second.as_os_str()) .join(third.as_os_str()) } _ => { return Vec::new(); } }; let file_location: PathBuf = components.as_path().to_path_buf(); if let Ok(entries) = fs::read_dir(&prefix) { for entry in entries.filter_map(Result::ok) { let path = entry.path(); if path.is_dir() { let target_path = path.join(&file_location); if target_path.exists() { if !path.join(PathBuf::from("disabled")).exists() { found_files.push(target_path); } } } } } found_files } fn choise_index_conflict(conflict_list: Vec) -> PathBuf { for (index, path) in conflict_list.iter().enumerate() { println!("{}: {}", index + 1, path.display()); } let count = conflict_list.len(); loop { print!("Choose a path to resolve the conflict (1-{}): ", count); io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin() .read_line(&mut input) .expect("Failed to read input"); match input.trim().parse::() { Ok(selected) if selected >= 1 && selected <= count => { return conflict_list[selected - 1].clone(); } _ => { println!("Invalid input. Please enter a number between 1 and {}.", count); } } } } fn append_index_block(source: &Path, destination: &Path) -> io::Result<()> { let source_components: Vec<_> = source.iter().collect(); let base_system_folder = source_components[4].to_str().unwrap(); let index_conflict_path = Path::new("/pkg/gnu/aeropkg/etc/index-conflict.md"); let content = fs::read_to_string(&index_conflict_path)?; let start_marker = format!("``` cfg *** {} ***", base_system_folder); let lines: Vec<&str> = content.lines().collect(); let mut start_block_index = None; let mut end_block_index = None; for (i, line) in lines.iter().enumerate() { if line.contains(&start_marker) { start_block_index = Some(i); } else if start_block_index.is_some() && line.trim() == "```" { end_block_index = Some(i); break; } } let end_block_index = end_block_index.ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidData, "End block not found") })?; let new_line = format!( "{} {}", destination.to_str().unwrap(), source.to_str().unwrap() ); let mut new_content = String::new(); for (i, line) in lines.iter().enumerate() { if i == end_block_index { new_content.push_str(&new_line); new_content.push('\n'); } new_content.push_str(line); new_content.push('\n'); } let mut file = fs::File::create(&index_conflict_path)?; file.write_all(new_content.as_bytes())?; Ok(()) }