1use anyhow::{Result, anyhow};
2use bytes::BytesMut;
3use once_cell::sync::Lazy;
4use sha2::{Digest, Sha256};
5use std::sync::RwLock;
6use std::{io::IoSlice, path::PathBuf};
7use tokio::io::AsyncReadExt;
8use tokio::{fs::create_dir_all, io::AsyncWriteExt};
9use tracing::{debug, info};
10
11static DEFAULT_CACHE_DIR: &str = "/tmp/mediacache";
13
14static CACHE_CONFIG: Lazy<RwLock<CacheConfig>> = Lazy::new(|| {
16 RwLock::new(CacheConfig {
17 cache_dir: PathBuf::from(DEFAULT_CACHE_DIR),
18 })
19});
20
21#[derive(Debug, Clone)]
22pub struct CacheConfig {
23 pub cache_dir: PathBuf,
24}
25
26pub fn set_cache_dir(path: &str) -> Result<()> {
28 let path = PathBuf::from(path);
29 let mut config = CACHE_CONFIG
30 .write()
31 .map_err(|_| anyhow!("Failed to acquire write lock"))?;
32 config.cache_dir = path;
33 Ok(())
34}
35
36pub fn get_cache_dir() -> Result<PathBuf> {
38 let config = CACHE_CONFIG
39 .read()
40 .map_err(|_| anyhow!("Failed to acquire read lock"))?;
41 Ok(config.cache_dir.clone())
42}
43
44pub async fn ensure_cache_dir() -> Result<()> {
46 let cache_dir = get_cache_dir()?;
47
48 if !cache_dir.exists() {
49 debug!("Creating cache directory: {:?}", cache_dir);
50 create_dir_all(&cache_dir).await?;
51 }
52
53 Ok(())
54}
55
56pub fn generate_cache_key(
58 input: &str,
59 sample_rate: u32,
60 speaker: Option<&String>,
61 speed: Option<f32>,
62) -> String {
63 let mut hasher = Sha256::new();
64 hasher.update(input.as_bytes());
65 let result = hasher.finalize();
66 match speaker {
67 Some(speaker) => format!(
68 "{}_{}_{}_{}",
69 hex::encode(result),
70 sample_rate,
71 speaker,
72 speed.unwrap_or(1.0)
73 ),
74 None => format!(
75 "{}_{}_{}",
76 hex::encode(result),
77 sample_rate,
78 speed.unwrap_or(1.0)
79 ),
80 }
81}
82
83pub fn get_cache_path(key: &str) -> Result<PathBuf> {
85 let cache_dir = get_cache_dir()?;
86 Ok(cache_dir.join(key).with_extension("pcm"))
87}
88
89pub async fn is_cached(key: &str) -> Result<bool> {
91 let path = get_cache_path(key)?;
92 Ok(tokio::fs::try_exists(&path).await?)
93}
94
95pub async fn store_in_cache(key: &str, data: &Vec<u8>) -> Result<()> {
97 ensure_cache_dir().await?;
98 let path = get_cache_path(key)?;
99 tokio::fs::write(&path.with_extension(".tmp"), data).await?;
100 tokio::fs::rename(&path.with_extension(".tmp"), &path).await?;
101 info!("cache: Stored {} -> {} bytes", key, data.len());
102 Ok(())
103}
104
105pub async fn store_in_cache_vectored(key: &str, data: &[impl AsRef<[u8]>]) -> Result<()> {
107 ensure_cache_dir().await?;
108 let path = get_cache_path(key)?;
109 let tmp_path = path.with_extension(".tmp");
110 let mut file = tokio::fs::File::create(tmp_path.clone()).await?;
111 let io_slices = data
112 .iter()
113 .map(|d| IoSlice::new(d.as_ref()))
114 .collect::<Vec<_>>();
115 let n = file.write_vectored(&io_slices).await?;
116 tokio::fs::rename(&tmp_path, &path).await?;
117 info!("cache: Stored {} -> {} bytes", key, n);
118 Ok(())
119}
120
121pub async fn retrieve_from_cache(key: &str) -> Result<Vec<u8>> {
123 let path = get_cache_path(key)?;
124
125 if !tokio::fs::try_exists(&path).await? {
126 return Err(anyhow!("Cache file not found for key: {}", key));
127 }
128
129 let data = tokio::fs::read(&path).await?;
130 debug!(key, size = data.len(), "retrieved file from cache");
131 Ok(data)
132}
133
134pub async fn retrieve_from_cache_with_buffer(key: &str, buffer: &mut BytesMut) -> Result<()> {
136 let path = get_cache_path(key)?;
137 let mut file = tokio::fs::File::open(path).await?;
138 let metadata = file.metadata().await?;
139 let file_size = metadata.len() as usize;
140 buffer.reserve(file_size);
141
142 while file.read_buf(buffer).await? > 0 {}
143 Ok(())
144}
145
146pub async fn delete_from_cache(key: &str) -> Result<()> {
148 let path = get_cache_path(key)?;
149
150 if tokio::fs::try_exists(&path).await? {
151 tokio::fs::remove_file(path).await?;
152 debug!("Deleted file from cache with key: {}", key);
153 }
154
155 Ok(())
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 #[tokio::test]
162 async fn test_cache_operations() -> Result<()> {
163 ensure_cache_dir().await?;
164
165 let key = generate_cache_key("test_data", 8000, None, None);
167
168 let test_data = b"TEST DATA".to_vec();
170 store_in_cache(&key, &test_data).await?;
171
172 assert!(is_cached(&key).await?);
174
175 let retrieved_data = retrieve_from_cache(&key).await?;
177 assert_eq!(retrieved_data, test_data);
178
179 delete_from_cache(&key).await?;
181 assert!(!is_cached(&key).await?);
182
183 let key2 = generate_cache_key("test_data2", 16000, None, None);
185 store_in_cache(&key2, &test_data).await?;
186 Ok(())
187 }
188
189 #[test]
190 fn test_generate_cache_key() {
191 let key1 = generate_cache_key("hello", 16000, None, None);
192 let key2 = generate_cache_key("hello", 8000, None, None);
193 let key3 = generate_cache_key("world", 16000, None, None);
194
195 assert_ne!(key1, key2);
197
198 assert_ne!(key1, key3);
200 }
201
202 #[tokio::test]
203 async fn test_store_in_cache_v() -> Result<()> {
204 let data1 = b"Hello, ";
206 let data2 = b"world!";
207 let data3 = b" This is a test.";
208 let data_slices = [data1.as_slice(), data2.as_slice(), data3.as_slice()];
209
210 let key = generate_cache_key("test_vectored_store", 16000, None, None);
211
212 delete_from_cache(&key).await.ok();
214 assert!(!is_cached(&key).await?);
215
216 store_in_cache_vectored(&key, &data_slices).await?;
218
219 assert!(is_cached(&key).await?);
221
222 let retrieved = retrieve_from_cache(&key).await?;
224 let expected = [data1.as_slice(), data2.as_slice(), data3.as_slice()].concat();
225 assert_eq!(retrieved, expected);
226
227 delete_from_cache(&key).await?;
229 Ok(())
230 }
231
232 #[tokio::test]
233 async fn test_store_in_cache_v_empty_slices() -> Result<()> {
234 let empty_data: &[&[u8]] = &[];
235 let key = generate_cache_key("test_empty_vectored", 16000, None, None);
236
237 delete_from_cache(&key).await.ok();
239
240 store_in_cache_vectored(&key, empty_data).await?;
242
243 assert!(is_cached(&key).await?);
245 let retrieved = retrieve_from_cache(&key).await?;
246 assert_eq!(retrieved.len(), 0);
247
248 delete_from_cache(&key).await?;
250 Ok(())
251 }
252
253 #[tokio::test]
254 async fn test_retrieve_from_cache_with_buffer() -> Result<()> {
255 let test_data = b"This is test data for buffer retrieval testing.";
257 let key = generate_cache_key("test_buffer_retrieve", 16000, None, None);
258
259 delete_from_cache(&key).await.ok();
261
262 store_in_cache(&key, &test_data.to_vec()).await?;
264
265 let mut buffer = BytesMut::new();
267 retrieve_from_cache_with_buffer(&key, &mut buffer).await?;
268
269 assert_eq!(buffer.as_ref(), test_data);
271 assert_eq!(buffer.len(), test_data.len());
272
273 delete_from_cache(&key).await?;
275 Ok(())
276 }
277
278 #[tokio::test]
279 async fn test_retrieve_from_cache_with_buffer_large_file() -> Result<()> {
280 let large_data: Vec<u8> = vec![7; 1024 * 1024];
282 let key = generate_cache_key("test_large_buffer", 16000, None, None);
283
284 delete_from_cache(&key).await.ok();
286
287 store_in_cache(&key, &large_data).await?;
289
290 let mut buffer = BytesMut::new();
292 retrieve_from_cache_with_buffer(&key, &mut buffer).await?;
293
294 assert_eq!(buffer.len(), large_data.len());
296 assert_eq!(buffer.as_ref(), large_data.as_slice());
297
298 delete_from_cache(&key).await?;
300 Ok(())
301 }
302
303 #[tokio::test]
304 async fn test_retrieve_from_cache_with_buffer_nonexistent() -> Result<()> {
305 let nonexistent_key = generate_cache_key("nonexistent_file", 16000, None, None);
306 let mut buffer = BytesMut::new();
307
308 let result = retrieve_from_cache_with_buffer(&nonexistent_key, &mut buffer).await;
310 assert!(result.is_err());
311
312 Ok(())
313 }
314
315 #[tokio::test]
316 async fn test_store_v_and_retrieve_buffer_integration() -> Result<()> {
317 let data_parts = [
319 b"Part 1: Hello".as_slice(),
320 b", Part 2: World".as_slice(),
321 b", Part 3: Integration Test!".as_slice(),
322 ];
323 let key = generate_cache_key("test_integration", 16000, None, None);
324
325 delete_from_cache(&key).await.ok();
327
328 store_in_cache_vectored(&key, &data_parts).await?;
330
331 let mut buffer = BytesMut::new();
333 retrieve_from_cache_with_buffer(&key, &mut buffer).await?;
334
335 let expected = data_parts.concat();
337 assert_eq!(buffer.as_ref(), expected.as_slice());
338 assert_eq!(buffer.len(), expected.len());
339
340 let regular_retrieve = retrieve_from_cache(&key).await?;
342 assert_eq!(regular_retrieve, expected);
343 assert_eq!(buffer.as_ref(), regular_retrieve.as_slice());
344
345 delete_from_cache(&key).await?;
347 Ok(())
348 }
349}