#![cfg(feature = "pathmap-backend")]
use libdictenstein::pathmap::{
PathMapDictionary, PathMapDictionaryChar, PathMapRef, PathMapRefChar, PathMapSnapshot,
};
use liblevenshtein::prelude::*;
use pathmap::PathMap;
fn sorted(mut v: Vec<String>) -> Vec<String> {
v.sort();
v.dedup();
v
}
fn raw_map(terms: &[&str]) -> PathMap<()> {
let mut map = PathMap::new();
for t in terms {
map.insert(t.as_bytes(), ());
}
map
}
#[test]
fn transducer_over_owned_snapshot() {
let dict: PathMapDictionary<()> =
PathMapDictionary::from_terms(vec!["foo", "food", "fool", "bar"]);
let t = Transducer::new(dict.snapshot(), Algorithm::Standard);
let got = sorted(t.query("fooo", 1).collect());
assert_eq!(
got,
vec!["foo".to_string(), "food".to_string(), "fool".to_string()]
);
let with_dist: Vec<(String, usize)> = t
.query_with_distance("foo", 1)
.map(|c| (c.term, c.distance))
.collect();
assert!(with_dist.contains(&("foo".to_string(), 0)));
let ordered: Vec<usize> = t.query_ordered("fooo", 2).map(|c| c.distance).collect();
assert!(
ordered.windows(2).all(|w| w[0] <= w[1]),
"query_ordered must be distance-first"
);
}
#[test]
fn transducer_over_borrowed_ref_no_static_bound() {
let map = raw_map(&["foo", "food", "fool", "bar", "baz"]);
let dict = PathMapRef::from_map(&map); let t = Transducer::new(dict, Algorithm::Standard);
let got = sorted(t.query("fooo", 1).collect());
assert_eq!(
got,
vec!["foo".to_string(), "food".to_string(), "fool".to_string()]
);
let near_bar = sorted(t.query("bar", 1).collect());
assert!(near_bar.contains(&"bar".to_string()));
assert!(near_bar.contains(&"baz".to_string()));
drop(t);
drop(map);
}
#[test]
fn transducer_over_subtrie_from_read_zippers() {
let map = raw_map(&["foobar", "foobaz", "foo", "other"]);
let rz = map.read_zipper();
let borrowed = PathMapRef::from_read_zipper(&rz);
let t = Transducer::new(borrowed, Algorithm::Standard);
assert!(t.query("foo", 0).any(|s| s == "foo"));
drop(t);
drop(rz);
let owned_rz = map.clone().into_read_zipper(b"foo");
let sub = PathMapSnapshot::from_read_zipper(owned_rz);
let t2 = Transducer::new(sub, Algorithm::Standard);
let got = sorted(t2.query("bar", 1).collect());
assert!(
got.contains(&"bar".to_string()),
"foobar's remainder 'bar' must be reachable in the foo-rooted subtrie"
);
}
#[test]
fn snapshot_isolation_at_transducer_level() {
let dict: PathMapDictionary<()> = PathMapDictionary::from_terms(vec!["alpha"]);
let t = Transducer::new(dict.snapshot(), Algorithm::Standard);
dict.insert("alphb");
let got = sorted(t.query("alpha", 1).collect());
assert_eq!(
got,
vec!["alpha".to_string()],
"snapshot must not observe post-snapshot inserts"
);
}
#[test]
fn char_snapshot_transducer_unicode() {
let dict: PathMapDictionaryChar<()> =
PathMapDictionaryChar::from_terms(vec!["café", "cafe", "cafés"]);
let t = Transducer::new(dict.snapshot(), Algorithm::Standard);
let got = sorted(t.query("café", 1).collect());
assert!(got.contains(&"café".to_string()));
assert!(got.contains(&"cafe".to_string()), "é↔e is one char edit");
assert!(
got.contains(&"cafés".to_string()),
"appending 's' is one char edit"
);
}
#[test]
fn borrowed_char_ref_transducer() {
let map = raw_map(&["中文", "中華"]);
let dict = PathMapRefChar::from_map(&map);
let t = Transducer::new(dict, Algorithm::Standard);
let got = sorted(t.query("中文", 1).collect());
assert!(got.contains(&"中文".to_string()));
assert!(got.contains(&"中華".to_string()), "文↔華 is one char edit");
drop(t);
drop(map);
}