arui_core/tree/node.rs
1//! # 项目树节点
2//! 维护节点层级的特性,包含数据获取等等各类节点级操作。
3//! 注意!项目树节点一般由 tree 代理,而不应该由用户手动控制,某些情况下可以对节点进行数据获取操作,但应该仅限于此。
4pub mod count;
5pub mod file;
6use crate::{tree::summary::NodeSummary, utils::check_path};
7use std::fmt::Display;
8
9/// 目录树节点
10/// - 节点为文件时,无子树
11/// - 节点为目录时,有子树
12#[derive(Debug, Clone)]
13pub struct TreeNode {
14 /// 当前节点所处路径
15 pub path: String,
16 /// 是否是目录
17 /// TODO:后续应该改为自动检测
18 pub is_dir: bool,
19 /// 如果是目录,那么遍历他的子节点;如果为文件,则为空
20 pub children: Option<Vec<TreeNode>>,
21 /// 节点总结信息
22 /// - 文件:当前文件的总结信息
23 /// - 目录:当前目录下所有文件的总结信息的加合
24 pub summary: NodeSummary,
25}
26
27/// 为节点实现 Display
28impl Display for TreeNode {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(
31 f,
32 "\n----- TreeNode -----\n\n- path: {}\n- is_dir: {}\n- children: {}\n- summary: {}\n\n--------------------",
33 self.path,
34 self.is_dir,
35 self.children.as_ref().unwrap_or(&Vec::new()).len(),
36 self.summary
37 )
38 }
39}
40
41/// 节点配置相关
42impl TreeNode {
43 /// 创建一个项目树节点
44 ///
45 /// # Examples
46 ///
47 /// ```rust
48 /// use arui_core::tree::node::TreeNode;
49 /// let node = TreeNode::new("./src", true);
50 /// ```
51 pub fn new<P>(path: P, is_dir: bool) -> Self
52 where
53 P: Into<String>,
54 {
55 TreeNode {
56 is_dir,
57 path: path.into(),
58 children: if is_dir { Some(Vec::new()) } else { None },
59 summary: NodeSummary::new(),
60 }
61 }
62
63 /// 检测节点路径是否合法
64 ///
65 /// # Examples
66 ///
67 /// ```rust
68 /// use arui_core::tree::node::TreeNode;
69 ///
70 /// let valid = TreeNode::new("./src", true).is_valid();
71 /// assert_eq!(valid, true);
72 ///
73 /// let in_valid = TreeNode::new("/not_exist", true).is_valid();
74 /// assert_eq!(in_valid, false);
75 /// ```
76 pub fn is_valid(&self) -> bool {
77 check_path(&self.path).is_ok()
78 }
79}
80
81/// 为节点实现总结信息相关操作
82impl TreeNode {
83 /// 初次调用为获取节点总结信息
84 /// 重复调用为更新节点总结信息,覆盖式更新当前节点的总结信息
85 ///
86 /// # Examples
87 ///
88 /// ```rust
89 /// use arui_core::tree::node::TreeNode;
90 ///
91 /// // 初始化目录节点,无子节点
92 /// let mut folder_node = TreeNode::new("./src", true);
93 /// // 初始获取总结信息
94 /// folder_node.upsert_summary();
95 /// assert_eq!(folder_node.summary.size, 0);
96 /// assert_eq!(folder_node.summary.count, 0);
97 ///
98 /// // 加入子节点
99 /// let sub_node = TreeNode::new("./src/lib.rs", false);
100 /// folder_node.children = Some(vec![sub_node]);
101 /// // 重复调用,覆盖式更新节点总结信息
102 /// folder_node.upsert_summary();
103 /// assert_eq!(folder_node.summary.size > 0, true);
104 /// assert_eq!(folder_node.summary.count > 0, true);
105 ///
106 /// // 初始化文件节点
107 /// let mut file_node = TreeNode::new("./src/lib.rs", false);
108 /// file_node.upsert_summary();
109 /// assert_eq!(file_node.summary.size > 0, true);
110 /// assert_eq!(file_node.summary.count > 0, true);
111 /// ```
112 pub fn upsert_summary(&mut self) {
113 let summary = NodeSummary::update(self);
114 self.summary = summary;
115 }
116}
117
118// --------------------- 单元测试 ---------------------
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_display() {
126 let node = TreeNode::new("./tests/examples/tree/summary".to_string(), true);
127 println!("{}", node);
128 }
129
130 #[test]
131 /// 测试节点创建
132 fn test_new() {
133 let node = TreeNode::new("./tests/examples/tree/summary".to_string(), true);
134 assert_eq!(node.path, "./tests/examples/tree/summary");
135 assert_eq!(node.is_dir, true);
136 assert!(node.children.is_some());
137 // 检查总结信息
138 assert_eq!(node.summary.size, 0);
139 assert_eq!(node.summary.count, 0);
140 }
141
142 #[test]
143 /// 在测试中调用
144 fn test_summary_update() {
145 let mut node = TreeNode::new("./tests/examples/tree/summary", true);
146 node.upsert_summary();
147 println!("{}", node);
148 }
149}