use crate::category::FileCategory;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(pub u32);
#[derive(Debug, Clone)]
pub struct TreeNode {
pub name: String,
pub kind: NodeKind,
pub total_size: u64,
pub file_count: u64,
pub parent: Option<NodeId>,
pub depth: u16,
}
#[derive(Debug, Clone)]
pub enum NodeKind {
File {
size: u64,
category: FileCategory,
modified: Option<SystemTime>,
},
Directory {
children: Vec<NodeId>,
},
}
#[derive(Debug)]
pub struct FileTree {
nodes: Vec<TreeNode>,
root: NodeId,
scan_root: PathBuf,
}
impl FileTree {
pub fn node(&self, id: NodeId) -> &TreeNode {
debug_assert!(
(id.0 as usize) < self.nodes.len(),
"invalid NodeId: {} (tree has {} nodes)",
id.0,
self.nodes.len()
);
&self.nodes[id.0 as usize]
}
pub fn root(&self) -> NodeId {
self.root
}
pub fn scan_root(&self) -> &Path {
&self.scan_root
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn children(&self, id: NodeId) -> &[NodeId] {
match &self.node(id).kind {
NodeKind::Directory { children } => children,
NodeKind::File { .. } => &[],
}
}
pub fn path(&self, id: NodeId) -> PathBuf {
let mut components = Vec::new();
let mut current = id;
loop {
let node = self.node(current);
match node.parent {
Some(parent) => {
components.push(node.name.as_str());
current = parent;
}
None => break, }
}
components.reverse();
let mut path = PathBuf::new();
for c in components {
path.push(c);
}
path
}
}
pub fn build_file_tree(dir_node: &super::DirNode, scan_root: PathBuf) -> FileTree {
let mut nodes = Vec::new();
let root_name = scan_root
.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| scan_root.to_string_lossy().into_owned());
build_recursive(dir_node, &root_name, None, 0, &mut nodes);
let root = NodeId(0);
FileTree {
nodes,
root,
scan_root,
}
}
fn build_recursive(
dir_node: &super::DirNode,
name: &str,
parent: Option<NodeId>,
depth: u16,
nodes: &mut Vec<TreeNode>,
) -> NodeId {
let my_id = NodeId(nodes.len() as u32);
if dir_node.children.is_empty() {
let category = FileCategory::from_path(Path::new(name));
nodes.push(TreeNode {
name: name.to_string(),
kind: NodeKind::File {
size: dir_node.file_size,
category,
modified: None,
},
total_size: dir_node.file_size,
file_count: 1,
parent,
depth,
});
return my_id;
}
nodes.push(TreeNode {
name: name.to_string(),
kind: NodeKind::Directory {
children: Vec::new(),
},
total_size: 0,
file_count: 0,
parent,
depth,
});
let mut child_ids: Vec<NodeId> = Vec::with_capacity(dir_node.children.len());
let mut total_size = dir_node.file_size; let mut file_count: u64 = dir_node.file_count as u64;
for (child_name, child_node) in &dir_node.children {
let child_id = build_recursive(child_node, child_name, Some(my_id), depth + 1, nodes);
let child = &nodes[child_id.0 as usize];
total_size += child.total_size;
file_count += child.file_count;
child_ids.push(child_id);
}
child_ids.sort_by(|a, b| {
let sa = nodes[b.0 as usize].total_size;
let sb = nodes[a.0 as usize].total_size;
sa.cmp(&sb)
});
let node = &mut nodes[my_id.0 as usize];
node.kind = NodeKind::Directory {
children: child_ids,
};
node.total_size = total_size;
node.file_count = file_count;
my_id
}
#[cfg(test)]
#[path = "arena_tests.rs"]
mod arena_tests;