Skip to main content

cache_lite/
cache.rs

1/*
2 * @filename: cache.rs
3 * @description: Main cache manager for cache-lite library
4 * @author: TaimWay <taimway@gmail.com>
5 * 
6 * Copyright (C) 2026 TaimWay
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27use std::io;
28use std::collections::HashMap;
29use std::time::SystemTime;
30use chrono::{DateTime, Local};
31use crate::config::{CacheConfig, CacheFormatConfig, };
32use crate::object::CacheObject;
33
34fn time_format(time: SystemTime, format: &str) -> String {
35    let datetime: DateTime<Local> = time.into();
36    datetime.format(format).to_string()
37}
38
39/// Main cache manager handling multiple cache objects
40pub struct Cache {
41    config: CacheConfig,
42    objects: HashMap<String, CacheObject>,
43    next_id: u32,
44}
45
46impl Cache {
47    /// Creates a new Cache with given configuration
48    /// 
49    /// # Parameters
50    /// - `config: CacheConfig` - Cache configuration
51    /// 
52    /// # Returns
53    /// New Cache instance
54    pub fn new(config: CacheConfig) -> Self {
55        Cache {
56            config,
57            objects: HashMap::new(),
58            next_id: 1,
59        }
60    }
61
62    /// Creates a new cache object with optional custom configuration
63    /// 
64    /// # Parameters
65    /// - `name: &str` - Cache object identifier
66    /// - `custom_config: Option<&str>` - Optional JSON configuration override
67    /// 
68    /// # Returns
69    /// New CacheObject instance
70    pub fn create(&mut self, name: &str, custom_config: Option<&str>) -> CacheObject {
71        let id = self.next_id;
72        self.next_id += 1;
73        
74        // Parse custom config if provided
75        let mut format_str = self.config.format.filename.clone();
76        
77        if let Some(config_str) = custom_config {
78            if let Ok(custom) = serde_json::from_str::<CacheFormatConfig>(config_str) {
79                format_str = custom.filename;
80            }
81            // Note: Custom lifecycle would need more complex parsing
82        }
83        
84        let cache_path = if cfg!(windows) {
85            self.expand_path(&self.config.path.windows)
86        } else {
87            self.expand_path(&self.config.path.linux)
88        };
89        
90        let filename = format_str
91            .replace("{name}", name)
92            .replace("{id}", &id.to_string())
93            .replace("{time}", &time_format(SystemTime::now(), &self.config.format.time));
94            
95        let full_path = std::path::PathBuf::from(&cache_path).join(&filename);
96        
97        // Create directory if it doesn't exist
98        if let Some(parent) = full_path.parent() {
99            let _ = std::fs::create_dir_all(parent);
100        }
101        
102        let cache_object = CacheObject::new(
103            name.to_string(),
104            full_path,
105            id
106        );
107        
108        self.objects.insert(name.to_string(), cache_object.clone());
109        
110        cache_object
111    }
112
113    /// Expands environment variables in path
114    fn expand_path(&self, path: &str) -> String {
115        let mut expanded = path.to_string();
116        
117        // Expand Windows environment variables
118        if cfg!(windows) && path.contains("%") {
119            expanded = self.expand_windows_env_vars(path);
120        }
121        
122        // Expand tilde for home directory (Unix-like systems)
123        if expanded.starts_with('~') {
124            if let Some(home) = dirs::home_dir() {
125                expanded = home.to_string_lossy().to_string() + &expanded[1..];
126            }
127        }
128        
129        expanded
130    }
131
132    /// Expands Windows environment variables
133    fn expand_windows_env_vars(&self, path: &str) -> String {
134        use std::env;
135        let mut result = path.to_string();
136        
137        // Simple environment variable expansion
138        if let Ok(temp) = env::var("TEMP") {
139            result = result.replace("%temp%", &temp);
140        }
141        if let Ok(tmp) = env::var("TMP") {
142            result = result.replace("%tmp%", &tmp);
143        }
144        if let Ok(appdata) = env::var("APPDATA") {
145            result = result.replace("%appdata%", &appdata);
146        }
147        if let Ok(localappdata) = env::var("LOCALAPPDATA") {
148            result = result.replace("%localappdata%", &localappdata);
149        }
150        if let Ok(userprofile) = env::var("USERPROFILE") {
151            result = result.replace("%userprofile%", &userprofile);
152        }
153        
154        result
155    }
156
157    /// Retrieves an existing cache object by name
158    /// 
159    /// # Parameters
160    /// - `name: &str` - Cache object identifier
161    /// 
162    /// # Returns
163    /// `io::Result<CacheObject>` - Retrieved cache object or error
164    pub fn get(&self, name: &str) -> io::Result<CacheObject> {
165        self.objects.get(name)
166            .cloned()
167            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Cache object not found"))
168    }
169
170    /// Returns the number of cache objects
171    /// 
172    /// # Returns
173    /// `io::Result<u32>` - Count of cache objects or error
174    pub fn len(&self) -> io::Result<u32> {
175        Ok(self.objects.len() as u32)
176    }
177
178    /// Removes a cache object by name
179    /// 
180    /// # Parameters
181    /// - `name: &str` - Cache object identifier
182    /// 
183    /// # Returns
184    /// `io::Result<()>` - Success or error
185    pub fn remove(&mut self, name: &str) -> io::Result<()> {
186        if let Some(cache_obj) = self.objects.remove(name) {
187            let _ = cache_obj.delete();
188        }
189        Ok(())
190    }
191
192    /// Cleans up expired cache objects
193    /// 
194    /// # Returns
195    /// `io::Result<u32>` - Number of cleaned objects
196    #[deprecated(note = "Due to being deprecated in its lifecycle, this function only returns 0; please use the Cache::clear()")]
197    pub fn cleanup(&mut self) -> io::Result<u32> {
198        Ok(0)
199    }
200
201    /// Clears all cache objects
202    /// 
203    /// # Returns
204    /// `io::Result<()>` - Success or error
205    pub fn clear(&mut self) -> io::Result<()> {
206        for (_, cache_obj) in &self.objects {
207            let _ = cache_obj.delete();
208        }
209        self.objects.clear();
210        
211        Ok(())
212    }
213
214    /// Updates the cache configuration
215    /// 
216    /// # Parameters
217    /// - `config: CacheConfig` - New configuration
218    pub fn set_config(&mut self, config: CacheConfig) {
219        self.config = config;
220    }
221
222    /// Returns current cache configuration
223    /// 
224    /// # Returns
225    /// `CacheConfig` - Current configuration
226    pub fn get_config(&self) -> CacheConfig {
227        self.config.clone()
228    }
229
230    /// Returns iterator over all cache objects
231    /// 
232    /// # Returns
233    /// `impl Iterator<Item = &CacheObject>` - Iterator over cache objects
234    pub fn iter(&self) -> impl Iterator<Item = &CacheObject> {
235        self.objects.values()
236    }
237}