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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
use anyhow::Context;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
use summain::ManifestEntry;
use walkdir::WalkDir;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let opt = Opt::from_args();
let mut files = vec![];
for pathname in opt.pathnames {
find(&pathname, &mut files)?;
}
files.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 files.iter().cloned() {
handles.push(tokio::spawn(async move { manifest(filename).await }));
}
// 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??;
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(())
}
fn find(root: &Path, files: &mut Vec<PathBuf>) -> anyhow::Result<()> {
let meta = std::fs::symlink_metadata(root)?;
if meta.file_type().is_symlink() {
// Walkdir doesn't handle dangling symlinks so we handle all
// symlinks here.
files.push(root.to_path_buf());
return Ok(());
}
for e in WalkDir::new(root) {
files.push(e?.path().to_path_buf());
}
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()))
}
|