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}