use parking_lot::RwLock;
use std::collections::HashMap;
use std::sync::Arc;
pub struct NameIndex {
inner: Arc<RwLock<HashMap<String, Vec<i64>>>>,
}
impl Default for NameIndex {
fn default() -> Self {
Self::new()
}
}
impl NameIndex {
pub fn new() -> Self {
Self {
inner: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn insert(&self, name: String, node_id: i64) {
let mut index = self.inner.write();
index.entry(name).or_default().push(node_id);
}
pub fn clear(&self) {
let mut index = self.inner.write();
index.clear();
}
pub fn get_exact(&self, name: &str) -> Vec<i64> {
let index = self.inner.read();
index.get(name).cloned().unwrap_or_default()
}
pub fn get_prefix(&self, prefix: &str) -> Vec<i64> {
let index = self.inner.read();
let mut result = Vec::new();
for (name, ids) in index.iter() {
if name.starts_with(prefix) {
result.extend(ids.clone());
}
}
result
}
pub fn get_substring(&self, substring: &str) -> Vec<i64> {
let index = self.inner.read();
let mut result = Vec::new();
for (name, ids) in index.iter() {
if name.contains(substring) {
result.extend(ids.clone());
}
}
result
}
pub fn stats(&self) -> NameIndexStats {
let index = self.inner.read();
let total_names = index.len();
let total_nodes: usize = index.values().map(|v| v.len()).sum();
NameIndexStats {
unique_names: total_names,
total_nodes,
}
}
}
pub struct NameIndexStats {
pub unique_names: usize,
pub total_nodes: usize,
}
impl NameIndex {
pub(crate) fn export(&self) -> HashMap<String, Vec<i64>> {
self.inner.read().clone()
}
pub(crate) fn import(&self, data: HashMap<String, Vec<i64>>) {
let mut index = self.inner.write();
*index = data;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exact_match() {
let index = NameIndex::new();
index.insert("func_a".to_string(), 1);
index.insert("func_b".to_string(), 2);
index.insert("class_a".to_string(), 3);
assert_eq!(index.get_exact("func_a"), vec![1]);
assert_eq!(index.get_exact("func_b"), vec![2]);
assert_eq!(index.get_exact("nonexistent"), Vec::<i64>::new());
}
#[test]
fn test_prefix_match() {
let index = NameIndex::new();
index.insert("func_a".to_string(), 1);
index.insert("func_b".to_string(), 2);
index.insert("class_a".to_string(), 3);
index.insert("func_ab".to_string(), 4);
let results = index.get_prefix("func");
assert_eq!(results.len(), 3);
assert!(results.contains(&1));
assert!(results.contains(&2));
assert!(results.contains(&4));
assert!(!results.contains(&3));
}
#[test]
fn test_multiple_nodes_same_name() {
let index = NameIndex::new();
index.insert("duplicate".to_string(), 1);
index.insert("duplicate".to_string(), 2);
index.insert("duplicate".to_string(), 3);
let mut results = index.get_exact("duplicate");
results.sort();
assert_eq!(results, vec![1, 2, 3]);
}
#[test]
fn test_stats() {
let index = NameIndex::new();
index.insert("a".to_string(), 1);
index.insert("b".to_string(), 2);
index.insert("a".to_string(), 3);
let stats = index.stats();
assert_eq!(stats.unique_names, 2);
assert_eq!(stats.total_nodes, 3);
}
}