use std::path::PathBuf;
use graphshell::receipts::{self, ReceiptError};
use muniment::{BlobStore, RedbBackend};
fn usage() -> ! {
eprintln!(
"usage: receipt_ingest --dir <receipt-dir> --store <blobs.redb> \
[--device <name>] [--inbox <dir> | --data-root <dir>] [--dry-run]"
);
std::process::exit(2);
}
struct Args {
dir: PathBuf,
store: PathBuf,
device: String,
inbox: Option<PathBuf>,
dry_run: bool,
}
fn parse_args() -> Args {
let mut dir = None;
let mut store = None;
let mut device = None;
let mut inbox = None;
let mut dry_run = false;
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"--dir" => dir = args.next().map(PathBuf::from),
"--store" => store = args.next().map(PathBuf::from),
"--device" => device = args.next(),
"--inbox" => inbox = args.next().map(PathBuf::from),
"--data-root" => {
inbox = args
.next()
.map(|root| receipts::inbox_dir(std::path::Path::new(&root)));
}
"--dry-run" => dry_run = true,
"-h" | "--help" => usage(),
other => {
eprintln!("unexpected argument `{other}`");
usage();
}
}
}
let Some(dir) = dir else { usage() };
let Some(store) = store else { usage() };
Args {
dir,
store,
device: device.unwrap_or_else(default_device),
inbox,
dry_run,
}
}
fn default_device() -> String {
std::env::var("COMPUTERNAME")
.or_else(|_| std::env::var("HOSTNAME"))
.unwrap_or_else(|_| "unknown-device".to_string())
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let args = parse_args();
if let Err(error) = run(&args).await {
eprintln!("receipt_ingest: {error}");
std::process::exit(1);
}
}
async fn run(args: &Args) -> Result<(), ReceiptError> {
if let Some(parent) = args.store.parent().filter(|p| !p.as_os_str().is_empty()) {
std::fs::create_dir_all(parent)?;
}
let store = BlobStore::new(RedbBackend::open(&args.store)?);
let ingested = receipts::ingest_directory(&args.dir, &store, &args.device).await?;
println!("node {}", ingested.node);
println!("address {}", ingested.address);
println!("device {}", args.device);
for (name, hash) in &ingested.blobs {
println!("blob {name} {}", hash.to_hex());
}
println!("events {}", ingested.events.len());
if args.dry_run {
println!("dry run: nothing written");
return Ok(());
}
let events_path = args.dir.join("graph-events.json");
let json =
serde_json::to_string_pretty(&ingested.events).expect("personal graph events serialize");
std::fs::write(&events_path, json)?;
println!("wrote {}", events_path.display());
match &args.inbox {
Some(inbox) => {
let deposited =
receipts::write_to_inbox(inbox, ingested.node, &args.dir, &ingested.events)?;
println!("inbox {}", deposited.display());
println!(" the resident host authors it within ~10s");
}
None => println!("inbox (none given; pass --inbox or --data-root to file it)"),
}
Ok(())
}