anyfs_backend/
layer.rs

1//! # Layer Trait
2//!
3//! Tower-style middleware composition for filesystem backends.
4//!
5//! ## Overview
6//!
7//! The [`Layer`] trait enables composable middleware that wraps backends to add
8//! functionality like caching, encryption, rate limiting, or logging.
9//!
10//! ## How It Works
11//!
12//! ```text
13//! Backend ──▶ Layer::layer() ──▶ Wrapped Backend
14//! ```
15//!
16//! Each middleware provides:
17//! 1. A wrapper struct that implements filesystem traits
18//! 2. A `Layer` implementation that creates the wrapper
19//!
20//! ## Example
21//!
22//! The Layer pattern separates middleware configuration from wrapping:
23//!
24//! ```rust
25//! use anyfs_backend::Layer;
26//!
27//! // Configuration for the layer
28//! struct CacheConfig {
29//!     max_entries: usize,
30//! }
31//!
32//! // The layer holds configuration
33//! struct CacheLayer {
34//!     config: CacheConfig,
35//! }
36//!
37//! // The middleware wraps any backend
38//! struct CacheMiddleware<B> {
39//!     inner: B,
40//!     config: CacheConfig,
41//! }
42//!
43//! // Layer creates the middleware
44//! impl<B> Layer<B> for CacheLayer {
45//!     type Backend = CacheMiddleware<B>;
46//!     
47//!     fn layer(self, backend: B) -> Self::Backend {
48//!         CacheMiddleware {
49//!             inner: backend,
50//!             config: self.config,
51//!         }
52//!     }
53//! }
54//! ```
55//!
56//! ## Fluent Composition
57//!
58//! Use [`LayerExt`] for fluent chaining:
59//!
60//! ```rust
61//! use anyfs_backend::LayerExt;
62//!
63//! // Hypothetical usage (requires concrete backend):
64//! // let backend = MemoryBackend::new()
65//! //     .layer(QuotaLayer::new(limits))
66//! //     .layer(TracingLayer::new());
67//! ```
68
69use crate::Fs;
70
71/// A layer that wraps a backend to add functionality.
72///
73/// Inspired by Tower's `Layer` trait, this enables composable middleware.
74/// Each middleware provides a corresponding `Layer` implementation.
75///
76/// # Type Parameters
77///
78/// - `B`: The backend type being wrapped (must implement [`Fs`])
79///
80/// # Design Notes
81///
82/// - `layer(self, backend)` consumes both the layer and backend
83/// - The resulting `Backend` type must also implement `Fs`
84/// - Middleware needing higher traits (e.g., `FsLink`) can add bounds in their impl
85///
86/// # Example
87///
88/// ```rust
89/// use anyfs_backend::Layer;
90///
91/// struct LoggingMiddleware<B> {
92///     inner: B,
93///     prefix: String,
94/// }
95///
96/// struct LoggingLayer {
97///     prefix: String,
98/// }
99///
100/// impl<B> Layer<B> for LoggingLayer {
101///     type Backend = LoggingMiddleware<B>;
102///     
103///     fn layer(self, backend: B) -> Self::Backend {
104///         LoggingMiddleware {
105///             inner: backend,
106///             prefix: self.prefix,
107///         }
108///     }
109/// }
110/// ```
111pub trait Layer<B> {
112    /// The resulting backend type after applying this layer.
113    ///
114    /// For middleware that preserves filesystem capabilities, this type
115    /// should implement the same traits as the input backend `B`.
116    type Backend;
117
118    /// Wrap the given backend with this layer's functionality.
119    ///
120    /// Consumes both the layer configuration and the backend,
121    /// returning a new wrapped backend.
122    fn layer(self, backend: B) -> Self::Backend;
123}
124
125/// Extension trait for fluent layer composition.
126///
127/// Provides the `.layer()` method on any `Fs` backend for ergonomic chaining.
128///
129/// # Example
130///
131/// ```rust
132/// use anyfs_backend::{Fs, LayerExt, Layer};
133///
134/// // With LayerExt, you can chain layers fluently:
135/// fn compose_backend<B: Fs, L: Layer<B>>(backend: B, layer: L) -> L::Backend {
136///     backend.layer(layer)
137/// }
138/// ```
139pub trait LayerExt: Fs + Sized {
140    /// Apply a layer to this backend.
141    ///
142    /// Returns the wrapped backend with the layer's functionality added.
143    ///
144    /// # Example
145    ///
146    /// ```rust
147    /// use anyfs_backend::{Fs, LayerExt, Layer};
148    ///
149    /// fn add_middleware<B, L>(backend: B, layer: L) -> L::Backend
150    /// where
151    ///     B: Fs,
152    ///     L: Layer<B>,
153    /// {
154    ///     backend.layer(layer)
155    /// }
156    /// ```
157    fn layer<L: Layer<Self>>(self, layer: L) -> L::Backend {
158        layer.layer(self)
159    }
160}
161
162// Blanket implementation - any Fs backend gets LayerExt for free
163impl<B: Fs> LayerExt for B {}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    /// Verify Layer trait is object-safe (can be used as trait object)
170    /// Note: Layer is NOT object-safe due to generic parameter and Sized bound
171    /// This is intentional - layers are compile-time composition
172
173    #[test]
174    fn layer_ext_is_auto_implemented() {
175        // LayerExt is blanket-implemented for all Fs types
176        fn _check<B: Fs + LayerExt>() {}
177    }
178
179    #[test]
180    fn layer_composes_types() {
181        use crate::{FsDir, FsRead, FsWrite, ReadDirIter};
182        use std::path::Path;
183
184        // Mock backend
185        struct MockBackend;
186
187        impl FsRead for MockBackend {
188            fn read(&self, _: &Path) -> Result<Vec<u8>, crate::FsError> {
189                Ok(vec![])
190            }
191            fn read_to_string(&self, _: &Path) -> Result<String, crate::FsError> {
192                Ok(String::new())
193            }
194            fn read_range(&self, _: &Path, _: u64, _: usize) -> Result<Vec<u8>, crate::FsError> {
195                Ok(vec![])
196            }
197            fn exists(&self, _: &Path) -> Result<bool, crate::FsError> {
198                Ok(true)
199            }
200            fn metadata(&self, _: &Path) -> Result<crate::Metadata, crate::FsError> {
201                Ok(crate::Metadata::default())
202            }
203            fn open_read(&self, _: &Path) -> Result<Box<dyn std::io::Read + Send>, crate::FsError> {
204                Ok(Box::new(std::io::empty()))
205            }
206        }
207
208        impl FsWrite for MockBackend {
209            fn write(&self, _: &Path, _: &[u8]) -> Result<(), crate::FsError> {
210                Ok(())
211            }
212            fn append(&self, _: &Path, _: &[u8]) -> Result<(), crate::FsError> {
213                Ok(())
214            }
215            fn truncate(&self, _: &Path, _: u64) -> Result<(), crate::FsError> {
216                Ok(())
217            }
218            fn remove_file(&self, _: &Path) -> Result<(), crate::FsError> {
219                Ok(())
220            }
221            fn rename(&self, _: &Path, _: &Path) -> Result<(), crate::FsError> {
222                Ok(())
223            }
224            fn copy(&self, _: &Path, _: &Path) -> Result<(), crate::FsError> {
225                Ok(())
226            }
227            fn open_write(
228                &self,
229                _: &Path,
230            ) -> Result<Box<dyn std::io::Write + Send>, crate::FsError> {
231                Ok(Box::new(std::io::sink()))
232            }
233        }
234
235        impl FsDir for MockBackend {
236            fn read_dir(&self, _: &Path) -> Result<ReadDirIter, crate::FsError> {
237                Ok(ReadDirIter::from_vec(vec![]))
238            }
239            fn create_dir(&self, _: &Path) -> Result<(), crate::FsError> {
240                Ok(())
241            }
242            fn create_dir_all(&self, _: &Path) -> Result<(), crate::FsError> {
243                Ok(())
244            }
245            fn remove_dir(&self, _: &Path) -> Result<(), crate::FsError> {
246                Ok(())
247            }
248            fn remove_dir_all(&self, _: &Path) -> Result<(), crate::FsError> {
249                Ok(())
250            }
251        }
252
253        // Mock wrapper
254        struct WrappedBackend<B> {
255            _inner: B,
256        }
257
258        impl<B: FsRead> FsRead for WrappedBackend<B> {
259            fn read(&self, _: &Path) -> Result<Vec<u8>, crate::FsError> {
260                Ok(vec![])
261            }
262            fn read_to_string(&self, _: &Path) -> Result<String, crate::FsError> {
263                Ok(String::new())
264            }
265            fn read_range(&self, _: &Path, _: u64, _: usize) -> Result<Vec<u8>, crate::FsError> {
266                Ok(vec![])
267            }
268            fn exists(&self, _: &Path) -> Result<bool, crate::FsError> {
269                Ok(true)
270            }
271            fn metadata(&self, _: &Path) -> Result<crate::Metadata, crate::FsError> {
272                Ok(crate::Metadata::default())
273            }
274            fn open_read(&self, _: &Path) -> Result<Box<dyn std::io::Read + Send>, crate::FsError> {
275                Ok(Box::new(std::io::empty()))
276            }
277        }
278
279        impl<B: FsWrite> FsWrite for WrappedBackend<B> {
280            fn write(&self, _: &Path, _: &[u8]) -> Result<(), crate::FsError> {
281                Ok(())
282            }
283            fn append(&self, _: &Path, _: &[u8]) -> Result<(), crate::FsError> {
284                Ok(())
285            }
286            fn truncate(&self, _: &Path, _: u64) -> Result<(), crate::FsError> {
287                Ok(())
288            }
289            fn remove_file(&self, _: &Path) -> Result<(), crate::FsError> {
290                Ok(())
291            }
292            fn rename(&self, _: &Path, _: &Path) -> Result<(), crate::FsError> {
293                Ok(())
294            }
295            fn copy(&self, _: &Path, _: &Path) -> Result<(), crate::FsError> {
296                Ok(())
297            }
298            fn open_write(
299                &self,
300                _: &Path,
301            ) -> Result<Box<dyn std::io::Write + Send>, crate::FsError> {
302                Ok(Box::new(std::io::sink()))
303            }
304        }
305
306        impl<B: FsDir> FsDir for WrappedBackend<B> {
307            fn read_dir(&self, _: &Path) -> Result<ReadDirIter, crate::FsError> {
308                Ok(ReadDirIter::from_vec(vec![]))
309            }
310            fn create_dir(&self, _: &Path) -> Result<(), crate::FsError> {
311                Ok(())
312            }
313            fn create_dir_all(&self, _: &Path) -> Result<(), crate::FsError> {
314                Ok(())
315            }
316            fn remove_dir(&self, _: &Path) -> Result<(), crate::FsError> {
317                Ok(())
318            }
319            fn remove_dir_all(&self, _: &Path) -> Result<(), crate::FsError> {
320                Ok(())
321            }
322        }
323
324        // Mock layer
325        struct MockLayer;
326
327        impl<B: Fs> Layer<B> for MockLayer {
328            type Backend = WrappedBackend<B>;
329
330            fn layer(self, backend: B) -> Self::Backend {
331                WrappedBackend { _inner: backend }
332            }
333        }
334
335        // Test composition
336        let backend = MockBackend;
337        let wrapped = backend.layer(MockLayer);
338
339        // Verify wrapped backend implements Fs
340        fn _takes_fs<T: Fs>(_: &T) {}
341        _takes_fs(&wrapped);
342    }
343}