ferro_storage/lib.rs
1//! # Ferro Storage
2//!
3//! File storage abstraction for the Ferro framework.
4//!
5//! Provides a unified API for working with different storage backends:
6//! - Local filesystem
7//! - In-memory (for testing)
8//! - Amazon S3 (with `s3` feature)
9//!
10//! ## Example
11//!
12//! ```rust,ignore
13//! use ferro_storage::{Storage, DiskConfig};
14//!
15//! // Create storage with configuration
16//! let storage = Storage::with_config(
17//! "local",
18//! vec![
19//! ("local", DiskConfig::local("storage/app")),
20//! ("public", DiskConfig::local("storage/public").with_url("/storage")),
21//! ],
22//! );
23//!
24//! // Store a file
25//! storage.put("documents/report.pdf", file_contents).await?;
26//!
27//! // Get a file
28//! let contents = storage.get("documents/report.pdf").await?;
29//!
30//! // Get URL
31//! let url = storage.disk("public")?.url("images/logo.png").await?;
32//! ```
33//!
34//! ## Multiple Disks
35//!
36//! You can configure multiple disks and switch between them:
37//!
38//! ```rust,ignore
39//! use ferro_storage::Storage;
40//!
41//! // Use specific disk
42//! let disk = storage.disk("s3")?;
43//! disk.put("backups/data.json", data).await?;
44//!
45//! // Use default disk
46//! storage.put("temp/cache.txt", cache_data).await?;
47//! ```
48
49pub mod cdn;
50mod config;
51mod drivers;
52mod error;
53mod facade;
54mod storage;
55
56#[cfg(feature = "s3")]
57pub use drivers::S3Driver;
58pub use drivers::{LocalDriver, MemoryDriver};
59
60pub use cdn::{DoSpacesCdn, DoSpacesCdnConfig, PurgeApi};
61
62#[cfg(feature = "cdn-bunny")]
63pub use cdn::{BunnyCdn, BunnyCdnConfig};
64#[cfg(feature = "cdn-cloudflare")]
65pub use cdn::{CloudflareCdn, CloudflareCdnConfig};
66
67pub use config::StorageConfig;
68pub use error::Error;
69pub use facade::{Disk, DiskConfig, DiskDriver, Storage};
70pub use storage::{FileMetadata, PutOptions, StorageDriver, Visibility};
71
72/// Re-export bytes for convenience.
73pub use bytes::Bytes;
74
75/// Re-export async_trait for implementing StorageDriver.
76pub use async_trait::async_trait;
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[tokio::test]
83 async fn test_full_workflow() {
84 let storage = Storage::with_config(
85 "memory",
86 vec![(
87 "memory",
88 DiskConfig::memory().with_url("https://cdn.example.com"),
89 )],
90 );
91
92 // Put file
93 storage.put("test/file.txt", "test content").await.unwrap();
94
95 // Verify exists
96 assert!(storage.exists("test/file.txt").await.unwrap());
97
98 // Get file
99 let contents = storage.get_string("test/file.txt").await.unwrap();
100 assert_eq!(contents, "test content");
101
102 // Get URL
103 let url = storage.url("test/file.txt").await.unwrap();
104 assert_eq!(url, "https://cdn.example.com/test/file.txt");
105
106 // Delete file
107 storage.delete("test/file.txt").await.unwrap();
108 assert!(!storage.exists("test/file.txt").await.unwrap());
109 }
110}