diff --git a/crates/core/src/commands/restore.rs b/crates/core/src/commands/restore.rs index 0cf96ab5..06d92550 100644 --- a/crates/core/src/commands/restore.rs +++ b/crates/core/src/commands/restore.rs @@ -4,16 +4,11 @@ use derive_setters::Setters; use jiff::Timestamp; use log::{debug, error, info, trace, warn}; -use std::{ - cmp::Ordering, - collections::BTreeMap, - path::{Path, PathBuf}, - sync::Mutex, -}; +use std::{cmp::Ordering, collections::BTreeMap, path::PathBuf, sync::Mutex}; -use ignore::{DirEntry, WalkBuilder}; use itertools::Itertools; use rayon::ThreadPoolBuilder; +use walkdir::{DirEntry, WalkDir}; use crate::{ backend::{ @@ -170,59 +165,65 @@ pub(crate) fn collect_and_prepare( let mut stats = RestoreStats::default(); let mut restore_infos = RestorePlan::default(); let mut additional_existing = false; - let mut removed_dir = None; - let mut process_existing = |entry: &DirEntry| -> RusticResult<_> { - if entry.depth() == 0 { - // don't process the root dir which should be existing - return Ok(()); - } + let next_entry = |walker: &mut walkdir::IntoIter| -> Option { + walker + .inspect(|r| { + if let Err(err) = r { + error!("Error during collection of files: {err:?}"); + } + }) + .find_map(Result::ok) + }; - debug!("additional {}", entry.path().display()); - if entry.file_type().unwrap().is_dir() { - stats.dirs.additional += 1; - } else { - stats.files.additional += 1; - } - match (opts.delete, dry_run, entry.file_type().unwrap().is_dir()) { - (true, true, true) => { - info!( - "would have removed the additional dir: {}", - entry.path().display() - ); + let mut process_existing = + |walker: &mut walkdir::IntoIter, entry: &DirEntry| -> RusticResult> { + if entry.depth() == 0 { + // don't process the root dir which should be existing + return Ok(next_entry(walker)); } - (true, true, false) => { - info!( - "would have removed the additional file: {}", - entry.path().display() - ); + + debug!("additional {}", entry.path().display()); + let is_dir = entry.file_type().is_dir(); + if is_dir { + stats.dirs.additional += 1; + } else { + stats.files.additional += 1; } - (true, false, true) => { - let path = entry.path(); - match &removed_dir { - Some(dir) if path.starts_with(dir) => {} - _ => match dest.remove_dir(path) { - Ok(()) => { - removed_dir = Some(path.to_path_buf()); - } - Err(err) => { - error!("error removing {}: {err}", path.display()); - } - }, + match (opts.delete, dry_run, is_dir) { + (true, true, true) => { + info!( + "would have removed the additional dir: {}", + entry.path().display() + ); } - } - (true, false, false) => { - if let Err(err) = dest.remove_file(entry.path()) { - error!("error removing {}: {err}", entry.path().display()); + (true, true, false) => { + info!( + "would have removed the additional file: {}", + entry.path().display() + ); + } + (true, false, true) => { + if let Err(err) = dest.remove_dir(entry.path()) { + error!("error removing {}: {err}", entry.path().display()); + } + } + (true, false, false) => { + if let Err(err) = dest.remove_file(entry.path()) { + error!("error removing {}: {err}", entry.path().display()); + } + } + (false, _, _) => { + additional_existing = true; } } - (false, _, _) => { - additional_existing = true; - } - } - Ok(()) - }; + // don't descend into extra dirs + if is_dir { + walker.skip_current_dir(); + } + Ok(next_entry(walker)) + }; let mut process_node = |path: &PathBuf, node: &Node, exists: bool| -> RusticResult<_> { match node.node_type { @@ -288,20 +289,12 @@ pub(crate) fn collect_and_prepare( Ok(()) }; - let mut dst_iter = WalkBuilder::new(dest_path) + let mut walker = WalkDir::new(&dest_path) .follow_links(false) - .hidden(false) - .ignore(false) - .sort_by_file_path(Path::cmp) - .build() - .inspect(|r| { - if let Err(err) = r { - error!("Error during collection of files: {err:?}"); - } - }) - .filter_map(Result::ok); + .sort_by_file_name() + .into_iter(); - let mut next_dst = dst_iter.next(); + let mut next_dst = next_entry(&mut walker); let mut next_node = node_streamer.next().transpose()?; @@ -310,26 +303,25 @@ pub(crate) fn collect_and_prepare( (None, None) => break, (Some(destination), None) => { - process_existing(destination)?; - next_dst = dst_iter.next(); + next_dst = process_existing(&mut walker, destination)?; } (Some(destination), Some((path, node))) => { match destination.path().cmp(&dest.path(path)) { Ordering::Less => { - process_existing(destination)?; - next_dst = dst_iter.next(); + next_dst = process_existing(&mut walker, destination)?; } Ordering::Equal => { // process existing node - if (node.is_dir() && !destination.file_type().unwrap().is_dir()) - || (node.is_file() && !destination.metadata().unwrap().is_file()) + if (node.is_dir() && !destination.file_type().is_dir()) + || (node.is_file() && !destination.file_type().is_file()) || node.is_special() { // if types do not match, first remove the existing file - process_existing(destination)?; + next_dst = process_existing(&mut walker, destination)?; + } else { + next_dst = next_entry(&mut walker); } process_node(path, node, true)?; - next_dst = dst_iter.next(); next_node = node_streamer.next().transpose()?; } Ordering::Greater => {