pytest_language_server/fixtures/
mod.rs1mod analyzer;
10pub(crate) mod cli;
11pub mod decorators; mod resolver;
13mod scanner;
14mod string_utils;
15pub mod types;
16
17#[allow(unused_imports)] pub use types::{
19 CompletionContext, FixtureDefinition, FixtureUsage, ParamInsertionInfo, UndeclaredFixture,
20};
21
22use dashmap::DashMap;
23use std::collections::hash_map::DefaultHasher;
24use std::collections::HashSet;
25use std::hash::{Hash, Hasher};
26use std::path::{Path, PathBuf};
27use std::sync::Arc;
28use tracing::debug;
29
30type LineIndexCacheEntry = (u64, Arc<Vec<usize>>);
33
34#[derive(Debug)]
38pub struct FixtureDatabase {
39 pub definitions: Arc<DashMap<String, Vec<FixtureDefinition>>>,
41 pub usages: Arc<DashMap<PathBuf, Vec<FixtureUsage>>>,
43 pub file_cache: Arc<DashMap<PathBuf, Arc<String>>>,
45 pub undeclared_fixtures: Arc<DashMap<PathBuf, Vec<UndeclaredFixture>>>,
47 pub imports: Arc<DashMap<PathBuf, HashSet<String>>>,
49 pub canonical_path_cache: Arc<DashMap<PathBuf, PathBuf>>,
51 pub line_index_cache: Arc<DashMap<PathBuf, LineIndexCacheEntry>>,
54}
55
56impl Default for FixtureDatabase {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl FixtureDatabase {
63 pub fn new() -> Self {
65 Self {
66 definitions: Arc::new(DashMap::new()),
67 usages: Arc::new(DashMap::new()),
68 file_cache: Arc::new(DashMap::new()),
69 undeclared_fixtures: Arc::new(DashMap::new()),
70 imports: Arc::new(DashMap::new()),
71 canonical_path_cache: Arc::new(DashMap::new()),
72 line_index_cache: Arc::new(DashMap::new()),
73 }
74 }
75
76 pub(crate) fn get_canonical_path(&self, path: PathBuf) -> PathBuf {
79 if let Some(cached) = self.canonical_path_cache.get(&path) {
81 return cached.value().clone();
82 }
83
84 let canonical = path.canonicalize().unwrap_or_else(|_| {
86 debug!("Could not canonicalize path {:?}, using as-is", path);
87 path.clone()
88 });
89
90 self.canonical_path_cache.insert(path, canonical.clone());
92 canonical
93 }
94
95 pub(crate) fn get_file_content(&self, file_path: &Path) -> Option<Arc<String>> {
98 if let Some(cached) = self.file_cache.get(file_path) {
99 Some(Arc::clone(cached.value()))
100 } else {
101 std::fs::read_to_string(file_path).ok().map(Arc::new)
102 }
103 }
104
105 pub(crate) fn get_line_index(&self, file_path: &Path, content: &str) -> Arc<Vec<usize>> {
109 let content_hash = Self::hash_content(content);
110
111 if let Some(cached) = self.line_index_cache.get(file_path) {
113 let (cached_hash, cached_index) = cached.value();
114 if *cached_hash == content_hash {
115 return Arc::clone(cached_index);
116 }
117 }
118
119 let line_index = Self::build_line_index(content);
121 let arc_index = Arc::new(line_index);
122
123 self.line_index_cache.insert(
125 file_path.to_path_buf(),
126 (content_hash, Arc::clone(&arc_index)),
127 );
128
129 arc_index
130 }
131
132 fn hash_content(content: &str) -> u64 {
134 let mut hasher = DefaultHasher::new();
135 content.hash(&mut hasher);
136 hasher.finish()
137 }
138}