dump_canonical/
dump_canonical.rs1use std::collections::BTreeMap;
7use std::path::PathBuf;
8
9use brdb::fs::BrFs;
10use brdb::{Brdb, BrFsReader, Brz, IntoReader};
11
12fn collect(fs: &BrFs, prefix: &str, out: &mut Vec<(String, Option<i64>)>) {
13 match fs {
14 BrFs::Root(children) => {
15 for (name, child) in children {
16 collect(child, name, out);
17 }
18 }
19 BrFs::Folder(_, children) => {
20 for (name, child) in children {
21 let path = format!("{prefix}/{name}");
22 collect(child, &path, out);
23 }
24 }
25 BrFs::File(file) => {
26 out.push((prefix.to_string(), file.content_id));
27 }
28 }
29}
30
31fn dump(reader: &impl BrFsReader) -> Result<(), Box<dyn std::error::Error>> {
32 let mut files = Vec::new();
33 collect(&reader.get_fs()?, "", &mut files);
34 files.sort_by(|a, b| a.0.cmp(&b.0));
35
36 let mut out = BTreeMap::new();
37 for (path, content_id) in files {
38 let content = match content_id {
39 Some(id) => reader.find_blob(id)?.read()?,
40 None => Vec::new(),
41 };
42 out.insert(
43 path,
44 serde_json::json!({
45 "blake3": blake3::hash(&content).to_hex().to_string(),
46 "len": content.len(),
47 }),
48 );
49 }
50 println!("{}", serde_json::to_string_pretty(&out)?);
51 Ok(())
52}
53
54fn main() -> Result<(), Box<dyn std::error::Error>> {
55 let mut args = std::env::args().skip(1);
56 let p = PathBuf::from(args.next().expect("usage: dump_canonical <file> [--mode=hashes]"));
57 if let Some(mode) = args.next() {
58 if mode != "--mode=hashes" {
59 return Err(format!("unsupported mode {mode}").into());
62 }
63 }
64 if p.extension().and_then(|e| e.to_str()) == Some("brdb") {
65 dump(&*Brdb::open(&p)?.into_reader())
66 } else {
67 dump(&*Brz::open(&p)?.into_reader())
68 }
69}