|
|
@ -14,130 +14,118 @@ |
|
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
|
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::path::Utf8Path; |
|
|
use std::iter::Peekable; |
|
|
// use std::fs::Metadata;
|
|
|
use std::fs::{DirEntry, Metadata, ReadDir, read_dir}; |
|
|
use std::fs::{DirEntry, ReadDir, read_dir}; |
|
|
use std::vec::IntoIter; |
|
|
|
|
|
|
|
|
use cas_core::{err, Error, Result}; |
|
|
use camino::{Utf8Path, Utf8PathBuf}; |
|
|
|
|
|
|
|
|
// use crate::{PathPair, Repository, TreeItem};
|
|
|
use cas_core::{err, Error, ObjectId, Result}; |
|
|
|
|
|
|
|
|
|
|
|
use crate::{PathPair, Repository, TreeItem}; |
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
|
pub struct RecursiveDirIterator { |
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
|
|
dir_iterators: Vec<ReadDir>, |
|
|
pub enum Action { |
|
|
|
|
|
Add, |
|
|
|
|
|
Update, |
|
|
|
|
|
Remove, |
|
|
|
|
|
Skip, |
|
|
|
|
|
Ignore, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
impl RecursiveDirIterator { |
|
|
pub struct TreeWalker { |
|
|
pub fn new<P: AsRef<Utf8Path>>(root_dir: P) -> Result<RecursiveDirIterator> { |
|
|
dir_it: Peekable<IntoIter<Result<TreeItem>>>, |
|
|
Ok(RecursiveDirIterator { |
|
|
prev_tree_it: Peekable<IntoIter<TreeItem>>, |
|
|
dir_iterators: vec![read_dir(root_dir)?], |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn pop_dir(&mut self) -> Result<()> { |
|
|
|
|
|
match self.dir_iterators.pop() { |
|
|
impl TreeWalker { |
|
|
Some(_) => Ok(()), |
|
|
pub fn new<P: AsRef<Utf8Path>>(path: P, prev_tree: Vec<TreeItem>) -> Result<Self> { |
|
|
None => err!("cannot pop directory: iterator reached the end"), |
|
|
let dir_entries = read_dir(path.as_ref().to_path_buf())? |
|
|
|
|
|
.map(|res| res.map_err(|err| err.into())) |
|
|
|
|
|
.collect::<Result<Vec<_>>>()?; |
|
|
|
|
|
|
|
|
|
|
|
let mut dir_items: Vec<_> = dir_entries.into_iter() |
|
|
|
|
|
.map(|dir_entry| { |
|
|
|
|
|
let file_name = dir_entry |
|
|
|
|
|
.file_name() |
|
|
|
|
|
.into_string() |
|
|
|
|
|
// .or_else(|os_string| err!("non-unicode file name '{}'", os_string.to_string_lossy()))?;
|
|
|
|
|
|
.or_else(|os_string| Err(Error::NonUnicodeFileName(os_string.to_string_lossy().into())))?; |
|
|
|
|
|
let metadata = dir_entry.metadata()?; |
|
|
|
|
|
Ok(TreeItem::from_metadata( |
|
|
|
|
|
file_name, |
|
|
|
|
|
&metadata, |
|
|
|
|
|
ObjectId::default() |
|
|
|
|
|
)? |
|
|
|
|
|
) |
|
|
|
|
|
}) |
|
|
|
|
|
.collect(); |
|
|
|
|
|
dir_items.sort_unstable_by_key(|result| { |
|
|
|
|
|
match result { |
|
|
|
|
|
Ok(entry) => entry.name.clone(), |
|
|
|
|
|
Err(_) => String::default(), |
|
|
} |
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
Ok(Self { |
|
|
|
|
|
dir_it: dir_items.into_iter().peekable(), |
|
|
|
|
|
prev_tree_it: prev_tree.into_iter().peekable(), |
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
impl Iterator for RecursiveDirIterator { |
|
|
impl Iterator for TreeWalker { |
|
|
type Item = Result<DirEntry>; |
|
|
type Item = Result<(Action, TreeItem)>; |
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> { |
|
|
fn next(&mut self) -> Option<Self::Item> { |
|
|
while let Some(top_it) = self.dir_iterators.last_mut() { |
|
|
match (self.dir_it.peek(), self.prev_tree_it.peek()) { |
|
|
let next = top_it.next(); |
|
|
(Some(Err(_)), _) => { |
|
|
if let Some(item) = next { |
|
|
Some(Err(self.dir_it.next().unwrap().unwrap_err())) |
|
|
let item = item.and_then(|dir_entry| { |
|
|
|
|
|
if let Ok(file_type) = dir_entry.file_type() { |
|
|
|
|
|
if file_type.is_dir() { |
|
|
|
|
|
self.dir_iterators.push(read_dir(dir_entry.path())?) |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
(Some(Ok(curr_item)), Some(prev_item)) => { |
|
|
|
|
|
if curr_item.name == prev_item.name { |
|
|
|
|
|
let action = |
|
|
|
|
|
if curr_item.modified != prev_item.modified { |
|
|
|
|
|
Action::Update |
|
|
} |
|
|
} |
|
|
Ok(dir_entry) |
|
|
else { |
|
|
}); |
|
|
Action::Skip |
|
|
return Some(item.map_err(Into::into)); |
|
|
}; |
|
|
|
|
|
self.prev_tree_it.next(); |
|
|
|
|
|
Some(Ok((action, self.dir_it.next().unwrap().unwrap()))) |
|
|
|
|
|
} |
|
|
|
|
|
else if curr_item.name < prev_item.name { |
|
|
|
|
|
Some(Ok((Action::Add, self.dir_it.next().unwrap().unwrap()))) |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
self.dir_iterators.pop(); |
|
|
Some(Ok((Action::Remove, self.prev_tree_it.next().unwrap()))) |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
None |
|
|
(Some(_), None) => { |
|
|
} |
|
|
Some(Ok((Action::Add, self.dir_it.next().unwrap().unwrap()))) |
|
|
} |
|
|
}, |
|
|
|
|
|
(None, Some(_)) => { |
|
|
|
|
|
Some(Ok((Action::Remove, self.prev_tree_it.next().unwrap()))) |
|
|
// pub trait FsWalker {
|
|
|
}, |
|
|
// fn visit(&self)
|
|
|
(None, None) => None, |
|
|
// }
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
// pub enum Action {
|
|
|
|
|
|
// Default,
|
|
|
#[cfg(test)] |
|
|
// Add,
|
|
|
mod tests { |
|
|
// Update,
|
|
|
use super::*; |
|
|
// Remove,
|
|
|
|
|
|
// Skip,
|
|
|
#[test] |
|
|
// Ignore,
|
|
|
fn test_tree_walker() { |
|
|
// }
|
|
|
for item in TreeWalker::new("/home/draklaw/tmp", vec![]).unwrap() { |
|
|
|
|
|
match item { |
|
|
|
|
|
Ok((action, tree_item)) => println!("{:?} {:?}", action, tree_item.name), |
|
|
// #[derive()]
|
|
|
Err(err) => println!("error while iterating directory: {}", err), |
|
|
// pub struct TreeWalker<'repo> {
|
|
|
} |
|
|
// repository: &'repo Repository,
|
|
|
} |
|
|
// rules: Vec<Box<dyn Fn(&Utf8Path, Option<&TreeItem>) -> Result<Action>>>,
|
|
|
} |
|
|
// default_action: Action,
|
|
|
} |
|
|
// reporters: Vec<Box<dyn Fn(&Utf8Path, Action, Option<&ObjectId>) -> Result<()>>>,
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// impl<'repo> TreeWalker<'repo> {
|
|
|
|
|
|
// pub fn process<P: AsRef<Utf8Path>>(&self, physic_path: P) -> Result<Option<ObjectId>>
|
|
|
|
|
|
// {
|
|
|
|
|
|
// let physic_path_ref = physic_path.as_ref();
|
|
|
|
|
|
|
|
|
|
|
|
// let metadata = std::fs::symlink_metadata(physic_path_ref)
|
|
|
|
|
|
// .or_else(|err| err!("failed to read {}: {}", physic_path_ref, err))?;
|
|
|
|
|
|
|
|
|
|
|
|
// let path_pair = match self.repository.path_pair_from_physic_path(physic_path)? {
|
|
|
|
|
|
// Some(path_pair) => path_pair,
|
|
|
|
|
|
// None => return Ok(None),
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// let maybe_tree = self.repository
|
|
|
|
|
|
// .oid_from_logic_path(&path_pair.logic)
|
|
|
|
|
|
// .ok()
|
|
|
|
|
|
// .map(|oid| self.repository.read_tree(&oid))
|
|
|
|
|
|
// .transpose()?
|
|
|
|
|
|
// .flatten();
|
|
|
|
|
|
|
|
|
|
|
|
// self.process_impl(&path_pair, &metadata, &maybe_tree)
|
|
|
|
|
|
// .map(|oid| Some(oid))
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// fn process_impl(&self, path_pair: &PathPair, metadata: &Metadata, maybe_tree: &Option<Vec<TreeItem>>) -> Result<ObjectId> {
|
|
|
|
|
|
// err!("not implemented")
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// fn eval_rules(&self, path: &Utf8Path, maybe_item: Option<&TreeItem>) -> Result<Action> {
|
|
|
|
|
|
// self.rules.iter()
|
|
|
|
|
|
// .map(|rule|
|
|
|
|
|
|
// rule(path, maybe_item)
|
|
|
|
|
|
// )
|
|
|
|
|
|
// .skip_while(|action_result|
|
|
|
|
|
|
// *action_result == Ok(Action::Default)
|
|
|
|
|
|
// )
|
|
|
|
|
|
// .nth(0)
|
|
|
|
|
|
// .unwrap_or(Ok(self.default_action))
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// fn report(&self, path: &Utf8Path, action: Action, maybe_oid: Option<&ObjectId>) -> Result<()> {
|
|
|
|
|
|
// for reporter in &self.reporters {
|
|
|
|
|
|
// reporter(path, action, maybe_oid)?
|
|
|
|
|
|
// }
|
|
|
|
|
|
// Ok(())
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|