Skip to main content

distri_filesystem/
wrapper.rs

1use crate::GrepSearcher;
2use anyhow::Result;
3use async_trait::async_trait;
4use distri_types::filesystem::{
5    DirectoryListing, FileMetadata, FileReadResult, FileSystemOps, ReadParams, SearchResult,
6};
7use std::sync::Arc;
8
9/// File system abstraction for compatibility  
10#[derive(Debug, Clone)]
11pub struct FileSystem {
12    file_store: Arc<crate::FileSystemStore>,
13    grep_searcher: Arc<dyn GrepSearcher>,
14    root_prefix: Option<String>,
15}
16
17impl FileSystem {
18    pub fn new(
19        file_store: Arc<crate::FileSystemStore>,
20        grep_searcher: Arc<dyn GrepSearcher>,
21    ) -> Self {
22        let root_prefix = file_store.root_prefix();
23        Self {
24            file_store,
25            grep_searcher,
26            root_prefix,
27        }
28    }
29
30    pub fn root_prefix(&self) -> Option<String> {
31        self.root_prefix.clone()
32    }
33
34    pub fn create_tools(&self) -> Vec<Arc<dyn distri_types::Tool>> {
35        crate::create_filesystem_tools(Arc::new(self.clone()))
36    }
37
38    pub fn scoped(&self, prefix: Option<&str>) -> anyhow::Result<Self> {
39        let store = Arc::new(self.file_store.scoped(prefix)?);
40        let grep_searcher =
41            Arc::new(crate::FileSystemGrepSearcher::new(store.clone())) as Arc<dyn GrepSearcher>;
42        Ok(Self {
43            root_prefix: store.root_prefix(),
44            file_store: store,
45            grep_searcher,
46        })
47    }
48
49    /// Create an artifact wrapper for processing tool responses
50    pub async fn create_artifact_wrapper(
51        &self,
52        base_path: String,
53    ) -> Result<crate::ArtifactWrapper, anyhow::Error> {
54        crate::ArtifactWrapper::new(Arc::new(self.clone()), base_path).await
55    }
56
57    /// Write binary data to a file (for screenshots, images, etc.)
58    pub async fn write_binary(&self, path: &str, content: &[u8]) -> Result<()> {
59        self.file_store.write_binary(path, content).await
60    }
61
62    /// Read binary data from a file
63    pub async fn read_binary(&self, path: &str) -> Result<Vec<u8>> {
64        self.file_store.read_binary(path).await
65    }
66}
67
68#[async_trait]
69impl FileSystemOps for FileSystem {
70    async fn read(&self, path: &str, params: ReadParams) -> Result<FileReadResult> {
71        let read_params = crate::ReadParams {
72            start_line: params.start_line,
73            end_line: params.end_line,
74        };
75        let result = self.file_store.read(path, read_params).await?;
76        Ok(FileReadResult {
77            content: result.content,
78            start_line: result.start_line,
79            end_line: result.end_line,
80            total_lines: result.total_lines,
81        })
82    }
83
84    async fn read_raw(&self, path: &str) -> Result<String> {
85        self.file_store.read_raw(path).await
86    }
87
88    async fn read_with_line_numbers(
89        &self,
90        path: &str,
91        params: ReadParams,
92    ) -> Result<FileReadResult> {
93        self.read(path, params).await
94    }
95
96    async fn write(&self, path: &str, content: &str) -> Result<()> {
97        self.file_store.write(path, content).await
98    }
99
100    async fn list(&self, path: &str) -> Result<DirectoryListing> {
101        let listing = self.file_store.list(path).await?;
102        Ok(DirectoryListing {
103            path: listing.path,
104            entries: listing
105                .entries
106                .into_iter()
107                .map(|e| distri_types::filesystem::DirectoryEntry {
108                    name: e.name,
109                    is_file: e.is_file,
110                    is_dir: e.is_dir,
111                    size: e.size,
112                })
113                .collect(),
114        })
115    }
116
117    async fn delete(&self, path: &str, recursive: bool) -> Result<()> {
118        self.file_store.delete(path, recursive).await
119    }
120
121    async fn search(
122        &self,
123        path: &str,
124        content_pattern: Option<&str>,
125        file_pattern: Option<&str>,
126    ) -> Result<SearchResult> {
127        let result = self
128            .grep_searcher
129            .search(path, content_pattern, file_pattern)
130            .await?;
131        Ok(SearchResult {
132            path: result.path,
133            matches: result
134                .matches
135                .into_iter()
136                .map(|m| distri_types::filesystem::SearchMatch {
137                    file_path: m.file_path,
138                    line_number: m.line_number,
139                    line_content: m.line_content,
140                    match_text: m.match_text,
141                })
142                .collect(),
143        })
144    }
145
146    async fn copy(&self, from: &str, to: &str) -> Result<()> {
147        let content = self
148            .file_store
149            .read(from, crate::ReadParams::default())
150            .await?
151            .content;
152        self.file_store.write(to, &content).await
153    }
154
155    async fn move_file(&self, from: &str, to: &str) -> Result<()> {
156        self.copy(from, to).await?;
157        self.file_store.delete(from, false).await
158    }
159
160    async fn mkdir(&self, _path: &str) -> Result<()> {
161        // Directories are created automatically when writing files
162        Ok(())
163    }
164
165    async fn info(&self, path: &str) -> Result<FileMetadata> {
166        self.file_store.info(path).await
167    }
168
169    async fn tree(&self, path: &str) -> Result<DirectoryListing> {
170        self.list(path).await
171    }
172}
173
174pub async fn create_file_system(config: crate::FileSystemConfig) -> anyhow::Result<FileSystem> {
175    let file_store = Arc::new(crate::FileSystemStore::new(config).await?);
176    let grep_searcher =
177        Arc::new(crate::FileSystemGrepSearcher::new(file_store.clone())) as Arc<dyn GrepSearcher>;
178
179    Ok(FileSystem::new(file_store, grep_searcher))
180}