vtcode_tui/utils/
cached_style_parser.rs1use anstyle::Style as AnsiStyle;
8use anyhow::{Context, Result};
9use std::collections::HashMap;
10use std::sync::RwLock;
11
12pub struct CachedStyleParser {
14 git_cache: RwLock<HashMap<String, AnsiStyle>>,
15 ls_colors_cache: RwLock<HashMap<String, AnsiStyle>>,
16}
17
18impl CachedStyleParser {
19 pub fn new() -> Self {
21 Self {
22 git_cache: RwLock::new(HashMap::new()),
23 ls_colors_cache: RwLock::new(HashMap::new()),
24 }
25 }
26
27 pub fn parse_git_style(&self, input: &str) -> Result<AnsiStyle> {
29 if let Ok(cache) = self.git_cache.read()
31 && let Some(cached) = cache.get(input)
32 {
33 return Ok(*cached);
34 }
35
36 let result = anstyle_git::parse(input)
38 .map_err(|e| anyhow::anyhow!("Failed to parse Git style '{}': {:?}", input, e))?;
39
40 if let Ok(mut cache) = self.git_cache.write() {
41 cache.insert(input.to_string(), result);
42 }
43
44 Ok(result)
45 }
46
47 pub fn parse_ls_colors(&self, input: &str) -> Result<AnsiStyle> {
49 if let Ok(cache) = self.ls_colors_cache.read()
51 && let Some(cached) = cache.get(input)
52 {
53 return Ok(*cached);
54 }
55
56 let result = anstyle_ls::parse(input)
58 .ok_or_else(|| anyhow::anyhow!("Failed to parse LS_COLORS '{}'", input))?;
59
60 if let Ok(mut cache) = self.ls_colors_cache.write() {
61 cache.insert(input.to_string(), result);
62 }
63
64 Ok(result)
65 }
66
67 pub fn parse_flexible(&self, input: &str) -> Result<AnsiStyle> {
69 match self.parse_git_style(input) {
71 Ok(style) => Ok(style),
72 Err(_) => {
73 self.parse_ls_colors(input)
75 .with_context(|| format!("Could not parse style string: '{}'", input))
76 }
77 }
78 }
79
80 pub fn clear_cache(&self) {
82 if let Ok(mut git_cache) = self.git_cache.write() {
83 git_cache.clear();
84 }
85 if let Ok(mut ls_colors_cache) = self.ls_colors_cache.write() {
86 ls_colors_cache.clear();
87 }
88 }
89
90 pub fn cache_stats(&self) -> (usize, usize) {
92 let git_count = self.git_cache.read().ok().map(|c| c.len()).unwrap_or(0);
93 let ls_colors_count = self
94 .ls_colors_cache
95 .read()
96 .ok()
97 .map(|c| c.len())
98 .unwrap_or(0);
99 (git_count, ls_colors_count)
100 }
101}
102
103impl Default for CachedStyleParser {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_parse_git_style() {
115 let parser = CachedStyleParser::new();
116 let result = parser.parse_git_style("bold red").unwrap();
117
118 assert!(result.get_effects().contains(anstyle::Effects::BOLD));
119 }
120
121 #[test]
122 fn test_parse_ls_colors() {
123 let parser = CachedStyleParser::new();
124 let result = parser.parse_ls_colors("34").unwrap(); assert!(result.get_fg_color().is_some());
127 }
128
129 #[test]
130 fn test_parse_flexible_git_first() {
131 let parser = CachedStyleParser::new();
132 let result = parser.parse_flexible("bold green").unwrap();
133
134 assert!(result.get_effects().contains(anstyle::Effects::BOLD));
135 }
136
137 #[test]
138 fn test_parse_flexible_ls_fallback() {
139 let parser = CachedStyleParser::new();
140 let result = parser.parse_flexible("01;34").unwrap(); assert!(result.get_effects().contains(anstyle::Effects::BOLD));
143 }
144
145 #[test]
146 fn test_caching_behavior() {
147 let parser = CachedStyleParser::new();
148
149 let _result1 = parser.parse_git_style("red").unwrap();
151 let _result2 = parser.parse_git_style("red").unwrap();
152
153 let (git_count, _) = parser.cache_stats();
154 assert_eq!(git_count, 1); }
156
157 #[test]
158 fn test_cache_clear() {
159 let parser = CachedStyleParser::new();
160 let _result = parser.parse_git_style("blue").unwrap();
161
162 assert_eq!(parser.cache_stats().0, 1); parser.clear_cache();
165
166 assert_eq!(parser.cache_stats().0, 0); }
168
169 #[test]
170 fn test_multiple_cache_entries() {
171 let parser = CachedStyleParser::new();
172 let _result1 = parser.parse_git_style("bold red").unwrap();
173 let _result2 = parser.parse_git_style("italic green").unwrap();
174 let _result3 = parser.parse_ls_colors("34").unwrap();
175
176 let (git_count, ls_count) = parser.cache_stats();
177 assert_eq!(git_count, 2); assert_eq!(ls_count, 1); }
180}