Skip to content

Commit 4ba2cd7

Browse files
committed
Add a section for typetag serialization
1 parent 0759335 commit 4ba2cd7

File tree

1 file changed

+77
-13
lines changed

1 file changed

+77
-13
lines changed

docs/rfcs/0002_storage_trait.md

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,74 @@ Features:
198198
- Case-insensitive scheme lookup
199199
- Thread-safe and cloneable
200200

201+
### Serialization with typetag
202+
203+
To enable serialization of `Storage` and `StorageFactory` trait objects, we use the `typetag` crate. This allows dynamic trait objects to be serialized and deserialized, which is essential for:
204+
205+
- Persisting storage configurations
206+
- Sending storage instances across process boundaries
207+
- Supporting custom storage implementations in distributed systems
208+
209+
The traits are annotated with `#[typetag::serde]`:
210+
211+
```rust
212+
#[async_trait]
213+
#[typetag::serde(tag = "type")]
214+
pub trait Storage: Debug + Send + Sync {
215+
// ... trait methods
216+
}
217+
218+
#[typetag::serde(tag = "type")]
219+
pub trait StorageFactory: Debug + Send + Sync {
220+
fn build(
221+
&self,
222+
props: HashMap<String, String>,
223+
extensions: Extensions,
224+
) -> Result<Arc<dyn Storage>>;
225+
}
226+
```
227+
228+
Each implementation must also use the `#[typetag::serde]` attribute:
229+
230+
```rust
231+
#[derive(Debug, Clone, Serialize, Deserialize)]
232+
pub struct OpenDALS3Storage {
233+
config: Arc<S3Config>,
234+
}
235+
236+
#[async_trait]
237+
#[typetag::serde]
238+
impl Storage for OpenDALS3Storage {
239+
// ... implementation
240+
}
241+
242+
#[derive(Debug, Serialize, Deserialize)]
243+
pub struct OpenDALS3StorageFactory;
244+
245+
#[typetag::serde]
246+
impl StorageFactory for OpenDALS3StorageFactory {
247+
// ... implementation
248+
}
249+
```
250+
251+
Benefits:
252+
253+
- **Type-safe serialization** - Each implementation is tagged with its type name
254+
- **Extensibility** - Custom implementations can be serialized without modifying core code
255+
- **Cross-language support** - Serialized format is JSON-compatible
256+
- **Replaces FileIOBuilder/Extensions** - Serializable storage implementations eliminate the need for separate builder patterns
257+
258+
Example usage:
259+
260+
```rust
261+
// Serialize a storage instance
262+
let storage: Arc<dyn Storage> = Arc::new(OpenDALS3Storage::new(config));
263+
let json = serde_json::to_string(&storage)?;
264+
265+
// Deserialize back to trait object
266+
let storage: Arc<dyn Storage> = serde_json::from_str(&json)?;
267+
```
268+
201269
## Example Usage
202270

203271
```rust
@@ -409,19 +477,15 @@ This question depends on the answer to Question 1:
409477

410478
**Option A: Enum-Based Errors**
411479
```rust
412-
#[derive(Debug, thiserror::Error)]
413-
pub enum StorageError {
414-
#[error("File not found: {path}")]
415-
NotFound { path: String },
416-
417-
#[error("Permission denied: {path}")]
418-
PermissionDenied { path: String },
419-
420-
#[error("IO error: {0}")]
421-
Io(#[from] std::io::Error),
422-
423-
#[error("Backend error: {0}")]
424-
Backend(Box<dyn std::error::Error + Send + Sync>),
480+
pub enum IoErrorKind {
481+
FileNotFound,
482+
CredentialExpired,
483+
}
484+
485+
pub enum ErrorKind {
486+
// Existing variants
487+
...
488+
Io(IoErrorKind)
425489
}
426490
```
427491
- Pros: Type-safe, pattern matching, clear error categories

0 commit comments

Comments
 (0)