filetree/
filetree.rs

1use std::fmt;
2use std::str::FromStr;
3
4use crate::errors::Error;
5use crate::node::{Node, Nodes};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Filetree {
9    root: Node,
10}
11
12impl Filetree {
13    pub fn new(root: Node) -> Filetree {
14        Filetree { root }
15    }
16
17    pub fn root(&self) -> &Node {
18        &self.root
19    }
20
21    #[cfg(feature = "color")]
22    pub fn color_display(&self) -> Result<String, Error> {
23        let mut s = String::new();
24        for node in self.root.nodes() {
25            s.push_str(&format!("{}\n", node.treeline_color(self)));
26        }
27        Ok(s)
28    }
29}
30
31impl fmt::Display for Filetree {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        let mut s = String::new();
34        for node in self.root.nodes() {
35            s.push_str(&format!("{}\n", node.treeline(self)));
36        }
37        write!(f, "{}", s)
38    }
39}
40
41impl FromStr for Filetree {
42    type Err = Error;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        let value: serde_yaml::Value = serde_yaml::from_str(s)?;
46        let mut root = Node::new(".".into(), vec![], true);
47        let mut children: Nodes = value.try_into()?;
48        root.add_children(&mut children.0);
49        Ok(Filetree::new(root))
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use textwrap::dedent;
56    use wasm_bindgen_test::wasm_bindgen_test;
57
58    use super::*;
59
60    macro_rules! test_filetree {
61        ($name:ident, $s:expr, $expected:expr) => {
62            #[wasm_bindgen_test]
63            #[test]
64            fn $name() -> anyhow::Result<()> {
65                let filetree: Filetree = $s.parse()?;
66                println!("{}", filetree);
67                assert_eq!(format!("{}", filetree), format!("{}\n", $expected.trim()));
68                Ok(())
69            }
70        };
71    }
72
73    test_filetree!(empty, "", ".");
74    test_filetree!(
75        flat,
76        "
77        - dir1:
78        - file1.txt
79        - dir2:
80        ",
81        dedent(
82            "
83            .
84            ├── dir1
85            ├── file1.txt
86            └── dir2
87            "
88        )
89    );
90    test_filetree!(
91        nested,
92        "
93        - dir1:
94            - file1.txt
95            - subdir1:
96            - subdir2:
97                - file2.py
98        - dir 2:
99            - subdir3:
100                - subsubdir:
101                    - file3.toml
102                - subsubdir2:
103            - subdir4:
104            - config.ini
105        ",
106        dedent(
107            "
108            .
109            ├── dir1
110            │   ├── file1.txt
111            │   ├── subdir1
112            │   └── subdir2
113            │       └── file2.py
114            └── dir 2
115                ├── subdir3
116                │   ├── subsubdir
117                │   │   └── file3.toml
118                │   └── subsubdir2
119                ├── subdir4
120                └── config.ini
121            "
122        )
123    );
124    test_filetree!(
125        deeply_nested,
126        "
127        - dir1:
128            - dir2:
129                - dir3:
130                    - file
131                - dir4:
132                    - file2
133            - dir5:
134                - file3
135        - dir6:
136            - file4
137        ",
138        dedent(
139            "
140            .
141            ├── dir1
142            │   ├── dir2
143            │   │   ├── dir3
144            │   │   │   └── file
145            │   │   └── dir4
146            │   │       └── file2
147            │   └── dir5
148            │       └── file3
149            └── dir6
150                └── file4
151            "
152        )
153    );
154    test_filetree!(
155        same_filename,
156        "
157        - dir:
158            - file
159        - file
160        ",
161        dedent(
162            "
163            .
164            ├── dir
165            │   └── file
166            └── file
167            "
168        )
169    );
170
171    #[cfg(feature = "color")]
172    #[wasm_bindgen_test]
173    #[test]
174    fn color_display() {
175        use colored::Colorize;
176
177        let s = dedent(
178            "
179            - dir1:
180            - file1.txt
181            - dir2:
182            ",
183        );
184        let expected: String = [
185            ".".cyan().bold().to_string(),
186            format!("├── {}", "dir1".cyan().bold()),
187            "├── file1.txt".into(),
188            format!("└── {}", "dir2".cyan().bold()),
189        ]
190        .into_iter()
191        .collect::<Vec<_>>()
192        .join("\n");
193        let filetree: Filetree = s.parse().unwrap();
194        println!("{}", filetree.color_display().unwrap());
195        assert_eq!(filetree.color_display().unwrap().trim(), expected);
196    }
197}