diff --git a/src/commands/cat.rs b/src/commands/cat.rs index ccf058e..58f0b62 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -1,56 +1,119 @@ -use anyhow::{anyhow, bail, Result}; -use clap::Parser; +use std::path::PathBuf; + +use anyhow::{anyhow, Result}; +use clap::{Parser, Subcommand}; use crate::backend::{DecryptReadBackend, FileType, ReadBackend}; +use crate::blob::Tree; use crate::id::Id; use crate::index::{IndexBackend, ReadIndex}; +use crate::repo::SnapshotFile; #[derive(Parser)] pub(super) struct Opts { - /// file type to list - tpe: String, + #[clap(subcommand)] + command: Command, +} - /// file type to list +#[derive(Subcommand)] +enum Command { + Blob(IdOpt), + Config(IdOpt), + Index(IdOpt), + Snapshot(IdOpt), + Key(IdOpt), + /// display a tree within a snapshot + Tree(TreeOpts), +} + +#[derive(Parser)] +struct IdOpt { + /// id to cat id: String, } +#[derive(Parser)] +struct TreeOpts { + /// snapshot id + id: String, + + /// path within snapshot + path: PathBuf, +} + pub(super) fn execute( be: &impl ReadBackend, dbe: &impl DecryptReadBackend, opts: Opts, ) -> Result<()> { - let tpe = match opts.tpe.as_str() { + match opts.command { + Command::Config(opt) => cat_file(dbe, FileType::Config, opt), + Command::Index(opt) => cat_file(dbe, FileType::Index, opt), + Command::Snapshot(opt) => cat_file(dbe, FileType::Snapshot, opt), + // special treatment for catting key files: those need no decryption + Command::Key(opt) => cat_file(be, FileType::Key, opt), // special treatment for catingg blobs: read the index and use it to locate the blob - "blob" => { - let id = Id::from_hex(&opts.id)?; - println!("reading index files.."); - let index = IndexBackend::new(dbe)?; - let dec = index - .get_id(&id) - .ok_or(anyhow!("blob not found in index"))? - .read_data(dbe)?; - print!("{}", String::from_utf8_lossy(&dec)); - return Ok(()); - } - "config" => FileType::Config, - "index" => FileType::Index, - "snapshot" => FileType::Snapshot, - "key" => FileType::Key, - t => bail!("invalid type: {}", t), - }; + Command::Blob(opt) => cat_blob(dbe, opt), + // special treatment for cating a tree within a snapshot + Command::Tree(opts) => cat_tree(dbe, opts), + } +} - let id = Id::from_hex(&opts.id).or_else(|_| { +fn cat_file(be: &impl ReadBackend, tpe: FileType, opt: IdOpt) -> Result<()> { + let id = Id::from_hex(&opt.id).or_else(|_| { // if the given id param is not a full Id, search for a suitable one - be.find_starts_with(tpe, &[&opts.id])?.remove(0) + be.find_starts_with(tpe, &[&opt.id])?.remove(0) })?; - let dec = match tpe { - // special treatment for catting key files: those need no decryption - FileType::Key => be.read_full(tpe, &id)?, - _ => dbe.read_full(tpe, &id)?, - }; + let dec = be.read_full(tpe, &id)?; print!("{}", String::from_utf8_lossy(&dec)); Ok(()) } + +fn cat_blob(be: &impl DecryptReadBackend, opt: IdOpt) -> Result<()> { + let id = Id::from_hex(&opt.id)?; + eprintln!("reading index files.."); + let index = IndexBackend::new(be)?; + let dec = index + .get_id(&id) + .ok_or(anyhow!("blob not found in index"))? + .read_data(be)?; + print!("{}", String::from_utf8_lossy(&dec)); + return Ok(()); +} + +fn cat_tree(be: &impl DecryptReadBackend, opts: TreeOpts) -> Result<()> { + let snap = SnapshotFile::from_str(be, &opts.id, |_| true)?; + + eprintln!("reading index files.."); + let index = IndexBackend::new(be)?; + + let mut id = snap.tree; + + for p in opts.path.iter() { + let p = p.to_str().unwrap(); + // TODO: check for root instead + if p == "/" { + continue; + } + let tree = Tree::from_backend(&index, &id)?; + + id = tree + .nodes() + .iter() + .find(|node| node.name() == p) + .unwrap() + .subtree() + .unwrap(); + } + + let dec = index + .get_id(&id) + .ok_or(anyhow!("blob not found in index"))? + .read_data(be)?; + print!("{}", String::from_utf8_lossy(&dec)); + + return Ok(()); +}