Allow file with SNAP:PATH syntax

This commit is contained in:
Alexander Weiss 2022-12-12 23:57:54 +01:00
parent f987de4219
commit 7b4f1b6cbd
5 changed files with 60 additions and 38 deletions

View File

@ -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<Id> {
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<Node> {
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<BE> NodeStreamer<BE>
where
BE: IndexedBackend,
{
pub fn new(be: BE, id: Id) -> Result<Self> {
let inner = Tree::from_backend(&be, id)?.nodes.into_iter();
pub fn new(be: BE, node: &Node) -> Result<Self> {
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(),

View File

@ -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())?);

View File

@ -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"),
}

View File

@ -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);
}

View File

@ -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<FileInfos> {
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() {