#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct SynonymRegistry {
groups: Vec<Vec<String>>,
index: HashMap<String, usize>,
}
impl SynonymRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_group(&mut self, words: &[&str]) {
if words.is_empty() {
return;
}
let group_idx = self.groups.len();
let group: Vec<String> = words.iter().map(|w| w.to_string()).collect();
for w in &group {
self.index.insert(w.to_lowercase(), group_idx);
}
self.groups.push(group);
}
pub fn synonyms_for(&self, word: &str) -> Option<&[String]> {
let key = word.to_lowercase();
self.index.get(&key).map(|&i| self.groups[i].as_slice())
}
pub fn is_empty(&self) -> bool {
self.groups.is_empty()
}
pub fn len(&self) -> usize {
self.groups.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_finds_registered_word() {
let mut r = SynonymRegistry::new();
r.register_group(&["class", "type"]);
let group = r.synonyms_for("class").unwrap();
assert_eq!(group, &["class".to_string(), "type".to_string()]);
}
#[test]
fn lookup_is_case_insensitive() {
let mut r = SynonymRegistry::new();
r.register_group(&["class", "type"]);
assert!(r.synonyms_for("Class").is_some());
assert!(r.synonyms_for("TYPE").is_some());
}
#[test]
fn unregistered_word_has_no_group() {
let r = SynonymRegistry::new();
assert!(r.synonyms_for("class").is_none());
}
#[test]
fn empty_group_is_ignored() {
let mut r = SynonymRegistry::new();
r.register_group(&[]);
assert!(r.is_empty());
}
}