Skip to main content

kaish_kernel/vfs/
builtin_fs.rs

1//! BuiltinFs — read-only VFS that presents builtins as executable entries under `/v/bin/`.
2
3use std::io;
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7use async_trait::async_trait;
8
9use crate::tools::ToolRegistry;
10use super::traits::{DirEntry, EntryType, Filesystem, Metadata};
11
12/// A read-only filesystem that exposes registered builtins as entries.
13pub struct BuiltinFs {
14    tools: Arc<ToolRegistry>,
15}
16
17impl BuiltinFs {
18    pub fn new(tools: Arc<ToolRegistry>) -> Self {
19        Self { tools }
20    }
21}
22
23#[async_trait]
24impl Filesystem for BuiltinFs {
25    async fn read(&self, path: &Path) -> io::Result<Vec<u8>> {
26        let name = path.to_str().unwrap_or("");
27        if self.tools.get(name).is_some() {
28            Ok(format!("#!/v/bin — kaish builtin: {}\n", name).into_bytes())
29        } else {
30            Err(io::Error::new(io::ErrorKind::NotFound, "builtin not found"))
31        }
32    }
33
34    async fn write(&self, _path: &Path, _data: &[u8]) -> io::Result<()> {
35        Err(io::Error::new(io::ErrorKind::PermissionDenied, "/v/bin is read-only"))
36    }
37
38    async fn list(&self, path: &Path) -> io::Result<Vec<DirEntry>> {
39        let p = path.to_str().unwrap_or("");
40        if !p.is_empty() && p != "." {
41            return Err(io::Error::new(io::ErrorKind::NotFound, "not a directory"));
42        }
43        let mut entries: Vec<DirEntry> = self.tools.names().iter().map(|name| DirEntry {
44            name: name.to_string(),
45            entry_type: EntryType::File,
46            size: 0,
47            symlink_target: None,
48        }).collect();
49        entries.sort_by(|a, b| a.name.cmp(&b.name));
50        Ok(entries)
51    }
52
53    async fn stat(&self, path: &Path) -> io::Result<Metadata> {
54        let name = path.to_str().unwrap_or("");
55        if name.is_empty() || name == "." {
56            return Ok(Metadata {
57                is_dir: true,
58                is_file: false,
59                is_symlink: false,
60                size: 0,
61                modified: None,
62            });
63        }
64        if self.tools.get(name).is_some() {
65            Ok(Metadata {
66                is_dir: false,
67                is_file: true,
68                is_symlink: false,
69                size: 0,
70                modified: None,
71            })
72        } else {
73            Err(io::Error::new(io::ErrorKind::NotFound, "builtin not found"))
74        }
75    }
76
77    async fn mkdir(&self, _path: &Path) -> io::Result<()> {
78        Err(io::Error::new(io::ErrorKind::PermissionDenied, "/v/bin is read-only"))
79    }
80
81    async fn remove(&self, _path: &Path) -> io::Result<()> {
82        Err(io::Error::new(io::ErrorKind::PermissionDenied, "/v/bin is read-only"))
83    }
84
85    fn read_only(&self) -> bool {
86        true
87    }
88
89    fn real_path(&self, _path: &Path) -> Option<PathBuf> {
90        None
91    }
92}