diff options
Diffstat (limited to 'filetreelist/src/filetreeitems.rs')
-rw-r--r-- | filetreelist/src/filetreeitems.rs | 1586 |
1 files changed, 793 insertions, 793 deletions
diff --git a/filetreelist/src/filetreeitems.rs b/filetreelist/src/filetreeitems.rs index dec003a9..b545f841 100644 --- a/filetreelist/src/filetreeitems.rs +++ b/filetreelist/src/filetreeitems.rs @@ -1,814 +1,814 @@ use crate::{ - error::Error, - item::{FileTreeItemKind, PathCollapsed}, - FileTreeItem, + error::Error, + item::{FileTreeItemKind, PathCollapsed}, + FileTreeItem, }; use crate::{error::Result, treeitems_iter::TreeItemsIterator}; use std::{ - collections::{BTreeSet, HashMap}, - path::{Path, PathBuf}, - usize, + collections::{BTreeSet, HashMap}, + path::{Path, PathBuf}, + usize, }; /// #[derive(Default)] pub struct FileTreeItems { - pub tree_items: Vec<FileTreeItem>, - files: usize, + pub tree_items: Vec<FileTreeItem>, + files: usize, } impl FileTreeItems { - /// - pub fn new( - list: &[&Path], - collapsed: &BTreeSet<&String>, - ) -> Result<Self> { - let (mut items, paths) = Self::create_items(list, collapsed)?; - - Self::fold_paths(&mut items, &paths); - - Ok(Self { - tree_items: items, - files: list.len(), - }) - } - - fn create_items<'a>( - list: &'a [&Path], - collapsed: &BTreeSet<&String>, - ) -> Result<(Vec<FileTreeItem>, HashMap<&'a Path, usize>)> { - // scopetime::scope_time!("create_items"); - - let mut items = Vec::with_capacity(list.len()); - let mut paths_added: HashMap<&Path, usize> = - HashMap::with_capacity(list.len()); - - for e in list { - { - Self::push_dirs( - e, - &mut items, - &mut paths_added, - collapsed, - )?; - } - - items.push(FileTreeItem::new_file(e)?); - } - - Ok((items, paths_added)) - } - - /// how many individual items (files/paths) are in the list - pub fn len(&self) -> usize { - self.tree_items.len() - } - - /// how many files were added to this list - pub const fn file_count(&self) -> usize { - self.files - } - - /// iterates visible elements - pub const fn iterate( - &self, - start: usize, - max_amount: usize, - ) -> TreeItemsIterator<'_> { - TreeItemsIterator::new(self, start, max_amount) - } - - fn push_dirs<'a>( - item_path: &'a Path, - nodes: &mut Vec<FileTreeItem>, - // helps to only add new nodes for paths that were not added before - // we also count the number of children a node has for later folding - paths_added: &mut HashMap<&'a Path, usize>, - collapsed: &BTreeSet<&String>, - ) -> Result<()> { - let mut ancestors = - item_path.ancestors().skip(1).collect::<Vec<_>>(); - ancestors.reverse(); - - for c in &ancestors { - if c.parent().is_some() && !paths_added.contains_key(c) { - // add node and set count to have no children - paths_added.insert(c, 0); - - // increase the number of children in the parent node count - if let Some(parent) = c.parent() { - if !parent.as_os_str().is_empty() { - *paths_added.entry(parent).or_insert(0) += 1; - } - } - - //TODO: make non alloc - let path_string = Self::path_to_string(c)?; - let is_collapsed = collapsed.contains(&path_string); - nodes.push(FileTreeItem::new_path(c, is_collapsed)?); - } - } - - // increase child count in parent node (the above ancenstor ignores the leaf component) - if let Some(parent) = item_path.parent() { - *paths_added.entry(parent).or_insert(0) += 1; - } - - Ok(()) - } - - //TODO: return ref - fn path_to_string(p: &Path) -> Result<String> { - Ok(p.to_str() - .map_or_else( - || Err(Error::InvalidPath(p.to_path_buf())), - Ok, - )? - .to_string()) - } - - pub fn collapse(&mut self, index: usize, recursive: bool) { - if self.tree_items[index].kind().is_path() { - self.tree_items[index].collapse_path(); - - let path = PathBuf::from( - self.tree_items[index].info().full_path_str(), - ); - - for i in index + 1..self.tree_items.len() { - let item = &mut self.tree_items[i]; - - if recursive && item.kind().is_path() { - item.collapse_path(); - } - - let item_path = - Path::new(item.info().full_path_str()); - - if item_path.starts_with(&path) { - item.hide(); - } else { - return; - } - } - } - } - - pub fn expand(&mut self, index: usize, recursive: bool) { - if self.tree_items[index].kind().is_path() { - self.tree_items[index].expand_path(); - - let full_path = PathBuf::from( - self.tree_items[index].info().full_path_str(), - ); - - if recursive { - for i in index + 1..self.tree_items.len() { - let item = &mut self.tree_items[i]; - - if !Path::new(item.info().full_path_str()) - .starts_with(&full_path) - { - break; - } - - if item.kind().is_path() - && item.kind().is_path_collapsed() - { - item.expand_path(); - } - } - } - - self.update_visibility( - &Some(full_path), - index + 1, - false, - ); - } - } - - fn update_visibility( - &mut self, - prefix: &Option<PathBuf>, - start_idx: usize, - set_defaults: bool, - ) { - // if we are in any subpath that is collapsed we keep skipping over it - let mut inner_collapsed: Option<PathBuf> = None; - - for i in start_idx..self.tree_items.len() { - if let Some(ref collapsed_path) = inner_collapsed { - let p = Path::new( - self.tree_items[i].info().full_path_str(), - ); - if p.starts_with(collapsed_path) { - if set_defaults { - self.tree_items[i] - .info_mut() - .set_visible(false); - } - // we are still in a collapsed inner path - continue; - } - inner_collapsed = None; - } - - let item_kind = self.tree_items[i].kind().clone(); - let item_path = - Path::new(self.tree_items[i].info().full_path_str()); - - if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed)) if collapsed) - { - // we encountered an inner path that is still collapsed - inner_collapsed = Some(item_path.into()); - } - - if prefix - .as_ref() - .map_or(true, |prefix| item_path.starts_with(prefix)) - { - self.tree_items[i].info_mut().set_visible(true); - } else { - // if we do not set defaults we can early out - if set_defaults { - self.tree_items[i].info_mut().set_visible(false); - } else { - return; - } - } - } - } - - fn fold_paths( - items: &mut Vec<FileTreeItem>, - paths: &HashMap<&Path, usize>, - ) { - let mut i = 0; - - while i < items.len() { - let item = &items[i]; - if item.kind().is_path() { - let children = paths - .get(&Path::new(item.info().full_path_str())); - - if let Some(children) = children { - if *children == 1 { - if i + 1 >= items.len() { - return; - } - - if items - .get(i + 1) - .map(|item| item.kind().is_path()) - .unwrap_or_default() - { - let next_item = items.remove(i + 1); - let item_mut = &mut items[i]; - item_mut.fold(next_item); - - let prefix = item_mut - .info() - .full_path_str() - .to_owned(); - - Self::unindent(items, &prefix, i + 1); - continue; - } - } - } - } - - i += 1; - } - } - - fn unindent( - items: &mut Vec<FileTreeItem>, - prefix: &str, - start: usize, - ) { - for elem in items.iter_mut().skip(start) { - if elem.info().full_path_str().starts_with(prefix) { - elem.info_mut().unindent(); - } else { - return; - } - } - } + /// + pub fn new( + list: &[&Path], + collapsed: &BTreeSet<&String>, + ) -> Result<Self> { + let (mut items, paths) = Self::create_items(list, collapsed)?; + + Self::fold_paths(&mut items, &paths); + + Ok(Self { + tree_items: items, + files: list.len(), + }) + } + + fn create_items<'a>( + list: &'a [&Path], + collapsed: &BTreeSet<&String>, + ) -> Result<(Vec<FileTreeItem>, HashMap<&'a Path, usize>)> { + // scopetime::scope_time!("create_items"); + + let mut items = Vec::with_capacity(list.len()); + let mut paths_added: HashMap<&Path, usize> = + HashMap::with_capacity(list.len()); + + for e in list { + { + Self::push_dirs( + e, + &mut items, + &mut paths_added, + collapsed, + )?; + } + + items.push(FileTreeItem::new_file(e)?); + } + + Ok((items, paths_added)) + } + + /// how many individual items (files/paths) are in the list + pub fn len(&self) -> usize { + self.tree_items.len() + } + + /// how many files were added to this list + pub const fn file_count(&self) -> usize { + self.files + } + + /// iterates visible elements + pub const fn iterate( + &self, + start: usize, + max_amount: usize, + ) -> TreeItemsIterator<'_> { + TreeItemsIterator::new(self, start, max_amount) + } + + fn push_dirs<'a>( + item_path: &'a Path, + nodes: &mut Vec<FileTreeItem>, + // helps to only add new nodes for paths that were not added before + // we also count the number of children a node has for later folding + paths_added: &mut HashMap<&'a Path, usize>, + collapsed: &BTreeSet<&String>, + ) -> Result<()> { + let mut ancestors = + item_path.ancestors().skip(1).collect::<Vec<_>>(); + ancestors.reverse(); + + for c in &ancestors { + if c.parent().is_some() && !paths_added.contains_key(c) { + // add node and set count to have no children + paths_added.insert(c, 0); + + // increase the number of children in the parent node count + if let Some(parent) = c.parent() { + if !parent.as_os_str().is_empty() { + *paths_added.entry(parent).or_insert(0) += 1; + } + } + + //TODO: make non alloc + let path_string = Self::path_to_string(c)?; + let is_collapsed = collapsed.contains(&path_string); + nodes.push(FileTreeItem::new_path(c, is_collapsed)?); + } + } + + // increase child count in parent node (the above ancenstor ignores the leaf component) + if let Some(parent) = item_path.parent() { + *paths_added.entry(parent).or_insert(0) += 1; + } + + Ok(()) + } + + //TODO: return ref + fn path_to_string(p: &Path) -> Result<String> { + Ok(p.to_str() + .map_or_else( + || Err(Error::InvalidPath(p.to_path_buf())), + Ok, + )? + .to_string()) + } + + pub fn collapse(&mut self, index: usize, recursive: bool) { + if self.tree_items[index].kind().is_path() { + self.tree_items[index].collapse_path(); + + let path = PathBuf::from( + self.tree_items[index].info().full_path_str(), + ); + + for i in index + 1..self.tree_items.len() { + let item = &mut self.tree_items[i]; + + if recursive && item.kind().is_path() { + item.collapse_path(); + } + + let item_path = + Path::new(item.info().full_path_str()); + + if item_path.starts_with(&path) { + item.hide(); + } else { + return; + } + } + } + } + + pub fn expand(&mut self, index: usize, recursive: bool) { + if self.tree_items[index].kind().is_path() { + self.tree_items[index].expand_path(); + + let full_path = PathBuf::from( + self.tree_items[index].info().full_path_str(), + ); + + if recursive { + for i in index + 1..self.tree_items.len() { + let item = &mut self.tree_items[i]; + + if !Path::new(item.info().full_path_str()) + .starts_with(&full_path) + { + break; + } + + if item.kind().is_path() + && item.kind().is_path_collapsed() + { + item.expand_path(); + } + } + } + + self.update_visibility( + &Some(full_path), + index + 1, + false, + ); + } + } + + fn update_visibility( + &mut self, + prefix: &Option<PathBuf>, + start_idx: usize, + set_defaults: bool, + ) { + // if we are in any subpath that is collapsed we keep skipping over it + let mut inner_collapsed: Option<PathBuf> = None; + + for i in start_idx..self.tree_items.len() { + if let Some(ref collapsed_path) = inner_collapsed { + let p = Path::new( + self.tree_items[i].info().full_path_str(), + ); + if p.starts_with(collapsed_path) { + if set_defaults { + self.tree_items[i] + .info_mut() + .set_visible(false); + } + // we are still in a collapsed inner path + continue; + } + inner_collapsed = None; + } + + let item_kind = self.tree_items[i].kind().clone(); + let item_path = + Path::new(self.tree_items[i].info().full_path_str()); + + if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed)) if collapsed) + { + // we encountered an inner path that is still collapsed + inner_collapsed = Some(item_path.into()); + } + + if prefix + .as_ref() + .map_or(true, |prefix| item_path.starts_with(prefix)) + { + self.tree_items[i].info_mut().set_visible(true); + } else { + // if we do not set defaults we can early out + if set_defaults { + self.tree_items[i].info_mut().set_visible(false); + } else { + return; + } + } + } + } + + fn fold_paths( + items: &mut Vec<FileTreeItem>, + paths: &HashMap<&Path, usize>, + ) { + let mut i = 0; + + while i < items.len() { + let item = &items[i]; + if item.kind().is_path() { + let children = paths + .get(&Path::new(item.info().full_path_str())); + + if let Some(children) = children { + if *children == 1 { + if i + 1 >= items.len() { + return; + } + + if items + .get(i + 1) + .map(|item| item.kind().is_path()) + .unwrap_or_default() + { + let next_item = items.remove(i + 1); + let item_mut = &mut items[i]; + item_mut.fold(next_item); + + let prefix = item_mut + .info() + .full_path_str() + .to_owned(); + + Self::unindent(items, &prefix, i + 1); + continue; + } + } + } + } + + i += 1; + } + } + + fn unindent( + items: &mut Vec<FileTreeItem>, + prefix: &str, + start: usize, + ) { + for elem in items.iter_mut().skip(start) { + if elem.info().full_path_str().starts_with(prefix) { + elem.info_mut().unindent(); + } else { + return; + } + } + } } #[cfg(test)] mod tests { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn test_simple() { - let items = vec![ - Path::new("file.txt"), // - ]; - - let res = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - assert!(res.tree_items[0].info().is_visible()); - assert_eq!(res.tree_items[0].info().indent(), 0); - assert_eq!(res.tree_items[0].info().path(), items[0]); - assert_eq!(res.tree_items[0].info().full_path(), items[0]); - - let items = vec![ - Path::new("file.txt"), // - Path::new("file2.txt"), // - ]; - - let res = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - assert_eq!(res.tree_items.len(), 2); - assert_eq!(res.tree_items.len(), res.len()); - assert_eq!(res.tree_items[1].info().path(), items[1]); - } - - #[test] - fn test_push_path() { - let mut items = Vec::new(); - let mut paths: HashMap<&Path, usize> = HashMap::new(); - - FileTreeItems::push_dirs( - Path::new("a/b/c"), - &mut items, - &mut paths, - &BTreeSet::new(), - ) - .unwrap(); - - assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); - - FileTreeItems::push_dirs( - Path::new("a/b2/c"), - &mut items, - &mut paths, - &BTreeSet::new(), - ) - .unwrap(); - - assert_eq!(*paths.get(&Path::new("a")).unwrap(), 2); - } - - #[test] - fn test_push_path2() { - let mut items = Vec::new(); - let mut paths: HashMap<&Path, usize> = HashMap::new(); - - FileTreeItems::push_dirs( - Path::new("a/b/c"), - &mut items, - &mut paths, - &BTreeSet::new(), - ) - .unwrap(); - - assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); - assert_eq!(*paths.get(&Path::new("a/b")).unwrap(), 1); - - FileTreeItems::push_dirs( - Path::new("a/b/d"), - &mut items, - &mut paths, - &BTreeSet::new(), - ) - .unwrap(); - - assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); - assert_eq!(*paths.get(&Path::new("a/b")).unwrap(), 2); - } - - #[test] - fn test_folder() { - let items = vec![ - Path::new("a/file.txt"), // - ]; - - let res = FileTreeItems::new(&items, &BTreeSet::new()) - .unwrap() - .tree_items - .iter() - .map(|i| i.info().full_path_str().to_string()) - .collect::<Vec<_>>(); - - assert_eq!( - res, - vec![String::from("a"), String::from("a/file.txt"),] - ); - } - - #[test] - fn test_indent() { - let items = vec![ - Path::new("a/b/file.txt"), // - ]; - - let list = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - let mut res = list - .tree_items - .iter() - .map(|i| (i.info().indent(), i.info().path())); - - assert_eq!(res.next(), Some((0, Path::new("a/b")))); - assert_eq!(res.next(), Some((1, Path::new("file.txt")))); - } - - #[test] - fn test_indent_folder_file_name() { - let items = vec![ - Path::new("a/b"), // - Path::new("a.txt"), // - ]; - - let list = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - let mut res = list - .tree_items - .iter() - .map(|i| (i.info().indent(), i.info().path_str())); - - assert_eq!(res.next(), Some((0, "a"))); - assert_eq!(res.next(), Some((1, "b"))); - assert_eq!(res.next(), Some((0, "a.txt"))); - } - - #[test] - fn test_folder_dup() { - let items = vec![ - Path::new("a/file.txt"), // - Path::new("a/file2.txt"), // - ]; - - let tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - assert_eq!(tree.file_count(), 2); - assert_eq!(tree.len(), 3); - - let res = tree - .tree_items - .iter() - .map(|i| i.info().full_path_str().to_string()) - .collect::<Vec<_>>(); - - assert_eq!( - res, - vec![ - String::from("a"), - String::from("a/file.txt"), - String::from("a/file2.txt"), - ] - ); - } - - #[test] - fn test_collapse() { - let items = vec![ - Path::new("a/file1.txt"), // - Path::new("b/file2.txt"), // - ]; - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - assert!(tree.tree_items[1].info().is_visible()); - - tree.collapse(0, false); - - assert!(!tree.tree_items[1].info().is_visible()); - } - - #[test] - fn test_iterate_collapsed() { - let items = vec![ - Path::new("a/file1.txt"), // - Path::new("b/file2.txt"), // - ]; - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - tree.collapse(0, false); - - let mut it = tree.iterate(0, 10); - - assert_eq!(it.next().unwrap().0, 0); - assert_eq!(it.next().unwrap().0, 2); - assert_eq!(it.next().unwrap().0, 3); - assert_eq!(it.next(), None); - } - - pub fn get_visibles(tree: &FileTreeItems) -> Vec<bool> { - tree.tree_items - .iter() - .map(|e| e.info().is_visible()) - .collect::<Vec<_>>() - } - - #[test] - fn test_expand() { - let items = vec![ - Path::new("a/b/c"), // - Path::new("a/d"), // - ]; - - //0 a/ - //1 b/ - //2 c - //3 d - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - tree.collapse(1, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - true, // - false, // - true, - ] - ); - - tree.expand(1, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - true, // - true, // - true, - ] - ); - } - - #[test] - fn test_expand_bug() { - let items = vec![ - Path::new("a/b/c"), // - Path::new("a/b2/d"), // - ]; - - //0 a/ - //1 b/ - //2 c - //3 b2/ - //4 d - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - tree.collapse(1, false); - tree.collapse(0, false); - - assert_eq!( - get_visibles(&tree), - vec![ - true, // - false, // - false, // - false, // - false, - ] - ); - - tree.expand(0, false); - - assert_eq!( - get_visibles(&tree), - vec![ - true, // - true, // - false, // - true, // - true, - ] - ); - } - - #[test] - fn test_collapse_too_much() { - let items = vec![ - Path::new("a/b"), // - Path::new("a2/c"), // - ]; - - //0 a/ - //1 b - //2 a2/ - //3 c - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - tree.collapse(0, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - false, // - true, // - true, - ] - ); - } - - #[test] - fn test_expand_with_collapsed_sub_parts() { - let items = vec![ - Path::new("a/b/c"), // - Path::new("a/d"), // - ]; - - //0 a/ - //1 b/ - //2 c - //3 d - - let mut tree = - FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); - - tree.collapse(1, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - true, // - false, // - true, - ] - ); - - tree.collapse(0, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - false, // - false, // - false, - ] - ); - - tree.expand(0, false); - - let visibles = get_visibles(&tree); - - assert_eq!( - visibles, - vec![ - true, // - true, // - false, // - true, - ] - ); - } + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_simple() { + let items = vec![ + Path::new("file.txt"), // + ]; + + let res = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + assert!(res.tree_items[0].info().is_visible()); + assert_eq!(res.tree_items[0].info().indent(), 0); + assert_eq!(res.tree_items[0].info().path(), items[0]); + assert_eq!(res.tree_items[0].info().full_path(), items[0]); + + let items = vec![ + Path::new("file.txt"), // + Path::new("file2.txt"), // + ]; + + let res = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + assert_eq!(res.tree_items.len(), 2); + assert_eq!(res.tree_items.len(), res.len()); + assert_eq!(res.tree_items[1].info().path(), items[1]); + } + + #[test] + fn test_push_path() { + let mut items = Vec::new(); + let mut paths: HashMap<&Path, usize> = HashMap::new(); + + FileTreeItems::push_dirs( + Path::new("a/b/c"), + &mut items, + &mut paths, + &BTreeSet::new(), + ) + .unwrap(); + + assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); + + FileTreeItems::push_dirs( + Path::new("a/b2/c"), + &mut items, + &mut paths, + &BTreeSet::new(), + ) + .unwrap(); + + assert_eq!(*paths.get(&Path::new("a")).unwrap(), 2); + } + + #[test] + fn test_push_path2() { + let mut items = Vec::new(); + let mut paths: HashMap<&Path, usize> = HashMap::new(); + + FileTreeItems::push_dirs( + Path::new("a/b/c"), + &mut items, + &mut paths, + &BTreeSet::new(), + ) + .unwrap(); + + assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); + assert_eq!(*paths.get(&Path::new("a/b")).unwrap(), 1); + + FileTreeItems::push_dirs( + Path::new("a/b/d"), + &mut items, + &mut paths, + &BTreeSet::new(), + ) + .unwrap(); + + assert_eq!(*paths.get(&Path::new("a")).unwrap(), 1); + assert_eq!(*paths.get(&Path::new("a/b")).unwrap(), 2); + } + + #[test] + fn test_folder() { + let items = vec![ + Path::new("a/file.txt"), // + ]; + + let res = FileTreeItems::new(&items, &BTreeSet::new()) + .unwrap() + .tree_items + .iter() + .map(|i| i.info().full_path_str().to_string()) + .collect::<Vec<_>>(); + + assert_eq!( + res, + vec![String::from("a"), String::from("a/file.txt"),] + ); + } + + #[test] + fn test_indent() { + let items = vec![ + Path::new("a/b/file.txt"), // + ]; + + let list = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + let mut res = list + .tree_items + .iter() + .map(|i| (i.info().indent(), i.info().path())); + + assert_eq!(res.next(), Some((0, Path::new("a/b")))); + assert_eq!(res.next(), Some((1, Path::new("file.txt")))); + } + + #[test] + fn test_indent_folder_file_name() { + let items = vec![ + Path::new("a/b"), // + Path::new("a.txt"), // + ]; + + let list = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + let mut res = list + .tree_items + .iter() + .map(|i| (i.info().indent(), i.info().path_str())); + + assert_eq!(res.next(), Some((0, "a"))); + assert_eq!(res.next(), Some((1, "b"))); + assert_eq!(res.next(), Some((0, "a.txt"))); + } + + #[test] + fn test_folder_dup() { + let items = vec![ + Path::new("a/file.txt"), // + Path::new("a/file2.txt"), // + ]; + + let tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + assert_eq!(tree.file_count(), 2); + assert_eq!(tree.len(), 3); + + let res = tree + .tree_items + .iter() + .map(|i| i.info().full_path_str().to_string()) + .collect::<Vec<_>>(); + + assert_eq!( + res, + vec![ + String::from("a"), + String::from("a/file.txt"), + String::from("a/file2.txt"), + ] + ); + } + + #[test] + fn test_collapse() { + let items = vec![ + Path::new("a/file1.txt"), // + Path::new("b/file2.txt"), // + ]; + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + assert!(tree.tree_items[1].info().is_visible()); + + tree.collapse(0, false); + + assert!(!tree.tree_items[1].info().is_visible()); + } + + #[test] + fn test_iterate_collapsed() { + let items = vec![ + Path::new("a/file1.txt"), // + Path::new("b/file2.txt"), // + ]; + + let mut tree = + FileTreeItems::new(&items, &BTreeSet::new()).unwrap(); + + tree.collapse(0, false); + + let mut it = tree.iterate(0, 10); + + assert_eq!(it.next().unwrap().0, 0); + assert_eq!(it.next().unwrap().0, 2); + assert_eq!(it.next().unwrap().0, 3); + assert_eq!(it.next(), None); + } + + pub fn get_visibles(tree: &FileTreeItems) -> Vec<bool> { + tree.tree_items + .iter() + .map(|e| e.info().is_visible()) + .collect::<Vec<_>>() + } + + #[test] + fn test_expand() { + let items = |