fresh/view/file_tree/
node.rs1use crate::model::filesystem::DirEntry;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub struct NodeId(pub usize);
7
8impl fmt::Display for NodeId {
9 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10 write!(f, "Node({})", self.0)
11 }
12}
13
14#[derive(Debug, Clone)]
16pub struct TreeNode {
17 pub id: NodeId,
19 pub entry: DirEntry,
21 pub parent: Option<NodeId>,
23 pub children: Vec<NodeId>,
25 pub state: NodeState,
27}
28
29impl TreeNode {
30 pub fn new(id: NodeId, entry: DirEntry, parent: Option<NodeId>) -> Self {
32 let state = if entry.is_dir() {
33 NodeState::Collapsed
34 } else {
35 NodeState::Leaf
36 };
37
38 Self {
39 id,
40 entry,
41 parent,
42 children: Vec::new(),
43 state,
44 }
45 }
46
47 pub fn is_dir(&self) -> bool {
49 self.entry.is_dir()
50 }
51
52 pub fn is_file(&self) -> bool {
54 self.entry.is_file()
55 }
56
57 pub fn is_expanded(&self) -> bool {
59 self.state == NodeState::Expanded
60 }
61
62 pub fn is_collapsed(&self) -> bool {
64 self.state == NodeState::Collapsed
65 }
66
67 pub fn is_loading(&self) -> bool {
69 self.state == NodeState::Loading
70 }
71
72 pub fn is_error(&self) -> bool {
74 matches!(self.state, NodeState::Error(_))
75 }
76
77 pub fn is_leaf(&self) -> bool {
79 self.state == NodeState::Leaf
80 }
81
82 pub fn depth(&self, get_parent: impl Fn(NodeId) -> Option<NodeId>) -> usize {
84 let mut depth = 0;
85 let mut current = self.parent;
86
87 while let Some(parent_id) = current {
88 depth += 1;
89 current = get_parent(parent_id);
90 }
91
92 depth
93 }
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub enum NodeState {
99 Collapsed,
101 Loading,
103 Expanded,
105 Error(String),
107 Leaf,
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::model::filesystem::{DirEntry, EntryType};
115 use std::path::PathBuf;
116
117 #[test]
118 fn test_node_creation() {
119 let entry = DirEntry::new(
120 PathBuf::from("/test/file.txt"),
121 "file.txt".to_string(),
122 EntryType::File,
123 );
124
125 let node = TreeNode::new(NodeId(0), entry, None);
126
127 assert_eq!(node.id, NodeId(0));
128 assert_eq!(node.parent, None);
129 assert!(node.is_file());
130 assert!(node.is_leaf());
131 assert_eq!(node.children.len(), 0);
132 }
133
134 #[test]
135 fn test_directory_node() {
136 let entry = DirEntry::new(
137 PathBuf::from("/test/dir"),
138 "dir".to_string(),
139 EntryType::Directory,
140 );
141
142 let node = TreeNode::new(NodeId(1), entry, Some(NodeId(0)));
143
144 assert!(node.is_dir());
145 assert!(node.is_collapsed());
146 assert!(!node.is_expanded());
147 assert_eq!(node.parent, Some(NodeId(0)));
148 }
149
150 #[test]
151 fn test_node_states() {
152 let entry = DirEntry::new(
153 PathBuf::from("/test/dir"),
154 "dir".to_string(),
155 EntryType::Directory,
156 );
157
158 let mut node = TreeNode::new(NodeId(0), entry, None);
159
160 assert!(node.is_collapsed());
161 assert!(!node.is_loading());
162 assert!(!node.is_error());
163
164 node.state = NodeState::Loading;
165 assert!(node.is_loading());
166 assert!(!node.is_collapsed());
167
168 node.state = NodeState::Expanded;
169 assert!(node.is_expanded());
170 assert!(!node.is_loading());
171
172 node.state = NodeState::Error("Failed to read".to_string());
173 assert!(node.is_error());
174 assert!(!node.is_expanded());
175 }
176
177 #[test]
178 fn test_node_depth() {
179 let root = TreeNode::new(
181 NodeId(0),
182 DirEntry::new(PathBuf::from("/"), "/".to_string(), EntryType::Directory),
183 None,
184 );
185
186 let child1 = TreeNode::new(
187 NodeId(1),
188 DirEntry::new(
189 PathBuf::from("/dir1"),
190 "dir1".to_string(),
191 EntryType::Directory,
192 ),
193 Some(NodeId(0)),
194 );
195
196 let child2 = TreeNode::new(
197 NodeId(2),
198 DirEntry::new(
199 PathBuf::from("/dir1/dir2"),
200 "dir2".to_string(),
201 EntryType::Directory,
202 ),
203 Some(NodeId(1)),
204 );
205
206 let get_parent = |id: NodeId| match id.0 {
208 0 => None,
209 1 => Some(NodeId(0)),
210 2 => Some(NodeId(1)),
211 _ => None,
212 };
213
214 assert_eq!(root.depth(get_parent), 0);
215 assert_eq!(child1.depth(get_parent), 1);
216 assert_eq!(child2.depth(get_parent), 2);
217 }
218}