stowr_core/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::PathBuf;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8pub enum CompressionAlgorithm {
9    Gzip,
10    Zstd,
11    Lz4,
12}
13
14impl FromStr for CompressionAlgorithm {
15    type Err = anyhow::Error;
16    
17    fn from_str(s: &str) -> Result<Self> {
18        Self::from_str(s)
19    }
20}
21
22impl FromStr for DeltaAlgorithm {
23    type Err = anyhow::Error;
24    
25    fn from_str(s: &str) -> Result<Self> {
26        Self::from_str(s)
27    }
28}
29
30impl Default for CompressionAlgorithm {
31    fn default() -> Self {
32        CompressionAlgorithm::Gzip
33    }
34}
35
36impl CompressionAlgorithm {
37    pub fn from_str(s: &str) -> Result<Self> {
38        match s.to_lowercase().as_str() {
39            "gzip" => Ok(CompressionAlgorithm::Gzip),
40            "zstd" => Ok(CompressionAlgorithm::Zstd),
41            "lz4" => Ok(CompressionAlgorithm::Lz4),
42            _ => Err(anyhow::anyhow!("Invalid compression algorithm. Valid values: gzip, zstd, lz4")),
43        }
44    }
45
46    pub fn to_string(&self) -> String {
47        match self {
48            CompressionAlgorithm::Gzip => "gzip".to_string(),
49            CompressionAlgorithm::Zstd => "zstd".to_string(),
50            CompressionAlgorithm::Lz4 => "lz4".to_string(),
51        }
52    }
53
54    pub fn file_extension(&self) -> &'static str {
55        match self {
56            CompressionAlgorithm::Gzip => "gz",
57            CompressionAlgorithm::Zstd => "zst",
58            CompressionAlgorithm::Lz4 => "lz4",
59        }
60    }
61
62    pub fn validate_level(&self, level: u32) -> Result<u32> {
63        match self {
64            CompressionAlgorithm::Gzip => {
65                if level > 9 {
66                    Err(anyhow::anyhow!("Gzip compression level must be between 0-9"))
67                } else {
68                    Ok(level)
69                }
70            }
71            CompressionAlgorithm::Zstd => {
72                if level < 1 || level > 22 {
73                    Err(anyhow::anyhow!("Zstd compression level must be between 1-22"))
74                } else {
75                    Ok(level)
76                }
77            }
78            CompressionAlgorithm::Lz4 => {
79                // LZ4 不使用压缩级别,始终返回0
80                Ok(0)
81            }
82        }
83    }
84
85    pub fn default_level(&self) -> u32 {
86        match self {
87            CompressionAlgorithm::Gzip => 6,
88            CompressionAlgorithm::Zstd => 3,
89            CompressionAlgorithm::Lz4 => 0,
90        }
91    }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub enum DeltaAlgorithm {
96    Simple,    // 简单差分
97    XDelta,    // xdelta3 算法
98    BsDiff,    // bsdiff 算法
99}
100
101impl Default for DeltaAlgorithm {
102    fn default() -> Self {
103        DeltaAlgorithm::Simple
104    }
105}
106
107impl DeltaAlgorithm {
108    pub fn from_str(s: &str) -> Result<Self> {
109        match s.to_lowercase().as_str() {
110            "simple" => Ok(DeltaAlgorithm::Simple),
111            "xdelta" => Ok(DeltaAlgorithm::XDelta),
112            "bsdiff" => Ok(DeltaAlgorithm::BsDiff),
113            _ => Err(anyhow::anyhow!("Invalid delta algorithm. Valid values: simple, xdelta, bsdiff")),
114        }
115    }
116
117    pub fn to_string(&self) -> String {
118        match self {
119            DeltaAlgorithm::Simple => "simple".to_string(),
120            DeltaAlgorithm::XDelta => "xdelta".to_string(),
121            DeltaAlgorithm::BsDiff => "bsdiff".to_string(),
122        }
123    }
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct Config {
128    pub storage_path: PathBuf,
129    pub index_mode: IndexMode,
130    #[serde(default = "default_multithread")]
131    pub multithread: usize,
132    #[serde(default = "default_compression_algorithm")]
133    pub compression_algorithm: CompressionAlgorithm,
134    #[serde(default = "default_compression_level")]
135    pub compression_level: u32,
136    #[serde(default = "default_enable_deduplication")]
137    pub enable_deduplication: bool,
138    #[serde(default = "default_enable_delta_compression")]
139    pub enable_delta_compression: bool,
140    #[serde(default = "default_similarity_threshold")]
141    pub similarity_threshold: f32,
142    #[serde(default = "default_delta_algorithm")]
143    pub delta_algorithm: DeltaAlgorithm,
144}
145
146fn default_multithread() -> usize {
147    1
148}
149
150fn default_compression_algorithm() -> CompressionAlgorithm {
151    CompressionAlgorithm::Gzip
152}
153
154fn default_compression_level() -> u32 {
155    6  // gzip 的默认压缩级别
156}
157
158fn default_enable_deduplication() -> bool {
159    true
160}
161
162fn default_enable_delta_compression() -> bool {
163    false
164}
165
166fn default_similarity_threshold() -> f32 {
167    0.7  // 70% 相似度阈值
168}
169
170fn default_delta_algorithm() -> DeltaAlgorithm {
171    DeltaAlgorithm::Simple
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub enum IndexMode {
176    Auto,
177    Json,
178    Sqlite,
179}
180
181impl Default for Config {
182    fn default() -> Self {
183        Self {
184            storage_path: PathBuf::from(".stowr").join("storage"),
185            index_mode: IndexMode::Auto,
186            multithread: 1,
187            compression_algorithm: CompressionAlgorithm::Gzip,
188            compression_level: 6,
189            enable_deduplication: true,
190            enable_delta_compression: false,
191            similarity_threshold: 0.7,
192            delta_algorithm: DeltaAlgorithm::Simple,
193        }
194    }
195}
196
197impl Config {
198    pub fn load() -> Result<Self> {
199        let config_path = Self::config_path()?;
200        
201        if config_path.exists() {
202            let content = fs::read_to_string(&config_path)
203                .context("Failed to read config file")?;
204            let config: Config = serde_json::from_str(&content)
205                .context("Failed to parse config file")?;
206            Ok(config)
207        } else {
208            let config = Config::default();
209            config.save()?;
210            Ok(config)
211        }
212    }
213
214    pub fn save(&self) -> Result<()> {
215        let config_path = Self::config_path()?;
216        
217        // 确保配置目录存在
218        if let Some(parent) = config_path.parent() {
219            fs::create_dir_all(parent)
220                .context("Failed to create config directory")?;
221        }
222
223        // 确保存储目录存在
224        fs::create_dir_all(&self.storage_path)
225            .context("Failed to create storage directory")?;
226
227        let content = serde_json::to_string_pretty(self)
228            .context("Failed to serialize config")?;
229        
230        fs::write(&config_path, content)
231            .context("Failed to write config file")?;
232
233        Ok(())
234    }
235
236    pub fn config_path() -> Result<PathBuf> {
237        Ok(PathBuf::from(".stowr").join("config.json"))
238    }
239
240    pub fn set(&mut self, key: &str, value: &str) -> Result<()> {
241        match key {
242            "storage.path" => {
243                self.storage_path = PathBuf::from(value);
244            }
245            "index.mode" => {
246                self.index_mode = match value.to_lowercase().as_str() {
247                    "auto" => IndexMode::Auto,
248                    "json" => IndexMode::Json,
249                    "sqlite" => IndexMode::Sqlite,
250                    _ => return Err(anyhow::anyhow!("Invalid index mode. Valid values: auto, json, sqlite")),
251                };
252            }
253            "multithread" => {
254                self.multithread = value.parse::<usize>()
255                    .map_err(|_| anyhow::anyhow!("Invalid multithread value. Must be a positive number"))?;
256                if self.multithread == 0 {
257                    return Err(anyhow::anyhow!("Multithread value must be greater than 0"));
258                }
259            }
260            "compression.algorithm" => {
261                self.compression_algorithm = CompressionAlgorithm::from_str(value)?;
262                // 当算法改变时,更新为该算法的默认压缩级别
263                self.compression_level = self.compression_algorithm.default_level();
264            }
265            "compression.level" => {
266                let level = value.parse::<u32>()
267                    .map_err(|_| anyhow::anyhow!("Invalid compression level. Must be a number"))?;
268                
269                // 对于LZ4,直接设置为0并提示用户
270                if self.compression_algorithm == CompressionAlgorithm::Lz4 {
271                    println!("Note: LZ4 does not use compression levels. Level set to 0.");
272                    self.compression_level = 0;
273                } else {
274                    self.compression_level = self.compression_algorithm.validate_level(level)?;
275                }
276            }
277            "dedup.enable" => {
278                self.enable_deduplication = value.parse::<bool>()
279                    .map_err(|_| anyhow::anyhow!("Invalid boolean value. Must be true or false"))?;
280            }
281            "delta.enable" => {
282                self.enable_delta_compression = value.parse::<bool>()
283                    .map_err(|_| anyhow::anyhow!("Invalid boolean value. Must be true or false"))?;
284            }
285            "delta.similarity_threshold" => {
286                let threshold = value.parse::<f32>()
287                    .map_err(|_| anyhow::anyhow!("Invalid similarity threshold. Must be a number between 0.0 and 1.0"))?;
288                if threshold < 0.0 || threshold > 1.0 {
289                    return Err(anyhow::anyhow!("Similarity threshold must be between 0.0 and 1.0"));
290                }
291                self.similarity_threshold = threshold;
292            }
293            "delta.algorithm" => {
294                self.delta_algorithm = DeltaAlgorithm::from_str(value)?;
295            }
296            _ => return Err(anyhow::anyhow!("Unknown config key: {}", key)),
297        }
298        Ok(())
299    }
300
301    pub fn list(&self) -> Vec<(String, String)> {
302        vec![
303            ("storage.path".to_string(), self.storage_path.display().to_string()),
304            ("index.mode".to_string(), format!("{:?}", self.index_mode).to_lowercase()),
305            ("multithread".to_string(), self.multithread.to_string()),
306            ("compression.algorithm".to_string(), self.compression_algorithm.to_string()),
307            ("compression.level".to_string(), self.compression_level.to_string()),
308            ("dedup.enable".to_string(), self.enable_deduplication.to_string()),
309            ("delta.enable".to_string(), self.enable_delta_compression.to_string()),
310            ("delta.similarity_threshold".to_string(), self.similarity_threshold.to_string()),
311            ("delta.algorithm".to_string(), self.delta_algorithm.to_string()),
312        ]
313    }
314}