Skip to main content

datui_lib/
cache.rs

1use color_eyre::Result;
2use std::fs;
3use std::io::{BufRead, BufReader, Write};
4use std::path::{Path, PathBuf};
5
6/// Registry of known cache files
7const CACHE_FILES: &[&str] = &["query_history.txt"];
8
9/// Manages cache directory and cache file operations
10#[derive(Clone)]
11pub struct CacheManager {
12    pub(crate) cache_dir: PathBuf,
13}
14
15impl CacheManager {
16    /// Create a new CacheManager for the given app name
17    pub fn new(app_name: &str) -> Result<Self> {
18        let cache_dir = dirs::cache_dir()
19            .ok_or_else(|| color_eyre::eyre::eyre!("Could not determine cache directory"))?
20            .join(app_name);
21
22        Ok(Self { cache_dir })
23    }
24
25    /// Get the cache directory path
26    pub fn cache_dir(&self) -> &Path {
27        &self.cache_dir
28    }
29
30    /// Get path to a specific cache file
31    pub fn cache_file(&self, filename: &str) -> PathBuf {
32        self.cache_dir.join(filename)
33    }
34
35    /// Ensure the cache directory exists
36    pub fn ensure_cache_dir(&self) -> Result<()> {
37        if !self.cache_dir.exists() {
38            fs::create_dir_all(&self.cache_dir)?;
39        }
40        Ok(())
41    }
42
43    /// Clear a specific cache file
44    pub fn clear_file(&self, filename: &str) -> Result<()> {
45        let file_path = self.cache_file(filename);
46        if file_path.exists() {
47            fs::remove_file(&file_path)?;
48        }
49        Ok(())
50    }
51
52    /// Clear all registered cache files
53    /// Note: Templates are stored in config directory, not cache, so they are not cleared here.
54    /// Note: History files (e.g., `{id}_history.txt`) are dynamic and excluded from `clear_all()`.
55    /// They can be cleared individually via `clear_file()` if needed.
56    pub fn clear_all(&self) -> Result<()> {
57        for filename in CACHE_FILES {
58            let file_path = self.cache_file(filename);
59            if file_path.exists() {
60                if let Err(e) = fs::remove_file(&file_path) {
61                    eprintln!("Warning: Could not remove cache file {}: {}", filename, e);
62                }
63            }
64        }
65
66        Ok(())
67    }
68
69    /// Load history from a history file
70    /// History files are dynamic (`{id}_history.txt`) and are NOT included in `CACHE_FILES`
71    pub fn load_history_file(&self, history_id: &str) -> Result<Vec<String>> {
72        let history_file = self.cache_file(&format!("{}_history.txt", history_id));
73
74        if !history_file.exists() {
75            return Ok(Vec::new());
76        }
77
78        let file = fs::File::open(&history_file)?;
79        let reader = BufReader::new(file);
80        let mut history = Vec::new();
81
82        for line in reader.lines() {
83            let line = line?;
84            if !line.trim().is_empty() {
85                history.push(line);
86            }
87        }
88
89        Ok(history)
90    }
91
92    /// Save history to a history file
93    /// History files are dynamic (`{id}_history.txt`) and are NOT included in `CACHE_FILES`
94    pub fn save_history_file(&self, history_id: &str, history: &[String]) -> Result<()> {
95        self.ensure_cache_dir()?;
96        let history_file = self.cache_file(&format!("{}_history.txt", history_id));
97
98        let mut file = fs::File::create(&history_file)?;
99
100        // Write history entries (oldest first, but we keep the most recent entries)
101        for entry in history {
102            writeln!(file, "{}", entry)?;
103        }
104
105        Ok(())
106    }
107}