use anyhow::Context; use std::path::PathBuf; use structopt::StructOpt; use summain::ManifestEntry; #[tokio::main] async fn main() -> anyhow::Result<()> { let mut opt = Opt::from_args(); opt.pathnames[..].sort(); // Get metadata about all files, but don't compute checksum yet. // // This all runs in async worker threads, since it's theoretically // I/O heavy and benefits from async task context switching. We // create a task for each named file, since tasks are very cheap. // We keep the JoinHandle for each task in a vector: there's not // going to be so many handles that the vector becomes impossibly // large: Linux only allows 128 KiB of command line arguments. let mut handles = vec![]; for filename in opt.pathnames.iter().cloned() { handles.push(tokio::spawn(async move { manifest(filename) })); } // Compute checksums for regular files. // // This runs in blocking threads, since it's CPU heavy. We create // another vector of JoinHandles. Again, it won't become too large. let mut sumhandles = vec![]; for h in handles { let mut m: ManifestEntry = h.await?.await?; let h = tokio::task::spawn_blocking(move || match m.compute_checksum() { Err(e) => Err(e), Ok(_) => Ok(m), }); sumhandles.push(h) } // Wait for checksums to be available and print manifest. // // Note how this iterates over the results in the right order. for h in sumhandles { let m: ManifestEntry = h.await??; print!("{}", serde_yaml::to_string(&m)?); } Ok(()) } #[derive(StructOpt, Debug)] struct Opt { #[structopt(parse(from_os_str))] pathnames: Vec, } async fn manifest(path: PathBuf) -> anyhow::Result { ManifestEntry::new(&path) .await .with_context(|| format!("{}", path.display())) }