#[cfg(not(feature = "std"))]
use alloc::format;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
use crate::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct AntonymRegistry {
map: HashMap<String, String>,
}
impl AntonymRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, negative: &str, positive: &str) {
self.map
.insert(negative.to_lowercase(), positive.to_string());
}
pub fn lookup(&self, negative: &str) -> Option<&str> {
self.map.get(&negative.to_lowercase()).map(|s| s.as_str())
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn len(&self) -> usize {
self.map.len()
}
}
const SIMPLE_AUX: &[&str] = &[
"is", "are", "was", "were", "has", "have", "had", "will", "would", "could", "should", "may",
"might", "must", "can",
];
pub fn insert_not(phrase: &str) -> String {
let mut parts = phrase.splitn(2, ' ');
let first = match parts.next() {
Some(w) => w,
None => return phrase.to_string(),
};
let rest = parts.next().unwrap_or("");
if SIMPLE_AUX.contains(&first.to_lowercase().as_str()) {
if rest.is_empty() {
return format!("{first} not");
}
return format!("{first} not {rest}");
}
format!("not {phrase}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_not_after_was() {
assert_eq!(insert_not("was modified"), "was not modified");
}
#[test]
fn insert_not_after_has_been() {
assert_eq!(insert_not("has been renamed"), "has not been renamed");
}
#[test]
fn insert_not_after_modal() {
assert_eq!(insert_not("will break"), "will not break");
assert_eq!(insert_not("must fail"), "must not fail");
}
#[test]
fn insert_not_without_aux_falls_back_to_prefix() {
assert_eq!(insert_not("broke"), "not broke");
}
#[test]
fn registry_lookup_case_insensitive() {
let mut r = AntonymRegistry::new();
r.register("was modified", "remained unchanged");
assert_eq!(r.lookup("Was Modified"), Some("remained unchanged"));
}
#[test]
fn registry_unknown_lookup_is_none() {
let r = AntonymRegistry::new();
assert_eq!(r.lookup("was modified"), None);
}
}