TreeWalker WIP.
This commit is contained in:
@@ -84,6 +84,9 @@ pub enum Error {
|
||||
#[error("io error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("non-unicode file name: '{0}'")]
|
||||
NonUnicodeFileName(String),
|
||||
|
||||
#[error("{0}")]
|
||||
UnknownError(String),
|
||||
}
|
||||
|
||||
@@ -57,6 +57,14 @@ impl FromStr for ObjectId {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ObjectId {
|
||||
fn default() -> Self {
|
||||
return Self {
|
||||
id: Arc::new(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ObjectId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
|
||||
write_hex(f, self.id.as_slice())
|
||||
|
||||
2
libbsv/.gitignore
vendored
2
libbsv/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
@@ -23,7 +23,7 @@ extern crate cas_core;
|
||||
|
||||
mod permissions;
|
||||
mod tree_item;
|
||||
// mod tree_walker;
|
||||
mod tree_walker;
|
||||
// mod config;
|
||||
mod path_map;
|
||||
mod ignore;
|
||||
|
||||
@@ -51,12 +51,12 @@ pub struct TreeItem {
|
||||
}
|
||||
|
||||
impl TreeItem {
|
||||
pub fn from_metadata(name: &str, metadata: &std::fs::Metadata, oid: ObjectId) -> Result<Self> {
|
||||
pub fn from_metadata(name: String, metadata: &std::fs::Metadata, oid: ObjectId) -> Result<Self> {
|
||||
let otype = otype_from_metadata(metadata)?;
|
||||
let permissions = Permissions::from_metadata(metadata)?;
|
||||
|
||||
Ok(Self {
|
||||
name: name.to_string(),
|
||||
name: name,
|
||||
otype,
|
||||
size: metadata.len(),
|
||||
created: metadata.created().unwrap_or(UNIX_EPOCH),
|
||||
|
||||
@@ -14,130 +14,118 @@
|
||||
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use std::path::Utf8Path;
|
||||
// use std::fs::Metadata;
|
||||
use std::fs::{DirEntry, ReadDir, read_dir};
|
||||
use std::iter::Peekable;
|
||||
use std::fs::{DirEntry, Metadata, 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 {
|
||||
dir_iterators: Vec<ReadDir>,
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Action {
|
||||
Add,
|
||||
Update,
|
||||
Remove,
|
||||
Skip,
|
||||
Ignore,
|
||||
}
|
||||
|
||||
impl RecursiveDirIterator {
|
||||
pub fn new<P: AsRef<Utf8Path>>(root_dir: P) -> Result<RecursiveDirIterator> {
|
||||
Ok(RecursiveDirIterator {
|
||||
dir_iterators: vec![read_dir(root_dir)?],
|
||||
pub struct TreeWalker {
|
||||
dir_it: Peekable<IntoIter<Result<TreeItem>>>,
|
||||
prev_tree_it: Peekable<IntoIter<TreeItem>>,
|
||||
}
|
||||
|
||||
|
||||
impl TreeWalker {
|
||||
pub fn new<P: AsRef<Utf8Path>>(path: P, prev_tree: Vec<TreeItem>) -> Result<Self> {
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pop_dir(&mut self) -> Result<()> {
|
||||
match self.dir_iterators.pop() {
|
||||
Some(_) => Ok(()),
|
||||
None => err!("cannot pop directory: iterator reached the end"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RecursiveDirIterator {
|
||||
type Item = Result<DirEntry>;
|
||||
impl Iterator for TreeWalker {
|
||||
type Item = Result<(Action, TreeItem)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(top_it) = self.dir_iterators.last_mut() {
|
||||
let next = top_it.next();
|
||||
if let Some(item) = next {
|
||||
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())?)
|
||||
match (self.dir_it.peek(), self.prev_tree_it.peek()) {
|
||||
(Some(Err(_)), _) => {
|
||||
Some(Err(self.dir_it.next().unwrap().unwrap_err()))
|
||||
}
|
||||
}
|
||||
Ok(dir_entry)
|
||||
});
|
||||
return Some(item.map_err(Into::into));
|
||||
(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
|
||||
}
|
||||
else {
|
||||
self.dir_iterators.pop();
|
||||
Action::Skip
|
||||
};
|
||||
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 {
|
||||
Some(Ok((Action::Remove, self.prev_tree_it.next().unwrap())))
|
||||
}
|
||||
},
|
||||
(Some(_), None) => {
|
||||
Some(Ok((Action::Add, self.dir_it.next().unwrap().unwrap())))
|
||||
},
|
||||
(None, Some(_)) => {
|
||||
Some(Ok((Action::Remove, self.prev_tree_it.next().unwrap())))
|
||||
},
|
||||
(None, None) => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pub trait FsWalker {
|
||||
// fn visit(&self)
|
||||
// }
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
// pub enum Action {
|
||||
// Default,
|
||||
// Add,
|
||||
// Update,
|
||||
// Remove,
|
||||
// Skip,
|
||||
// Ignore,
|
||||
// }
|
||||
|
||||
|
||||
// #[derive()]
|
||||
// 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(())
|
||||
// }
|
||||
// }
|
||||
#[test]
|
||||
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),
|
||||
Err(err) => println!("error while iterating directory: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user