Skip to main content

st/inputs/
filesystem.rs

1//! Traditional file system input adapter
2
3use super::*;
4use anyhow::Result;
5use async_trait::async_trait;
6use std::fs;
7
8pub struct FileSystemAdapter;
9
10#[async_trait]
11impl InputAdapter for FileSystemAdapter {
12    fn name(&self) -> &'static str {
13        "FileSystem"
14    }
15
16    fn supported_formats(&self) -> Vec<&'static str> {
17        vec!["dir", "directory", "folder", "path"]
18    }
19
20    async fn can_handle(&self, input: &InputSource) -> bool {
21        match input {
22            InputSource::Path(path) => path.exists(),
23            _ => false,
24        }
25    }
26
27    async fn parse(&self, input: InputSource) -> Result<ContextNode> {
28        match input {
29            InputSource::Path(path) => {
30                let metadata = fs::metadata(&path)?;
31                let name = path
32                    .file_name()
33                    .and_then(|n| n.to_str())
34                    .unwrap_or("root")
35                    .to_string();
36
37                let mut root = ContextNode {
38                    id: path.to_string_lossy().to_string(),
39                    name,
40                    node_type: if metadata.is_dir() {
41                        NodeType::Directory
42                    } else {
43                        NodeType::File
44                    },
45                    quantum_state: None,
46                    children: vec![],
47                    metadata: serde_json::json!({
48                        "size": metadata.len(),
49                        "modified": metadata.modified().ok(),
50                        "readonly": metadata.permissions().readonly(),
51                    }),
52                    entanglements: vec![],
53                };
54
55                if metadata.is_dir() {
56                    root.children = self.scan_directory(&path)?;
57                }
58
59                Ok(root)
60            }
61            _ => anyhow::bail!("FileSystem adapter only handles Path inputs"),
62        }
63    }
64}
65
66impl FileSystemAdapter {
67    #[allow(clippy::only_used_in_recursion)]
68    fn scan_directory(&self, path: &std::path::Path) -> Result<Vec<ContextNode>> {
69        let mut nodes = Vec::new();
70
71        for entry in fs::read_dir(path)? {
72            let entry = entry?;
73            let path = entry.path();
74            let metadata = entry.metadata()?;
75
76            let node = ContextNode {
77                id: path.to_string_lossy().to_string(),
78                name: entry.file_name().to_string_lossy().to_string(),
79                node_type: if metadata.is_dir() {
80                    NodeType::Directory
81                } else {
82                    NodeType::File
83                },
84                quantum_state: None,
85                children: if metadata.is_dir() {
86                    self.scan_directory(&path).unwrap_or_default()
87                } else {
88                    vec![]
89                },
90                metadata: serde_json::json!({
91                    "size": metadata.len(),
92                    "modified": metadata.modified().ok(),
93                }),
94                entanglements: vec![],
95            };
96
97            nodes.push(node);
98        }
99
100        Ok(nodes)
101    }
102}