layer_middleware/
layer_middleware.rs

1//! Using the Layer trait for middleware composition.
2//!
3//! This example demonstrates how to use the Tower-style Layer pattern
4//! to add cross-cutting functionality (logging, caching, metrics) to
5//! any filesystem backend.
6//!
7//! Run with: `cargo run --example layer_middleware`
8
9use anyfs_backend::*;
10use std::collections::HashMap;
11use std::io::{Read, Write};
12use std::path::{Path, PathBuf};
13use std::sync::atomic::{AtomicUsize, Ordering};
14use std::sync::RwLock;
15use std::time::SystemTime;
16
17// =============================================================================
18// Layer 1: Logging Layer
19// =============================================================================
20
21/// A layer that logs all filesystem operations.
22struct LoggingLayer;
23
24/// The logging middleware that wraps any Fs backend.
25struct LoggingFs<B> {
26    inner: B,
27    prefix: String,
28}
29
30impl<B: Fs> Layer<B> for LoggingLayer {
31    type Backend = LoggingFs<B>;
32
33    fn layer(self, inner: B) -> Self::Backend {
34        LoggingFs {
35            inner,
36            prefix: "[LOG]".to_string(),
37        }
38    }
39}
40
41impl<B: FsRead> FsRead for LoggingFs<B> {
42    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
43        println!("{} read({})", self.prefix, path.display());
44        let result = self.inner.read(path);
45        println!(
46            "{} read({}) -> {:?}",
47            self.prefix,
48            path.display(),
49            result.as_ref().map(|d| format!("{} bytes", d.len()))
50        );
51        result
52    }
53
54    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
55        println!("{} read_to_string({})", self.prefix, path.display());
56        self.inner.read_to_string(path)
57    }
58
59    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
60        println!(
61            "{} read_range({}, {}, {})",
62            self.prefix,
63            path.display(),
64            offset,
65            len
66        );
67        self.inner.read_range(path, offset, len)
68    }
69
70    fn exists(&self, path: &Path) -> Result<bool, FsError> {
71        println!("{} exists({})", self.prefix, path.display());
72        self.inner.exists(path)
73    }
74
75    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
76        println!("{} metadata({})", self.prefix, path.display());
77        self.inner.metadata(path)
78    }
79
80    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
81        println!("{} open_read({})", self.prefix, path.display());
82        self.inner.open_read(path)
83    }
84}
85
86impl<B: FsWrite> FsWrite for LoggingFs<B> {
87    fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
88        println!(
89            "{} write({}, {} bytes)",
90            self.prefix,
91            path.display(),
92            data.len()
93        );
94        self.inner.write(path, data)
95    }
96
97    fn append(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
98        println!(
99            "{} append({}, {} bytes)",
100            self.prefix,
101            path.display(),
102            data.len()
103        );
104        self.inner.append(path, data)
105    }
106
107    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
108        println!("{} remove_file({})", self.prefix, path.display());
109        self.inner.remove_file(path)
110    }
111
112    fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
113        println!(
114            "{} rename({} -> {})",
115            self.prefix,
116            from.display(),
117            to.display()
118        );
119        self.inner.rename(from, to)
120    }
121
122    fn copy(&self, from: &Path, to: &Path) -> Result<(), FsError> {
123        println!(
124            "{} copy({} -> {})",
125            self.prefix,
126            from.display(),
127            to.display()
128        );
129        self.inner.copy(from, to)
130    }
131
132    fn truncate(&self, path: &Path, size: u64) -> Result<(), FsError> {
133        println!("{} truncate({}, {})", self.prefix, path.display(), size);
134        self.inner.truncate(path, size)
135    }
136
137    fn open_write(&self, path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
138        println!("{} open_write({})", self.prefix, path.display());
139        self.inner.open_write(path)
140    }
141}
142
143impl<B: FsDir> FsDir for LoggingFs<B> {
144    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
145        println!("{} read_dir({})", self.prefix, path.display());
146        self.inner.read_dir(path)
147    }
148
149    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
150        println!("{} create_dir({})", self.prefix, path.display());
151        self.inner.create_dir(path)
152    }
153
154    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
155        println!("{} create_dir_all({})", self.prefix, path.display());
156        self.inner.create_dir_all(path)
157    }
158
159    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
160        println!("{} remove_dir({})", self.prefix, path.display());
161        self.inner.remove_dir(path)
162    }
163
164    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
165        println!("{} remove_dir_all({})", self.prefix, path.display());
166        self.inner.remove_dir_all(path)
167    }
168}
169
170// =============================================================================
171// Layer 2: Metrics Layer
172// =============================================================================
173
174/// A layer that tracks operation counts.
175struct MetricsLayer;
176
177/// The metrics middleware with atomic counters.
178struct MetricsFs<B> {
179    inner: B,
180    reads: AtomicUsize,
181    writes: AtomicUsize,
182    deletes: AtomicUsize,
183}
184
185impl<B> MetricsFs<B> {
186    fn stats(&self) -> (usize, usize, usize) {
187        (
188            self.reads.load(Ordering::Relaxed),
189            self.writes.load(Ordering::Relaxed),
190            self.deletes.load(Ordering::Relaxed),
191        )
192    }
193}
194
195impl<B: Fs> Layer<B> for MetricsLayer {
196    type Backend = MetricsFs<B>;
197
198    fn layer(self, inner: B) -> Self::Backend {
199        MetricsFs {
200            inner,
201            reads: AtomicUsize::new(0),
202            writes: AtomicUsize::new(0),
203            deletes: AtomicUsize::new(0),
204        }
205    }
206}
207
208impl<B: FsRead> FsRead for MetricsFs<B> {
209    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
210        self.reads.fetch_add(1, Ordering::Relaxed);
211        self.inner.read(path)
212    }
213
214    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
215        self.reads.fetch_add(1, Ordering::Relaxed);
216        self.inner.read_to_string(path)
217    }
218
219    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
220        self.reads.fetch_add(1, Ordering::Relaxed);
221        self.inner.read_range(path, offset, len)
222    }
223
224    fn exists(&self, path: &Path) -> Result<bool, FsError> {
225        self.inner.exists(path)
226    }
227
228    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
229        self.inner.metadata(path)
230    }
231
232    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
233        self.reads.fetch_add(1, Ordering::Relaxed);
234        self.inner.open_read(path)
235    }
236}
237
238impl<B: FsWrite> FsWrite for MetricsFs<B> {
239    fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
240        self.writes.fetch_add(1, Ordering::Relaxed);
241        self.inner.write(path, data)
242    }
243
244    fn append(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
245        self.writes.fetch_add(1, Ordering::Relaxed);
246        self.inner.append(path, data)
247    }
248
249    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
250        self.deletes.fetch_add(1, Ordering::Relaxed);
251        self.inner.remove_file(path)
252    }
253
254    fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
255        self.inner.rename(from, to)
256    }
257
258    fn copy(&self, from: &Path, to: &Path) -> Result<(), FsError> {
259        self.writes.fetch_add(1, Ordering::Relaxed);
260        self.inner.copy(from, to)
261    }
262
263    fn truncate(&self, path: &Path, size: u64) -> Result<(), FsError> {
264        self.writes.fetch_add(1, Ordering::Relaxed);
265        self.inner.truncate(path, size)
266    }
267
268    fn open_write(&self, path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
269        self.writes.fetch_add(1, Ordering::Relaxed);
270        self.inner.open_write(path)
271    }
272}
273
274impl<B: FsDir> FsDir for MetricsFs<B> {
275    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
276        self.inner.read_dir(path)
277    }
278
279    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
280        self.inner.create_dir(path)
281    }
282
283    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
284        self.inner.create_dir_all(path)
285    }
286
287    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
288        self.deletes.fetch_add(1, Ordering::Relaxed);
289        self.inner.remove_dir(path)
290    }
291
292    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
293        self.deletes.fetch_add(1, Ordering::Relaxed);
294        self.inner.remove_dir_all(path)
295    }
296}
297
298// =============================================================================
299// Layer 3: Read-Only Layer
300// =============================================================================
301
302/// A layer that makes the filesystem read-only.
303struct ReadOnlyLayer;
304
305/// The read-only middleware that rejects all writes.
306struct ReadOnlyFs<B> {
307    inner: B,
308}
309
310impl<B: Fs> Layer<B> for ReadOnlyLayer {
311    type Backend = ReadOnlyFs<B>;
312
313    fn layer(self, inner: B) -> Self::Backend {
314        ReadOnlyFs { inner }
315    }
316}
317
318impl<B: FsRead> FsRead for ReadOnlyFs<B> {
319    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
320        self.inner.read(path)
321    }
322
323    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
324        self.inner.read_to_string(path)
325    }
326
327    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
328        self.inner.read_range(path, offset, len)
329    }
330
331    fn exists(&self, path: &Path) -> Result<bool, FsError> {
332        self.inner.exists(path)
333    }
334
335    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
336        self.inner.metadata(path)
337    }
338
339    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
340        self.inner.open_read(path)
341    }
342}
343
344impl<B: FsRead> FsWrite for ReadOnlyFs<B> {
345    fn write(&self, path: &Path, _data: &[u8]) -> Result<(), FsError> {
346        Err(FsError::PermissionDenied {
347            path: path.to_path_buf(),
348            operation: "write (read-only filesystem)",
349        })
350    }
351
352    fn append(&self, path: &Path, _data: &[u8]) -> Result<(), FsError> {
353        Err(FsError::PermissionDenied {
354            path: path.to_path_buf(),
355            operation: "append (read-only filesystem)",
356        })
357    }
358
359    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
360        Err(FsError::PermissionDenied {
361            path: path.to_path_buf(),
362            operation: "remove_file (read-only filesystem)",
363        })
364    }
365
366    fn rename(&self, from: &Path, _to: &Path) -> Result<(), FsError> {
367        Err(FsError::PermissionDenied {
368            path: from.to_path_buf(),
369            operation: "rename (read-only filesystem)",
370        })
371    }
372
373    fn copy(&self, from: &Path, _to: &Path) -> Result<(), FsError> {
374        Err(FsError::PermissionDenied {
375            path: from.to_path_buf(),
376            operation: "copy (read-only filesystem)",
377        })
378    }
379
380    fn truncate(&self, path: &Path, _size: u64) -> Result<(), FsError> {
381        Err(FsError::PermissionDenied {
382            path: path.to_path_buf(),
383            operation: "truncate (read-only filesystem)",
384        })
385    }
386
387    fn open_write(&self, path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
388        Err(FsError::PermissionDenied {
389            path: path.to_path_buf(),
390            operation: "open_write (read-only filesystem)",
391        })
392    }
393}
394
395impl<B: FsDir> FsDir for ReadOnlyFs<B> {
396    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
397        self.inner.read_dir(path)
398    }
399
400    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
401        Err(FsError::PermissionDenied {
402            path: path.to_path_buf(),
403            operation: "create_dir (read-only filesystem)",
404        })
405    }
406
407    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
408        Err(FsError::PermissionDenied {
409            path: path.to_path_buf(),
410            operation: "create_dir_all (read-only filesystem)",
411        })
412    }
413
414    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
415        Err(FsError::PermissionDenied {
416            path: path.to_path_buf(),
417            operation: "remove_dir (read-only filesystem)",
418        })
419    }
420
421    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
422        Err(FsError::PermissionDenied {
423            path: path.to_path_buf(),
424            operation: "remove_dir_all (read-only filesystem)",
425        })
426    }
427}
428
429// =============================================================================
430// Simple In-Memory Backend (same as other examples)
431// =============================================================================
432
433struct MemoryFs {
434    files: RwLock<HashMap<PathBuf, Vec<u8>>>,
435    dirs: RwLock<std::collections::HashSet<PathBuf>>,
436}
437
438impl MemoryFs {
439    fn new() -> Self {
440        let fs = Self {
441            files: RwLock::new(HashMap::new()),
442            dirs: RwLock::new(std::collections::HashSet::new()),
443        };
444        fs.dirs.write().unwrap().insert(PathBuf::from("/"));
445        fs
446    }
447}
448
449impl FsRead for MemoryFs {
450    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
451        self.files
452            .read()
453            .unwrap()
454            .get(path)
455            .cloned()
456            .ok_or_else(|| FsError::NotFound {
457                path: path.to_path_buf(),
458            })
459    }
460    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
461        String::from_utf8(self.read(path)?).map_err(|_| FsError::InvalidData {
462            path: path.to_path_buf(),
463            details: "not UTF-8".into(),
464        })
465    }
466    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
467        let data = self.read(path)?;
468        let start = offset as usize;
469        Ok(if start >= data.len() {
470            vec![]
471        } else {
472            data[start..(start + len).min(data.len())].to_vec()
473        })
474    }
475    fn exists(&self, path: &Path) -> Result<bool, FsError> {
476        Ok(self.files.read().unwrap().contains_key(path)
477            || self.dirs.read().unwrap().contains(path))
478    }
479    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
480        if self.dirs.read().unwrap().contains(path) {
481            Ok(Metadata {
482                file_type: FileType::Directory,
483                size: 0,
484                permissions: Permissions::default_dir(),
485                created: SystemTime::UNIX_EPOCH,
486                modified: SystemTime::UNIX_EPOCH,
487                accessed: SystemTime::UNIX_EPOCH,
488                inode: 0,
489                nlink: 1,
490            })
491        } else if let Some(data) = self.files.read().unwrap().get(path) {
492            Ok(Metadata {
493                file_type: FileType::File,
494                size: data.len() as u64,
495                permissions: Permissions::default_file(),
496                created: SystemTime::UNIX_EPOCH,
497                modified: SystemTime::UNIX_EPOCH,
498                accessed: SystemTime::UNIX_EPOCH,
499                inode: 0,
500                nlink: 1,
501            })
502        } else {
503            Err(FsError::NotFound {
504                path: path.to_path_buf(),
505            })
506        }
507    }
508    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
509        Ok(Box::new(std::io::Cursor::new(self.read(path)?)))
510    }
511}
512
513impl FsWrite for MemoryFs {
514    fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
515        self.files
516            .write()
517            .unwrap()
518            .insert(path.to_path_buf(), data.to_vec());
519        Ok(())
520    }
521    fn append(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
522        self.files
523            .write()
524            .unwrap()
525            .entry(path.to_path_buf())
526            .or_default()
527            .extend_from_slice(data);
528        Ok(())
529    }
530    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
531        self.files
532            .write()
533            .unwrap()
534            .remove(path)
535            .map(|_| ())
536            .ok_or_else(|| FsError::NotFound {
537                path: path.to_path_buf(),
538            })
539    }
540    fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
541        let data = self
542            .files
543            .write()
544            .unwrap()
545            .remove(from)
546            .ok_or_else(|| FsError::NotFound {
547                path: from.to_path_buf(),
548            })?;
549        self.files.write().unwrap().insert(to.to_path_buf(), data);
550        Ok(())
551    }
552    fn copy(&self, from: &Path, to: &Path) -> Result<(), FsError> {
553        let data = self.read(from)?;
554        self.write(to, &data)
555    }
556    fn truncate(&self, path: &Path, size: u64) -> Result<(), FsError> {
557        self.files
558            .write()
559            .unwrap()
560            .get_mut(path)
561            .ok_or_else(|| FsError::NotFound {
562                path: path.to_path_buf(),
563            })?
564            .resize(size as usize, 0);
565        Ok(())
566    }
567    fn open_write(&self, _path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
568        Ok(Box::new(std::io::Cursor::new(Vec::new())))
569    }
570}
571
572impl FsDir for MemoryFs {
573    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
574        if !self.dirs.read().unwrap().contains(path) {
575            return Err(FsError::NotFound {
576                path: path.to_path_buf(),
577            });
578        }
579        let mut entries = Vec::new();
580        for (fp, data) in self.files.read().unwrap().iter() {
581            if fp.parent() == Some(path) {
582                if let Some(name) = fp.file_name() {
583                    entries.push(Ok(DirEntry {
584                        name: name.to_string_lossy().into(),
585                        path: fp.clone(),
586                        file_type: FileType::File,
587                        size: data.len() as u64,
588                        inode: 0,
589                    }));
590                }
591            }
592        }
593        for dp in self.dirs.read().unwrap().iter() {
594            if dp.parent() == Some(path) && dp != path {
595                if let Some(name) = dp.file_name() {
596                    entries.push(Ok(DirEntry {
597                        name: name.to_string_lossy().into(),
598                        path: dp.clone(),
599                        file_type: FileType::Directory,
600                        size: 0,
601                        inode: 0,
602                    }));
603                }
604            }
605        }
606        Ok(ReadDirIter::from_vec(entries))
607    }
608    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
609        if self.dirs.read().unwrap().contains(path) {
610            return Err(FsError::AlreadyExists {
611                path: path.to_path_buf(),
612                operation: "create_dir",
613            });
614        }
615        self.dirs.write().unwrap().insert(path.to_path_buf());
616        Ok(())
617    }
618    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
619        let mut current = PathBuf::new();
620        for c in path.components() {
621            current.push(c);
622            self.dirs.write().unwrap().insert(current.clone());
623        }
624        Ok(())
625    }
626    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
627        if !self.dirs.write().unwrap().remove(path) {
628            return Err(FsError::NotFound {
629                path: path.to_path_buf(),
630            });
631        }
632        Ok(())
633    }
634    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
635        self.dirs.write().unwrap().remove(path);
636        self.files
637            .write()
638            .unwrap()
639            .retain(|p, _| !p.starts_with(path));
640        Ok(())
641    }
642}
643
644// =============================================================================
645// Main: Demonstrate layer composition
646// =============================================================================
647
648fn main() {
649    println!("=== Layer Middleware Example ===\n");
650
651    // Create base filesystem
652    let base = MemoryFs::new();
653
654    // Write some initial data
655    base.write(Path::new("/config.txt"), b"initial config")
656        .unwrap();
657    base.create_dir(Path::new("/data")).unwrap();
658    base.write(Path::new("/data/file1.txt"), b"file 1 content")
659        .unwrap();
660
661    // ===========================================
662    // Example 1: Logging layer
663    // ===========================================
664    println!("--- Example 1: Logging Layer ---\n");
665
666    // Using LayerExt for fluent API
667    let logged_fs = base.layer(LoggingLayer);
668
669    println!("Reading a file through logging layer:");
670    let _ = logged_fs.read(Path::new("/config.txt"));
671    println!();
672
673    // Get base back (we'll use a new one for other examples)
674    let base = MemoryFs::new();
675    base.write(Path::new("/config.txt"), b"initial config")
676        .unwrap();
677    base.create_dir(Path::new("/data")).unwrap();
678    base.write(Path::new("/data/file1.txt"), b"file 1 content")
679        .unwrap();
680    base.write(Path::new("/data/file2.txt"), b"file 2 content")
681        .unwrap();
682
683    // ===========================================
684    // Example 2: Metrics layer
685    // ===========================================
686    println!("--- Example 2: Metrics Layer ---\n");
687
688    let metrics_layer = MetricsLayer;
689    let metrics_fs = metrics_layer.layer(MemoryFs::new());
690
691    // Setup
692    metrics_fs.write(Path::new("/a.txt"), b"a").unwrap();
693    metrics_fs.write(Path::new("/b.txt"), b"b").unwrap();
694    metrics_fs.write(Path::new("/c.txt"), b"c").unwrap();
695    let _ = metrics_fs.read(Path::new("/a.txt"));
696    let _ = metrics_fs.read(Path::new("/b.txt"));
697    metrics_fs.remove_file(Path::new("/c.txt")).unwrap();
698
699    let (reads, writes, deletes) = metrics_fs.stats();
700    println!("Metrics: reads={reads}, writes={writes}, deletes={deletes}\n");
701
702    // ===========================================
703    // Example 3: Read-only layer
704    // ===========================================
705    println!("--- Example 3: Read-Only Layer ---\n");
706
707    let readonly_fs = base.layer(ReadOnlyLayer);
708
709    // Reading works
710    println!("Reading through read-only layer:");
711    match readonly_fs.read(Path::new("/config.txt")) {
712        Ok(data) => println!("  Success: {} bytes", data.len()),
713        Err(e) => println!("  Error: {e}"),
714    }
715
716    // Writing fails
717    println!("Writing through read-only layer:");
718    match readonly_fs.write(Path::new("/new.txt"), b"test") {
719        Ok(()) => println!("  Success (unexpected!)"),
720        Err(e) => println!("  Error: {e}"),
721    }
722    println!();
723
724    // ===========================================
725    // Example 4: Composing multiple layers
726    // ===========================================
727    println!("--- Example 4: Composing Layers ---\n");
728
729    // Create a stack: MemoryFs -> Metrics -> Logging
730    // Operations flow: Logging -> Metrics -> MemoryFs
731    let base = MemoryFs::new();
732    let with_metrics = MetricsLayer.layer(base);
733    let with_logging = LoggingLayer.layer(with_metrics);
734
735    println!("Writing through Logging -> Metrics -> Memory:");
736    with_logging
737        .write(Path::new("/test.txt"), b"hello")
738        .unwrap();
739    println!();
740
741    println!("Reading through Logging -> Metrics -> Memory:");
742    let _ = with_logging.read(Path::new("/test.txt"));
743    println!();
744
745    // Access inner metrics
746    let (reads, writes, _) = with_logging.inner.stats();
747    println!("Inner metrics: reads={reads}, writes={writes}\n");
748
749    // ===========================================
750    // Example 5: Using with trait objects
751    // ===========================================
752    println!("--- Example 5: Layers with Trait Objects ---\n");
753
754    fn use_any_fs(fs: &dyn Fs) {
755        let _ = fs.exists(Path::new("/"));
756        println!("  Used filesystem through trait object");
757    }
758
759    let base = MemoryFs::new();
760    let layered = base.layer(LoggingLayer);
761
762    println!("Passing layered FS as &dyn Fs:");
763    use_any_fs(&layered);
764
765    println!("\n=== Layer examples complete! ===");
766}