From 7b4f1b6cbda7b7a8a468aee6be64808ffa093f64 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 12 Dec 2022 23:57:54 +0100 Subject: [PATCH] Allow file with SNAP:PATH syntax --- src/blob/tree.rs | 48 +++++++++++++++++++++++++---------------- src/commands/cat.rs | 5 +++-- src/commands/diff.rs | 27 ++++++++++++++++------- src/commands/ls.rs | 4 ++-- src/commands/restore.rs | 14 ++++++------ 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/blob/tree.rs b/src/blob/tree.rs index be18131..1f30aae 100644 --- a/src/blob/tree.rs +++ b/src/blob/tree.rs @@ -1,8 +1,9 @@ use std::collections::HashSet; +use std::ffi::OsStr; use std::mem; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use crossbeam_channel::{bounded, unbounded, Receiver, Sender}; use derive_getters::Getters; use indicatif::ProgressBar; @@ -12,7 +13,7 @@ use crate::crypto::hash; use crate::id::Id; use crate::index::IndexedBackend; -use super::Node; +use super::{Metadata, Node, NodeType}; #[derive(Clone, Debug, Serialize, Deserialize, Getters)] pub struct Tree { @@ -54,22 +55,25 @@ impl Tree { Ok(serde_json::from_slice(&data)?) } - pub fn subtree_id(be: &impl IndexedBackend, mut id: Id, path: &Path) -> Result { - for p in path.iter() { - let p = p.to_str().unwrap(); - // TODO: check for root instead - if p == "/" { - continue; + pub fn node_from_path(be: &impl IndexedBackend, id: Id, path: &Path) -> Result { + let mut node = Node::new_node(OsStr::new(""), NodeType::Dir, Metadata::default()); + node.set_subtree(id); + for p in path.components() { + match p { + Component::RootDir | Component::Prefix(_) => {} + Component::Normal(p) => { + let id = node.subtree().ok_or_else(|| anyhow!("{p:?} is no dir"))?; + let tree = Tree::from_backend(be, id)?; + node = tree + .nodes + .into_iter() + .find(|node| node.name() == p) + .ok_or_else(|| anyhow!("{p:?} not found"))?; + } + _ => bail!("path should not contain current or parent dir, path: {path:?}"), } - let tree = Tree::from_backend(be, id)?; - let node = tree - .nodes() - .iter() - .find(|node| node.name() == p) - .ok_or_else(|| anyhow!("{} not found", p))?; - id = node.subtree().ok_or_else(|| anyhow!("{} is no dir", p))?; } - Ok(id) + Ok(node) } } @@ -97,8 +101,14 @@ impl NodeStreamer where BE: IndexedBackend, { - pub fn new(be: BE, id: Id) -> Result { - let inner = Tree::from_backend(&be, id)?.nodes.into_iter(); + pub fn new(be: BE, node: &Node) -> Result { + let inner = if node.is_dir() { + Tree::from_backend(&be, node.subtree.unwrap())? + .nodes + .into_iter() + } else { + vec![node.clone()].into_iter() + }; Ok(Self { inner, open_iterators: Vec::new(), diff --git a/src/commands/cat.rs b/src/commands/cat.rs index 87cf67e..6a8b226 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -1,6 +1,6 @@ use std::path::Path; -use anyhow::Result; +use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; use indicatif::ProgressBar; @@ -93,7 +93,8 @@ fn cat_tree( let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; - let id = Tree::subtree_id(&index, snap.tree, Path::new(path))?; + let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; + let id = node.subtree.ok_or_else(|| anyhow!("{path} is no dir"))?; let data = index.blob_from_backend(&BlobType::Tree, &id)?; println!("{}", String::from_utf8(data.to_vec())?); diff --git a/src/commands/diff.rs b/src/commands/diff.rs index 6cc2a09..b8d096a 100644 --- a/src/commands/diff.rs +++ b/src/commands/diff.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::Parser; use super::{progress_counter, RusticConfig}; @@ -46,12 +46,12 @@ pub(super) fn execute( let snap2 = &snaps[1]; let index = IndexBackend::new(be, progress_counter(""))?; - let id1 = Tree::subtree_id(&index, snap1.tree, Path::new(path1))?; - let id2 = Tree::subtree_id(&index, snap2.tree, Path::new(path2))?; + let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?; + let node2 = Tree::node_from_path(&index, snap2.tree, Path::new(path2))?; diff( - NodeStreamer::new(index.clone(), id1)?, - NodeStreamer::new(index, id2)?, + NodeStreamer::new(index.clone(), &node1)?, + NodeStreamer::new(index, &node2)?, true, ) } @@ -64,14 +64,25 @@ pub(super) fn execute( p.finish(); let index = IndexBackend::new(be, progress_counter(""))?; - let id1 = Tree::subtree_id(&index, snap1.tree, Path::new(path1))?; + let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?; let path2 = PathBuf::from(path2); + let is_dir = path2 + .metadata() + .with_context(|| format!("Error accessing {path2:?}"))? + .is_dir(); let src = LocalSource::new(opts.ignore_opts, path2.clone())?.map(|item| { let (path, node) = item?; - Ok((path.strip_prefix(&path2)?.to_path_buf(), node)) + let path = if is_dir { + // remove given path prefix for dirs as local path + path.strip_prefix(&path2)?.to_path_buf() + } else { + // ensure that we really get the filename if local path is a file + path2.file_name().unwrap().into() + }; + Ok((path, node)) }); - diff(NodeStreamer::new(index, id1)?, src, false) + diff(NodeStreamer::new(index, &node1)?, src, false) } (None, _) => bail!("cannot use local path as first argument"), } diff --git a/src/commands/ls.rs b/src/commands/ls.rs index eb33ca7..b1e06e6 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -29,9 +29,9 @@ pub(super) fn execute( let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; - let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?; + let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; - for item in NodeStreamer::new(index, tree)? { + for item in NodeStreamer::new(index, &node)? { let (path, _) = item?; println!("{:?} ", path); } diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 39f62c3..4c6e0a0 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -79,12 +79,12 @@ pub(super) fn execute( let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; - let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?; + let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; let dest = LocalBackend::new(&opts.dest)?; let p = progress_spinner("collecting file information..."); - let file_infos = allocate_and_collect(&dest, index.clone(), tree, &opts)?; + let file_infos = allocate_and_collect(&dest, index.clone(), &node, &opts)?; p.finish(); info!("total restore size: {}", bytes(file_infos.total_size)); if file_infos.matched_size > 0 { @@ -113,7 +113,7 @@ pub(super) fn execute( if !opts.dry_run { let p = progress_spinner("setting metadata..."); - restore_metadata(&dest, index, tree, &opts)?; + restore_metadata(&dest, index, &node, &opts)?; p.finish(); } @@ -125,7 +125,7 @@ pub(super) fn execute( fn allocate_and_collect( dest: &LocalBackend, index: impl IndexedBackend + Unpin, - tree: Id, + node: &Node, opts: &Opts, ) -> Result { let dest_path = Path::new(&opts.dest); @@ -231,7 +231,7 @@ fn allocate_and_collect( .filter_map(Result::ok); // TODO: print out the ignored error let mut next_dst = dst_iter.next(); - let mut node_streamer = NodeStreamer::new(index.clone(), tree)?; + let mut node_streamer = NodeStreamer::new(index.clone(), node)?; let mut next_node = node_streamer.next().transpose()?; loop { @@ -353,11 +353,11 @@ fn restore_contents( fn restore_metadata( dest: &LocalBackend, index: impl IndexedBackend + Unpin, - tree: Id, + node: &Node, opts: &Opts, ) -> Result<()> { // walk over tree in repository and compare with tree in dest - let mut node_streamer = NodeStreamer::new(index, tree)?; + let mut node_streamer = NodeStreamer::new(index, node)?; let mut dir_stack = Vec::new(); while let Some((path, node)) = node_streamer.next().transpose()? { match node.node_type() {