basic_usage/
basic_usage.rs

1//! Basic usage of anyfs-backend traits.
2//!
3//! This example demonstrates the fundamental operations you can perform
4//! with any filesystem backend that implements the `Fs` trait.
5//!
6//! Run with: `cargo run --example basic_usage`
7
8use anyfs_backend::*;
9use std::collections::HashMap;
10use std::io::{Read, Write};
11use std::path::{Path, PathBuf};
12use std::sync::RwLock;
13use std::time::SystemTime;
14
15// =============================================================================
16// Step 1: Create a minimal filesystem implementation
17// =============================================================================
18
19/// A simple in-memory filesystem that implements `Fs` (the base trait).
20///
21/// This is the minimum viable implementation to use anyfs-backend.
22/// It only implements FsRead, FsWrite, and FsDir.
23struct SimpleFs {
24    files: RwLock<HashMap<PathBuf, Vec<u8>>>,
25    dirs: RwLock<std::collections::HashSet<PathBuf>>,
26}
27
28impl SimpleFs {
29    fn new() -> Self {
30        let fs = Self {
31            files: RwLock::new(HashMap::new()),
32            dirs: RwLock::new(std::collections::HashSet::new()),
33        };
34        // Create root directory
35        fs.dirs.write().unwrap().insert(PathBuf::from("/"));
36        fs
37    }
38}
39
40// Implement FsRead - reading files and metadata
41impl FsRead for SimpleFs {
42    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
43        self.files
44            .read()
45            .unwrap()
46            .get(path)
47            .cloned()
48            .ok_or_else(|| FsError::NotFound {
49                path: path.to_path_buf(),
50            })
51    }
52
53    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
54        let bytes = self.read(path)?;
55        String::from_utf8(bytes).map_err(|_| FsError::InvalidData {
56            path: path.to_path_buf(),
57            details: "not valid UTF-8".into(),
58        })
59    }
60
61    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
62        let data = self.read(path)?;
63        let start = offset as usize;
64        if start >= data.len() {
65            return Ok(Vec::new());
66        }
67        let end = (start + len).min(data.len());
68        Ok(data[start..end].to_vec())
69    }
70
71    fn exists(&self, path: &Path) -> Result<bool, FsError> {
72        Ok(self.files.read().unwrap().contains_key(path)
73            || self.dirs.read().unwrap().contains(path))
74    }
75
76    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
77        if self.dirs.read().unwrap().contains(path) {
78            Ok(Metadata {
79                file_type: FileType::Directory,
80                size: 0,
81                permissions: Permissions::default_dir(),
82                created: SystemTime::UNIX_EPOCH,
83                modified: SystemTime::UNIX_EPOCH,
84                accessed: SystemTime::UNIX_EPOCH,
85                inode: 0,
86                nlink: 1,
87            })
88        } else if let Some(data) = self.files.read().unwrap().get(path) {
89            Ok(Metadata {
90                file_type: FileType::File,
91                size: data.len() as u64,
92                permissions: Permissions::default_file(),
93                created: SystemTime::UNIX_EPOCH,
94                modified: SystemTime::UNIX_EPOCH,
95                accessed: SystemTime::UNIX_EPOCH,
96                inode: 0,
97                nlink: 1,
98            })
99        } else {
100            Err(FsError::NotFound {
101                path: path.to_path_buf(),
102            })
103        }
104    }
105
106    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
107        let data = self.read(path)?;
108        Ok(Box::new(std::io::Cursor::new(data)))
109    }
110}
111
112// Implement FsWrite - writing and modifying files
113impl FsWrite for SimpleFs {
114    fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
115        self.files
116            .write()
117            .unwrap()
118            .insert(path.to_path_buf(), data.to_vec());
119        Ok(())
120    }
121
122    fn append(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
123        self.files
124            .write()
125            .unwrap()
126            .entry(path.to_path_buf())
127            .or_default()
128            .extend_from_slice(data);
129        Ok(())
130    }
131
132    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
133        self.files
134            .write()
135            .unwrap()
136            .remove(path)
137            .map(|_| ())
138            .ok_or_else(|| FsError::NotFound {
139                path: path.to_path_buf(),
140            })
141    }
142
143    fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
144        let mut files = self.files.write().unwrap();
145        let data = files.remove(from).ok_or_else(|| FsError::NotFound {
146            path: from.to_path_buf(),
147        })?;
148        files.insert(to.to_path_buf(), data);
149        Ok(())
150    }
151
152    fn copy(&self, from: &Path, to: &Path) -> Result<(), FsError> {
153        let data = self.read(from)?;
154        self.write(to, &data)
155    }
156
157    fn truncate(&self, path: &Path, size: u64) -> Result<(), FsError> {
158        let mut files = self.files.write().unwrap();
159        let data = files.get_mut(path).ok_or_else(|| FsError::NotFound {
160            path: path.to_path_buf(),
161        })?;
162        data.resize(size as usize, 0);
163        Ok(())
164    }
165
166    fn open_write(&self, _path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
167        Ok(Box::new(std::io::Cursor::new(Vec::new())))
168    }
169}
170
171// Implement FsDir - directory operations
172impl FsDir for SimpleFs {
173    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
174        if !self.dirs.read().unwrap().contains(path) {
175            return Err(FsError::NotFound {
176                path: path.to_path_buf(),
177            });
178        }
179
180        let mut entries = Vec::new();
181
182        // Collect files in this directory
183        for (file_path, data) in self.files.read().unwrap().iter() {
184            if let Some(parent) = file_path.parent() {
185                if parent == path {
186                    if let Some(name) = file_path.file_name() {
187                        entries.push(Ok(DirEntry {
188                            name: name.to_string_lossy().into_owned(),
189                            path: file_path.clone(),
190                            file_type: FileType::File,
191                            size: data.len() as u64,
192                            inode: 0,
193                        }));
194                    }
195                }
196            }
197        }
198
199        // Collect subdirectories
200        for dir_path in self.dirs.read().unwrap().iter() {
201            if let Some(parent) = dir_path.parent() {
202                if parent == path && dir_path != path {
203                    if let Some(name) = dir_path.file_name() {
204                        entries.push(Ok(DirEntry {
205                            name: name.to_string_lossy().into_owned(),
206                            path: dir_path.clone(),
207                            file_type: FileType::Directory,
208                            size: 0,
209                            inode: 0,
210                        }));
211                    }
212                }
213            }
214        }
215
216        Ok(ReadDirIter::from_vec(entries))
217    }
218
219    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
220        let mut dirs = self.dirs.write().unwrap();
221        if dirs.contains(path) {
222            return Err(FsError::AlreadyExists {
223                path: path.to_path_buf(),
224                operation: "create_dir",
225            });
226        }
227        dirs.insert(path.to_path_buf());
228        Ok(())
229    }
230
231    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
232        let mut current = PathBuf::new();
233        for component in path.components() {
234            current.push(component);
235            self.dirs.write().unwrap().insert(current.clone());
236        }
237        Ok(())
238    }
239
240    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
241        if !self.dirs.write().unwrap().remove(path) {
242            return Err(FsError::NotFound {
243                path: path.to_path_buf(),
244            });
245        }
246        Ok(())
247    }
248
249    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
250        self.dirs.write().unwrap().remove(path);
251        self.files
252            .write()
253            .unwrap()
254            .retain(|p, _| !p.starts_with(path));
255        Ok(())
256    }
257}
258
259// =============================================================================
260// Step 2: Use the filesystem
261// =============================================================================
262
263fn main() {
264    println!("=== anyfs-backend Basic Usage Example ===\n");
265
266    // Create our filesystem
267    let fs = SimpleFs::new();
268
269    // --- Writing files ---
270    println!("1. Writing files...");
271    fs.write(Path::new("/hello.txt"), b"Hello, World!").unwrap();
272    fs.write(Path::new("/data.bin"), &[0x00, 0x01, 0x02, 0x03])
273        .unwrap();
274    println!("   Created /hello.txt and /data.bin");
275
276    // --- Reading files ---
277    println!("\n2. Reading files...");
278    let text = fs.read_to_string(Path::new("/hello.txt")).unwrap();
279    println!("   /hello.txt contains: {text}");
280
281    let binary = fs.read(Path::new("/data.bin")).unwrap();
282    println!("   /data.bin contains: {binary:?}");
283
284    // --- Checking existence ---
285    println!("\n3. Checking existence...");
286    println!(
287        "   /hello.txt exists: {}",
288        fs.exists(Path::new("/hello.txt")).unwrap()
289    );
290    println!(
291        "   /missing.txt exists: {}",
292        fs.exists(Path::new("/missing.txt")).unwrap()
293    );
294
295    // --- Getting metadata ---
296    println!("\n4. Getting metadata...");
297    let meta = fs.metadata(Path::new("/hello.txt")).unwrap();
298    println!(
299        "   /hello.txt: type={:?}, size={}",
300        meta.file_type, meta.size
301    );
302
303    // --- Directory operations ---
304    println!("\n5. Directory operations...");
305    fs.create_dir(Path::new("/subdir")).unwrap();
306    fs.write(Path::new("/subdir/nested.txt"), b"Nested file")
307        .unwrap();
308
309    println!("   Created /subdir/nested.txt");
310    println!("   Contents of /:");
311    for entry in fs.read_dir(Path::new("/")).unwrap() {
312        let entry = entry.unwrap();
313        println!("     - {} ({:?})", entry.name, entry.file_type);
314    }
315
316    // --- Copy and rename ---
317    println!("\n6. Copy and rename...");
318    fs.copy(Path::new("/hello.txt"), Path::new("/hello_copy.txt"))
319        .unwrap();
320    fs.rename(Path::new("/hello_copy.txt"), Path::new("/greeting.txt"))
321        .unwrap();
322    println!("   Copied /hello.txt to /greeting.txt (via rename)");
323
324    // --- Append ---
325    println!("\n7. Appending to files...");
326    fs.append(Path::new("/hello.txt"), b" Appended!").unwrap();
327    let updated = fs.read_to_string(Path::new("/hello.txt")).unwrap();
328    println!("   /hello.txt now contains: {updated}");
329
330    // --- Error handling ---
331    println!("\n8. Error handling...");
332    match fs.read(Path::new("/nonexistent.txt")) {
333        Ok(_) => println!("   Unexpected success"),
334        Err(FsError::NotFound { path }) => {
335            println!("   Correctly got NotFound for: {}", path.display());
336        }
337        Err(e) => println!("   Unexpected error: {e}"),
338    }
339
340    // --- Using the Fs trait bound ---
341    println!("\n9. Using generic functions...");
342    fn count_files<B: Fs>(fs: &B, dir: &Path) -> usize {
343        fs.read_dir(dir)
344            .map(|iter| iter.filter(|e| e.is_ok()).count())
345            .unwrap_or(0)
346    }
347    println!("   Files in /: {}", count_files(&fs, Path::new("/")));
348
349    println!("\n=== Example complete! ===");
350}