summaryrefslogtreecommitdiff
path: root/src/bin/summain.rs
blob: 5031006869faa4f7703e8024a771c3ef70f7b1a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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<PathBuf>,
}

async fn manifest(path: PathBuf) -> anyhow::Result<ManifestEntry> {
    ManifestEntry::new(&path)
        .await
        .with_context(|| format!("{}", path.display()))
}