Skip to main content

ane/data/
file_tree.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Result;
4use walkdir::WalkDir;
5
6#[derive(Debug, Clone)]
7pub struct FileEntry {
8    pub path: PathBuf,
9    pub depth: usize,
10    pub is_dir: bool,
11}
12
13impl FileEntry {
14    pub fn name(&self) -> &str {
15        self.path.file_name().and_then(|n| n.to_str()).unwrap_or("")
16    }
17}
18
19#[derive(Debug, Clone)]
20pub struct FileTree {
21    pub root: PathBuf,
22    pub entries: Vec<FileEntry>,
23}
24
25impl FileTree {
26    pub fn from_dir(root: &Path) -> Result<Self> {
27        let root = root.canonicalize()?;
28        let mut entries = Vec::new();
29
30        for entry in WalkDir::new(&root).sort_by_file_name().min_depth(1) {
31            let entry = entry?;
32            let depth = entry.depth() - 1;
33            entries.push(FileEntry {
34                path: entry.path().to_path_buf(),
35                depth,
36                is_dir: entry.file_type().is_dir(),
37            });
38        }
39
40        Ok(Self { root, entries })
41    }
42
43    pub fn files_only(&self) -> impl Iterator<Item = &FileEntry> {
44        self.entries.iter().filter(|e| !e.is_dir)
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use std::fs;
52    use tempfile::TempDir;
53
54    #[test]
55    fn scan_directory() {
56        let tmp = TempDir::new().unwrap();
57        fs::create_dir(tmp.path().join("sub")).unwrap();
58        fs::write(tmp.path().join("file.txt"), "content").unwrap();
59        fs::write(tmp.path().join("sub/nested.rs"), "fn main() {}").unwrap();
60        fs::write(tmp.path().join(".hidden"), "secret").unwrap();
61
62        let tree = FileTree::from_dir(tmp.path()).unwrap();
63        let names: Vec<&str> = tree.entries.iter().map(|e| e.name()).collect();
64        assert!(names.contains(&"file.txt"));
65        assert!(names.contains(&"sub"));
66        assert!(names.contains(&"nested.rs"));
67        assert!(names.contains(&".hidden"));
68    }
69}