steer_tui/tui/state/
file_cache.rs1use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
2use std::sync::Arc;
3use tokio::sync::RwLock;
4
5#[derive(Clone, Debug)]
7pub struct FileCache {
8 files: Arc<RwLock<Vec<String>>>,
10 session_id: String,
12}
13
14impl FileCache {
15 pub fn new(session_id: String) -> Self {
17 Self {
18 files: Arc::new(RwLock::new(Vec::new())),
19 session_id,
20 }
21 }
22
23 pub async fn update(&self, files: Vec<String>) {
25 let mut cache = self.files.write().await;
26 *cache = files;
27 }
28
29 pub async fn clear(&self) {
31 let mut cache = self.files.write().await;
32 cache.clear();
33 }
34
35 pub async fn is_empty(&self) -> bool {
37 let cache = self.files.read().await;
38 cache.is_empty()
39 }
40
41 pub async fn len(&self) -> usize {
43 let cache = self.files.read().await;
44 cache.len()
45 }
46
47 pub async fn fuzzy_search(&self, query: &str, max_results: Option<usize>) -> Vec<String> {
49 if query.is_empty() {
50 let cache = self.files.read().await;
52 let max = max_results.unwrap_or(cache.len());
53 return cache.iter().take(max).cloned().collect();
54 }
55
56 let cache = self.files.read().await;
57 let matcher = SkimMatcherV2::default();
58
59 let mut scored_files: Vec<(i64, String)> = cache
60 .iter()
61 .filter_map(|file| {
62 matcher
63 .fuzzy_match(file, query)
64 .map(|score| (score, file.clone()))
65 })
66 .collect();
67
68 scored_files.sort_by(|a, b| b.0.cmp(&a.0));
70
71 if let Some(max) = max_results {
74 scored_files
75 .into_iter()
76 .take(max)
77 .map(|(_, file)| file)
78 .collect()
79 } else {
80 scored_files.into_iter().map(|(_, file)| file).collect()
81 }
82 }
83
84 pub fn session_id(&self) -> &str {
86 &self.session_id
87 }
88}