use anstyle::Style as AnsiStyle;
use anyhow::{Context, Result};
use vtcode_commons::lr_map::LrMap;
pub struct CachedStyleParser {
git_cache: LrMap<String, AnsiStyle>,
ls_colors_cache: LrMap<String, AnsiStyle>,
}
impl CachedStyleParser {
pub fn new() -> Self {
Self {
git_cache: LrMap::new(),
ls_colors_cache: LrMap::new(),
}
}
pub fn parse_git_style(&self, input: &str) -> Result<AnsiStyle> {
if let Some(cached) = self.git_cache.get(input) {
return Ok(cached);
}
let result = anstyle_git::parse(input)
.map_err(|e| anyhow::anyhow!("Failed to parse Git style '{}': {:?}", input, e))?;
self.git_cache.insert(input.to_string(), result);
Ok(result)
}
pub fn parse_ls_colors(&self, input: &str) -> Result<AnsiStyle> {
if let Some(cached) = self.ls_colors_cache.get(input) {
return Ok(cached);
}
let result = anstyle_ls::parse(input)
.ok_or_else(|| anyhow::anyhow!("Failed to parse LS_COLORS '{}'", input))?;
self.ls_colors_cache.insert(input.to_string(), result);
Ok(result)
}
pub fn parse_flexible(&self, input: &str) -> Result<AnsiStyle> {
match self.parse_git_style(input) {
Ok(style) => Ok(style),
Err(_) => self
.parse_ls_colors(input)
.with_context(|| format!("Could not parse style string: '{}'", input)),
}
}
pub fn clear_cache(&self) {
self.git_cache.clear();
self.ls_colors_cache.clear();
}
pub fn cache_stats(&self) -> (usize, usize) {
(self.git_cache.len(), self.ls_colors_cache.len())
}
}
impl Default for CachedStyleParser {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_git_style() {
let parser = CachedStyleParser::new();
let result = parser.parse_git_style("bold red").unwrap();
assert!(result.get_effects().contains(anstyle::Effects::BOLD));
}
#[test]
fn test_parse_ls_colors() {
let parser = CachedStyleParser::new();
let result = parser.parse_ls_colors("34").unwrap();
assert!(result.get_fg_color().is_some());
}
#[test]
fn test_parse_flexible_git_first() {
let parser = CachedStyleParser::new();
let result = parser.parse_flexible("bold green").unwrap();
assert!(result.get_effects().contains(anstyle::Effects::BOLD));
}
#[test]
fn test_parse_flexible_ls_fallback() {
let parser = CachedStyleParser::new();
let result = parser.parse_flexible("01;34").unwrap();
assert!(result.get_effects().contains(anstyle::Effects::BOLD));
}
#[test]
fn test_caching_behavior() {
let parser = CachedStyleParser::new();
let _result1 = parser.parse_git_style("red").unwrap();
let _result2 = parser.parse_git_style("red").unwrap();
let (git_count, _) = parser.cache_stats();
assert_eq!(git_count, 1); }
#[test]
fn test_cache_clear() {
let parser = CachedStyleParser::new();
let _result = parser.parse_git_style("blue").unwrap();
assert_eq!(parser.cache_stats().0, 1);
parser.clear_cache();
assert_eq!(parser.cache_stats().0, 0); }
#[test]
fn test_multiple_cache_entries() {
let parser = CachedStyleParser::new();
let _result1 = parser.parse_git_style("bold red").unwrap();
let _result2 = parser.parse_git_style("italic green").unwrap();
let _result3 = parser.parse_ls_colors("34").unwrap();
let (git_count, ls_count) = parser.cache_stats();
assert_eq!(git_count, 2); assert_eq!(ls_count, 1); }
}