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()))
}
|