Skip to main content

dux_core/tree/
node.rs

1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5/// Unique identifier for a node in the tree
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub struct NodeId(pub usize);
8
9impl NodeId {
10    pub const ROOT: NodeId = NodeId(0);
11
12    pub fn index(&self) -> usize {
13        self.0
14    }
15}
16
17/// Type of filesystem entry
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19pub enum NodeKind {
20    Directory,
21    File,
22    Symlink,
23    Error,
24}
25
26impl NodeKind {
27    pub fn icon(&self) -> &'static str {
28        match self {
29            NodeKind::Directory => "📁",
30            NodeKind::File => "📄",
31            NodeKind::Symlink => "🔗",
32            NodeKind::Error => "⚠️",
33        }
34    }
35
36    pub fn is_directory(&self) -> bool {
37        matches!(self, NodeKind::Directory)
38    }
39}
40
41/// A node in the disk tree
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct TreeNode {
44    pub id: NodeId,
45    pub name: String,
46    pub kind: NodeKind,
47    /// Actual disk usage in bytes
48    pub size: u64,
49    /// Number of files (including self if file)
50    pub file_count: u64,
51    /// Parent node (None for root)
52    pub parent: Option<NodeId>,
53    /// Children sorted by size descending
54    pub children: Vec<NodeId>,
55    /// Depth in tree (0 for root)
56    pub depth: u16,
57    /// Whether directory is expanded in UI
58    #[serde(skip)]
59    pub is_expanded: bool,
60    /// Full path to this node (reconstructed on cache load)
61    #[serde(skip)]
62    pub path: PathBuf,
63}
64
65impl TreeNode {
66    pub fn new(
67        id: NodeId,
68        name: String,
69        kind: NodeKind,
70        path: PathBuf,
71        parent: Option<NodeId>,
72        depth: u16,
73    ) -> Self {
74        Self {
75            id,
76            name,
77            kind,
78            size: 0,
79            file_count: if kind == NodeKind::File { 1 } else { 0 },
80            parent,
81            children: Vec::new(),
82            depth,
83            is_expanded: depth == 0, // Root starts expanded
84            path,
85        }
86    }
87
88    pub fn has_children(&self) -> bool {
89        !self.children.is_empty()
90    }
91
92    pub fn is_expandable(&self) -> bool {
93        self.kind.is_directory() && !self.children.is_empty()
94    }
95}