use clap::Parser; use std::path::Path; use anyhow::Result; use walkdir::WalkDir; #[derive(Parser, Debug)] #[command(version, about, author)] struct Args { source: String, destination: String, #[arg(short, long, default_value_t = 8)] jobs: usize, } async fn copy(from: String, to: String) -> std::io::Result<()> { let from_path = std::path::Path::new(&from); let to_path = std::path::Path::new(&to); if !to_path.exists() { if from_path.is_dir() { std::fs::create_dir_all(to)?; } else if from_path.is_file() { std::fs::create_dir_all(to_path.parent().unwrap())?; std::fs::copy(from, to)?; } } Ok(()) } async fn run(args: Args) -> Result<()> { let source = Path::new(&args.source); let destination = Path::new(&args.destination); let mut tasks: Vec>> = Vec::new(); for (count, entry) in WalkDir::new(source).into_iter().filter_map(|e| e.ok()).enumerate() { let from = entry.path().to_str().unwrap().to_string(); let rel = entry.path().strip_prefix(source)?; let to = { let mut target = destination.to_path_buf(); target.push(rel); target.to_str().unwrap().to_string() }; tasks.push(tokio::spawn(copy(from, to))); print!("copied {count} files\r"); } println!("\nsyncing filesystem"); for task in tasks { match task.await { Ok(result) => { if let Err(e) = result { eprintln!("Error: {:?}", e); } } Err(e) => eprintln!("Runtime Error: {:?}", e) } } Ok(()) } fn main() -> Result<()> { let args = Args::parse(); let runtime = tokio::runtime::Builder::new_multi_thread() .worker_threads(args.jobs) .enable_all() .build() .unwrap(); runtime.block_on(run(args)) }