use crate::{
error::Error,
item::{FileTreeItemKind, PathCollapsed},
FileTreeItem,
};
use crate::{error::Result, treeitems_iter::TreeItemsIterator};
use std::{
collections::{BTreeSet, HashMap},
path::{Path, PathBuf},
usize,
};
///
#[derive(Default)]
pub struct FileTreeItems {
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: