ferro_storage/
config.rs

1//! Configuration for the storage system.
2
3use crate::facade::DiskConfig;
4#[cfg(feature = "s3")]
5use crate::facade::DiskDriver;
6use std::collections::HashMap;
7use std::env;
8
9/// Configuration for the storage system.
10#[derive(Debug, Clone)]
11pub struct StorageConfig {
12    /// Default disk name.
13    pub default: String,
14    /// Disk configurations.
15    pub disks: HashMap<String, DiskConfig>,
16}
17
18impl Default for StorageConfig {
19    fn default() -> Self {
20        let mut disks = HashMap::new();
21        disks.insert("local".to_string(), DiskConfig::local("./storage"));
22
23        Self {
24            default: "local".to_string(),
25            disks,
26        }
27    }
28}
29
30impl StorageConfig {
31    /// Create a new storage config with a default disk.
32    pub fn new(default: impl Into<String>) -> Self {
33        Self {
34            default: default.into(),
35            disks: HashMap::new(),
36        }
37    }
38
39    /// Create configuration from environment variables.
40    ///
41    /// Reads the following environment variables:
42    /// - `FILESYSTEM_DISK`: Default disk name (default: "local")
43    /// - `FILESYSTEM_LOCAL_ROOT`: Root path for local disk (default: "./storage")
44    /// - `FILESYSTEM_LOCAL_URL`: Public URL for local files
45    /// - `FILESYSTEM_PUBLIC_ROOT`: Root path for public disk (default: "./storage/public")
46    /// - `FILESYSTEM_PUBLIC_URL`: Public URL for public files (default: "/storage")
47    ///
48    /// With `s3` feature:
49    /// - `AWS_ACCESS_KEY_ID`: S3 access key
50    /// - `AWS_SECRET_ACCESS_KEY`: S3 secret key
51    /// - `AWS_DEFAULT_REGION`: S3 region (default: "us-east-1")
52    /// - `AWS_BUCKET`: S3 bucket name
53    /// - `AWS_URL`: S3 URL base
54    ///
55    /// # Example
56    ///
57    /// ```rust,ignore
58    /// use ferro_storage::{StorageConfig, Storage};
59    ///
60    /// let config = StorageConfig::from_env();
61    /// let storage = Storage::with_storage_config(config);
62    /// ```
63    pub fn from_env() -> Self {
64        let default = env::var("FILESYSTEM_DISK").unwrap_or_else(|_| "local".to_string());
65        let mut disks = HashMap::new();
66
67        // Local disk
68        let local_root =
69            env::var("FILESYSTEM_LOCAL_ROOT").unwrap_or_else(|_| "./storage".to_string());
70        let mut local_config = DiskConfig::local(&local_root);
71        if let Ok(url) = env::var("FILESYSTEM_LOCAL_URL") {
72            local_config = local_config.with_url(url);
73        }
74        disks.insert("local".to_string(), local_config);
75
76        // Public disk (for publicly accessible files)
77        let public_root =
78            env::var("FILESYSTEM_PUBLIC_ROOT").unwrap_or_else(|_| "./storage/public".to_string());
79        let public_url =
80            env::var("FILESYSTEM_PUBLIC_URL").unwrap_or_else(|_| "/storage".to_string());
81        let public_config = DiskConfig::local(&public_root).with_url(public_url);
82        disks.insert("public".to_string(), public_config);
83
84        // S3 disk (if configured)
85        #[cfg(feature = "s3")]
86        if let Ok(bucket) = env::var("AWS_BUCKET") {
87            let region = env::var("AWS_DEFAULT_REGION").unwrap_or_else(|_| "us-east-1".to_string());
88            let mut s3_config = DiskConfig {
89                driver: DiskDriver::S3,
90                root: None,
91                url: None,
92                bucket: Some(bucket),
93                region: Some(region),
94            };
95            if let Ok(url) = env::var("AWS_URL") {
96                s3_config.url = Some(url);
97            }
98            disks.insert("s3".to_string(), s3_config);
99        }
100
101        Self { default, disks }
102    }
103
104    /// Add a disk configuration.
105    pub fn disk(mut self, name: impl Into<String>, config: DiskConfig) -> Self {
106        self.disks.insert(name.into(), config);
107        self
108    }
109
110    /// Set the default disk.
111    pub fn default_disk(mut self, name: impl Into<String>) -> Self {
112        self.default = name.into();
113        self
114    }
115
116    /// Get the default disk name.
117    pub fn get_default(&self) -> &str {
118        &self.default
119    }
120
121    /// Get a disk configuration by name.
122    pub fn get_disk(&self, name: &str) -> Option<&DiskConfig> {
123        self.disks.get(name)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_storage_config_defaults() {
133        let config = StorageConfig::default();
134        assert_eq!(config.default, "local");
135        assert!(config.disks.contains_key("local"));
136    }
137
138    #[test]
139    fn test_storage_config_builder() {
140        let config = StorageConfig::new("s3")
141            .disk("local", DiskConfig::local("./storage"))
142            .disk("public", DiskConfig::local("./public").with_url("/files"));
143
144        assert_eq!(config.default, "s3");
145        assert!(config.disks.contains_key("local"));
146        assert!(config.disks.contains_key("public"));
147    }
148
149    #[test]
150    fn test_storage_config_from_env() {
151        // Test with default env (no env vars set)
152        let config = StorageConfig::from_env();
153        assert_eq!(config.default, "local");
154        assert!(config.disks.contains_key("local"));
155        assert!(config.disks.contains_key("public"));
156    }
157}