use std::collections::BTreeMap;
use std::sync::Arc;
use parking_lot::{Mutex, RwLock};
use crate::sugest::{SuggestionDict, SuggestionHit};
type DictMap = BTreeMap<Vec<u8>, Arc<Mutex<SuggestionDict>>>;
#[derive(Clone, Default)]
pub struct SuggestionRegistry {
inner: Arc<RwLock<DictMap>>,
}
impl std::fmt::Debug for SuggestionRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let count = self.inner.read().len();
f.debug_struct("SuggestionRegistry")
.field("dict_count", &count)
.finish()
}
}
impl SuggestionRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add(
&self,
key: &[u8],
suggestion: Vec<u8>,
score: f64,
incr: bool,
payload: Option<Vec<u8>>,
) -> usize {
let dict = self.get_or_create(key);
let mut guard = dict.lock();
guard.add(suggestion, score, incr, payload)
}
#[must_use]
pub fn get(
&self,
key: &[u8],
prefix: &[u8],
max: usize,
fuzzy: bool,
with_scores: bool,
with_payloads: bool,
) -> Vec<SuggestionHit> {
let Some(dict) = self.lookup(key) else {
return Vec::new();
};
let guard = dict.lock();
guard.get(prefix, max, fuzzy, with_scores, with_payloads)
}
pub fn del(&self, key: &[u8], suggestion: &[u8]) -> bool {
let Some(dict) = self.lookup(key) else {
return false;
};
let mut guard = dict.lock();
guard.del(suggestion)
}
#[must_use]
pub fn len(&self, key: &[u8]) -> usize {
let Some(dict) = self.lookup(key) else {
return 0;
};
let guard = dict.lock();
guard.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.read().is_empty()
}
fn lookup(&self, key: &[u8]) -> Option<Arc<Mutex<SuggestionDict>>> {
self.inner.read().get(key).cloned()
}
fn get_or_create(&self, key: &[u8]) -> Arc<Mutex<SuggestionDict>> {
if let Some(existing) = self.lookup(key) {
return existing;
}
let mut guard = self.inner.write();
if let Some(existing) = guard.get(key) {
return existing.clone();
}
let fresh = Arc::new(Mutex::new(SuggestionDict::new()));
guard.insert(key.to_vec(), fresh.clone());
fresh
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_then_len_round_trip() {
let reg = SuggestionRegistry::new();
assert_eq!(reg.add(b"k", b"alpha".to_vec(), 1.0, false, None), 1);
assert_eq!(reg.add(b"k", b"beta".to_vec(), 2.0, false, None), 2);
assert_eq!(reg.len(b"k"), 2);
assert_eq!(reg.len(b"missing"), 0);
}
#[test]
fn del_reports_presence() {
let reg = SuggestionRegistry::new();
reg.add(b"k", b"alpha".to_vec(), 1.0, false, None);
assert!(reg.del(b"k", b"alpha"));
assert!(!reg.del(b"k", b"alpha"));
assert!(!reg.del(b"missing", b"x"));
}
#[test]
fn distinct_keys_have_independent_dicts() {
let reg = SuggestionRegistry::new();
reg.add(b"k1", b"alpha".to_vec(), 1.0, false, None);
reg.add(b"k2", b"beta".to_vec(), 1.0, false, None);
assert_eq!(reg.len(b"k1"), 1);
assert_eq!(reg.len(b"k2"), 1);
let h1 = reg.get(b"k1", b"a", 5, false, false, false);
assert_eq!(h1.len(), 1);
assert_eq!(h1[0].value, b"alpha");
}
}