1use serde::Deserialize;
2
3#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq)]
5#[serde(rename_all = "lowercase")]
6pub enum StorageBackend {
7 #[default]
9 Local,
10 S3,
12}
13
14#[derive(Debug, Clone, Deserialize)]
19#[serde(default)]
20pub struct UploadConfig {
21 pub backend: StorageBackend,
23 pub path: String,
25 pub max_file_size: Option<String>,
28 #[cfg(feature = "opendal")]
30 pub s3: S3Config,
31}
32
33impl Default for UploadConfig {
34 fn default() -> Self {
35 Self {
36 backend: StorageBackend::default(),
37 path: "./uploads".to_string(),
38 max_file_size: Some("10mb".to_string()),
39 #[cfg(feature = "opendal")]
40 s3: S3Config::default(),
41 }
42 }
43}
44
45impl UploadConfig {
46 pub fn validate(&self) {
52 if let Some(ref size_str) = self.max_file_size {
53 let bytes = modo::config::parse_size(size_str).unwrap_or_else(|e| {
54 panic!("invalid max_file_size \"{size_str}\": {e}");
55 });
56 assert!(
57 bytes > 0,
58 "max_file_size must be greater than 0, got \"{size_str}\""
59 );
60 }
61 }
62}
63
64#[cfg(feature = "opendal")]
66#[derive(Debug, Clone, Default, Deserialize)]
67#[serde(default)]
68pub struct S3Config {
69 pub bucket: String,
71 pub region: String,
73 pub endpoint: String,
75 pub access_key_id: String,
77 pub secret_access_key: String,
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn default_config_is_valid() {
87 let config = UploadConfig::default();
89 config.validate();
90 }
91
92 #[test]
93 #[should_panic(expected = "max_file_size")]
94 fn rejects_zero_max_file_size() {
95 let config = UploadConfig {
96 max_file_size: Some("0".to_string()),
97 ..Default::default()
98 };
99 config.validate();
100 }
101
102 #[test]
103 #[should_panic(expected = "max_file_size")]
104 fn rejects_zero_bytes_max_file_size() {
105 let config = UploadConfig {
106 max_file_size: Some("0mb".to_string()),
107 ..Default::default()
108 };
109 config.validate();
110 }
111
112 #[test]
113 #[should_panic(expected = "max_file_size")]
114 fn rejects_unparseable_max_file_size() {
115 let config = UploadConfig {
116 max_file_size: Some("not-a-size".to_string()),
117 ..Default::default()
118 };
119 config.validate();
120 }
121
122 #[test]
123 fn none_max_file_size_is_valid() {
124 let config = UploadConfig {
125 max_file_size: None,
126 ..Default::default()
127 };
128 config.validate();
129 }
130}