1use crate::facade::DiskConfig;
4#[cfg(feature = "s3")]
5use crate::facade::DiskDriver;
6use std::collections::HashMap;
7use std::env;
8
9#[derive(Debug, Clone)]
11pub struct StorageConfig {
12 pub default: String,
14 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 pub fn new(default: impl Into<String>) -> Self {
33 Self {
34 default: default.into(),
35 disks: HashMap::new(),
36 }
37 }
38
39 pub fn from_env() -> Self {
65 let default = env::var("FILESYSTEM_DISK").unwrap_or_else(|_| "local".to_string());
66 let mut disks = HashMap::new();
67
68 let local_root =
70 env::var("FILESYSTEM_LOCAL_ROOT").unwrap_or_else(|_| "./storage".to_string());
71 let mut local_config = DiskConfig::local(&local_root);
72 if let Ok(url) = env::var("FILESYSTEM_LOCAL_URL") {
73 local_config = local_config.with_url(url);
74 }
75 disks.insert("local".to_string(), local_config);
76
77 let public_root =
79 env::var("FILESYSTEM_PUBLIC_ROOT").unwrap_or_else(|_| "./storage/public".to_string());
80 let public_url =
81 env::var("FILESYSTEM_PUBLIC_URL").unwrap_or_else(|_| "/storage".to_string());
82 let public_config = DiskConfig::local(&public_root).with_url(public_url);
83 disks.insert("public".to_string(), public_config);
84
85 #[cfg(feature = "s3")]
87 if let Ok(bucket) = env::var("AWS_BUCKET") {
88 let region = env::var("AWS_DEFAULT_REGION").unwrap_or_else(|_| "us-east-1".to_string());
89 let mut s3_config = DiskConfig {
90 driver: DiskDriver::S3,
91 root: None,
92 url: None,
93 bucket: Some(bucket.clone()),
94 region: Some(region),
95 };
96 let public_url = if let Ok(explicit) = env::var("AWS_PUBLIC_URL") {
102 Some(explicit)
103 } else if let Ok(api_url) = env::var("AWS_URL") {
104 let host = api_url
105 .trim_start_matches("https://")
106 .trim_start_matches("http://");
107 let scheme = if api_url.starts_with("https://") {
108 "https"
109 } else {
110 "http"
111 };
112 Some(format!("{scheme}://{bucket}.{host}"))
113 } else {
114 None
115 };
116 s3_config.url = public_url;
117 disks.insert("s3".to_string(), s3_config);
118 }
119
120 Self { default, disks }
121 }
122
123 pub fn disk(mut self, name: impl Into<String>, config: DiskConfig) -> Self {
125 self.disks.insert(name.into(), config);
126 self
127 }
128
129 pub fn default_disk(mut self, name: impl Into<String>) -> Self {
131 self.default = name.into();
132 self
133 }
134
135 pub fn get_default(&self) -> &str {
137 &self.default
138 }
139
140 pub fn get_disk(&self, name: &str) -> Option<&DiskConfig> {
142 self.disks.get(name)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_storage_config_defaults() {
152 let config = StorageConfig::default();
153 assert_eq!(config.default, "local");
154 assert!(config.disks.contains_key("local"));
155 }
156
157 #[test]
158 fn test_storage_config_builder() {
159 let config = StorageConfig::new("s3")
160 .disk("local", DiskConfig::local("./storage"))
161 .disk("public", DiskConfig::local("./public").with_url("/files"));
162
163 assert_eq!(config.default, "s3");
164 assert!(config.disks.contains_key("local"));
165 assert!(config.disks.contains_key("public"));
166 }
167
168 #[test]
169 fn test_storage_config_from_env() {
170 let config = StorageConfig::from_env();
172 assert_eq!(config.default, "local");
173 assert!(config.disks.contains_key("local"));
174 assert!(config.disks.contains_key("public"));
175 }
176}