use std::{
fmt::Display,
path::{Path, PathBuf},
vec::Vec,
};
use crate::{
impls::SortedVec,
prelude::{DiGraph, GenericEdgesBuilder},
traits::{EdgesBuilder, MonopartiteGraph, MonoplexGraph},
};
#[cfg_attr(feature = "mem_size", derive(mem_dbg::MemSize))]
#[cfg_attr(feature = "mem_size", mem_size(rec))]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DirectoryTree {
graph: DiGraph<PathBuf>,
}
fn recursive_build_tree(
path: &Path,
path_id: usize,
nodes: &mut Vec<PathBuf>,
edges: &mut Vec<(usize, usize)>,
) {
if let Ok(entries) = path.read_dir() {
let mut entries = entries.flatten().collect::<Vec<_>>();
entries.sort_by_key(std::fs::DirEntry::path);
for entry in entries {
let child_path = entry.path();
let child_id = nodes.len();
edges.push((path_id, child_id));
nodes.push(child_path.clone());
if child_path.is_dir() {
recursive_build_tree(&child_path, child_id, nodes, edges);
}
}
}
}
impl From<PathBuf> for DirectoryTree {
#[inline]
fn from(path: PathBuf) -> Self {
let mut nodes = Vec::new();
let mut edges = Vec::new();
nodes.push(path.clone());
recursive_build_tree(&path, 0, &mut nodes, &mut edges);
edges.sort_unstable();
let edges = GenericEdgesBuilder::default()
.edges(edges)
.expected_shape(nodes.len())
.build()
.expect("Failed to build directory tree edges");
let graph = DiGraph::from((
SortedVec::try_from(nodes).expect("Failed to build directory tree nodes"),
edges,
));
DirectoryTree { graph }
}
}
fn display_node(
f: &mut core::fmt::Formatter<'_>,
graph: &DiGraph<PathBuf>,
node: usize,
indent_level: usize,
) -> core::fmt::Result {
for successor in graph.successors(node) {
for _ in 0..indent_level {
write!(f, " ")?; }
let path = &graph.nodes_vocabulary()[successor];
let last_segment = path.file_name().unwrap_or_default();
writeln!(f, "/{}", last_segment.to_string_lossy())?;
display_node(f, graph, successor, indent_level + 1)?;
}
Ok(())
}
impl Display for DirectoryTree {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let base_path = &self.graph.nodes_vocabulary()[0];
writeln!(f, "{}", base_path.display())?;
display_node(f, &self.graph, 0, 1)
}
}