summaryrefslogtreecommitdiffstats
path: root/filetreelist/src/filetreeitems.rs
diff options
context:
space:
mode:
Diffstat (limited to 'filetreelist/src/filetreeitems.rs')
-rw-r--r--filetreelist/src/filetreeitems.rs1586
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 =