use petgraph::{graph::NodeIndex, stable_graph::StableGraph, visit::EdgeRef, Directed};
use crate::InvalidKeyPartErr;
#[derive(Debug)]
pub struct Dictionary<K, V>
where
K: PartialEq,
{
pub(crate) graph: StableGraph<Option<V>, K, Directed, usize>,
pub(crate) root: NodeIndex<usize>,
}
#[derive(Debug)]
pub struct Lookup<'a, K, V>
where
K: PartialEq,
{
dict: &'a Dictionary<K, V>,
curr_node: NodeIndex<usize>,
}
impl<K, V> Default for Dictionary<K, V>
where
K: PartialEq,
{
fn default() -> Self {
let mut graph = StableGraph::<Option<V>, K, Directed, usize>::default();
let root = graph.add_node(None);
Self { graph, root }
}
}
impl<K, V> Dictionary<K, V>
where
K: PartialEq,
{
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.graph.node_count() <= 1
}
pub fn len(&self) -> usize {
self.graph
.node_indices()
.filter(|index| self.graph[*index].is_some())
.count()
}
pub fn insert<W>(&mut self, key: W, value: V)
where
W: IntoIterator<Item = K>,
{
let mut iter = key.into_iter().peekable();
let mut curr_node = self.root;
while let Some(key_part) = iter.next() {
match self
.graph
.edges(curr_node)
.find(|edge| edge.weight() == &key_part)
{
Some(edge) => {
curr_node = edge.target();
}
None => match iter.peek() {
Some(_) => {
let new_node = self.graph.add_node(None);
self.graph.add_edge(curr_node, new_node, key_part);
curr_node = new_node;
}
None => {
let node_with_val = self.graph.add_node(Some(value));
self.graph.add_edge(curr_node, node_with_val, key_part);
break;
}
},
}
}
}
pub fn get<W>(&self, key: W) -> Option<&V>
where
W: IntoIterator<Item = K>,
{
let node = self.find_node(key)?;
self.graph.node_weight(node)?.as_ref()
}
pub fn get_mut<W>(&mut self, key: W) -> Option<&mut V>
where
W: IntoIterator<Item = K>,
{
let node = self.find_node(key)?;
self.graph.node_weight_mut(node)?.as_mut()
}
fn find_node<W>(&self, key: W) -> Option<NodeIndex<usize>>
where
W: IntoIterator<Item = K>,
{
let mut iter = key.into_iter().peekable();
let mut curr_node = self.root;
while let Some(key_part) = iter.next() {
match self
.graph
.edges(curr_node)
.find(|edge| edge.weight() == &key_part)
{
Some(edge) => {
curr_node = edge.target();
if iter.peek().is_none() {
return Some(curr_node);
}
}
None => return None,
}
}
None
}
pub fn lookup(&self) -> Lookup<'_, K, V> {
Lookup {
dict: self,
curr_node: self.root,
}
}
}
impl<K, V> Lookup<'_, K, V>
where
K: PartialEq,
{
pub fn partial_search(&mut self, key_part: &K) -> Result<(), InvalidKeyPartErr> {
match self
.dict
.graph
.edges(self.curr_node)
.find(|edge| edge.weight() == key_part)
{
Some(edge) => self.curr_node = edge.target(),
None => {
self.curr_node = self.dict.root;
return Err(InvalidKeyPartErr);
}
}
Ok(())
}
pub fn try_resolve(&self) -> Option<&V> {
self.dict.graph.node_weight(self.curr_node)?.as_ref()
}
pub fn get<W>(&self, key: W) -> Option<&V>
where
W: IntoIterator<Item = K>,
{
self.dict.get(key)
}
pub fn reset(&mut self) {
self.curr_node = self.dict.root;
}
}
#[cfg(test)]
mod tests {
use super::Dictionary;
#[test]
fn insert() {
let mut dict = Dictionary::new();
dict.insert(":D".chars(), "Hello");
assert_eq!(dict.len(), 1);
let val = dict.get(":D".chars());
assert_eq!(val, Some(&"Hello"));
}
#[test]
fn partial_find() {
let mut dict = Dictionary::new();
dict.insert(":D".chars(), "Hello");
let mut lookup = dict.lookup();
let _ = lookup.partial_search(&':');
let _ = lookup.partial_search(&'D');
let val = lookup.try_resolve();
assert_eq!(val, Some(&"Hello"));
}
#[test]
fn multiple_vals() {
let mut dict = Dictionary::new();
dict.insert(":D".chars(), "Hi there");
dict.insert(":)".chars(), "Hello");
let mut lookup = dict.lookup();
let _ = lookup.partial_search(&':');
let _ = lookup.partial_search(&'D');
let val1 = lookup.try_resolve();
let mut lookup = dict.lookup();
let _ = lookup.partial_search(&':');
let _ = lookup.partial_search(&')');
let val2 = lookup.try_resolve();
assert_eq!(val1, Some(&"Hi there"));
assert_eq!(val2, Some(&"Hello"));
}
#[test]
fn mutate_node() {
let mut dict = Dictionary::new();
dict.insert(":D".chars(), String::from("Hi there"));
let val = dict.get_mut(":D".chars()).unwrap();
*val = String::from("Hi capillary!");
let new_val = dict.get(":D".chars());
assert_eq!(new_val, Some(&String::from("Hi capillary!")));
}
}