use std::fmt::Debug;
#[derive(Debug, Clone)]
struct Node<Value: Debug> {
label: u8,
children: Vec<Node<Value>>,
value: Option<Value>,
}
impl<Value: Debug> Node<Value> {
fn new(label: u8) -> Self {
Self {
label,
children: Vec::new(),
value: None,
}
}
fn insert(&mut self, key: &[u8], value: Value) {
if key.is_empty() {
self.value = Some(value);
return;
}
match self
.children
.binary_search_by(|node| node.label.cmp(&key[0]))
{
Ok(idx) => {
self.children[idx].insert(&key[1..], value);
},
Err(idx) => {
self.children.insert(idx, Node::new(key[0]));
self.children[idx].insert(&key[1..], value);
},
}
}
fn lookup(&self, key: &[u8], depth: usize, maybe_more: bool) -> NodeFind<&Value> {
if key.is_empty() {
if self.children.is_empty() {
match self.value.as_ref() {
Some(value) => {
return NodeFind::Exact(depth, value);
},
None => panic!("Node has no children and no value!?"),
}
}
return match self.value.as_ref() {
Some(value) => {
if maybe_more {
NodeFind::AmbiguousMatch(depth, value)
} else {
NodeFind::Exact(depth, value)
}
},
None => NodeFind::AmbiguousBackTrack,
};
}
match self
.children
.binary_search_by(|node| node.label.cmp(&key[0]))
{
Ok(idx) => {
match self.children[idx].lookup(&key[1..], depth + 1, maybe_more) {
NodeFind::AmbiguousBackTrack => {
match self.value.as_ref() {
Some(value) => {
if maybe_more {
NodeFind::AmbiguousMatch(depth, value)
} else {
NodeFind::Exact(depth, value)
}
},
None => NodeFind::AmbiguousBackTrack,
}
},
result => result,
}
},
Err(_) => {
if depth == 0 {
NodeFind::None
} else {
match self.value.as_ref() {
Some(value) => NodeFind::Exact(depth, value),
None => NodeFind::AmbiguousBackTrack,
}
}
},
}
}
}
enum NodeFind<Value> {
None,
Exact(usize, Value),
AmbiguousBackTrack,
AmbiguousMatch(usize, Value),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Found<Value> {
None,
Exact(usize, Value),
Ambiguous(usize, Value),
NeedData,
}
#[derive(Debug, Clone)]
pub struct KeyMap<Value: Debug + Clone> {
root: Node<Value>,
}
impl<Value: Debug + Clone> Default for KeyMap<Value> {
fn default() -> Self {
Self::new()
}
}
impl<Value: Debug + Clone> KeyMap<Value> {
pub fn new() -> Self {
Self { root: Node::new(0) }
}
pub fn insert<K: AsRef<[u8]>>(&mut self, key: K, value: Value) {
self.root.insert(key.as_ref(), value)
}
pub fn lookup<S: AsRef<[u8]>>(&self, key: S, maybe_more: bool) -> Found<Value> {
match self.root.lookup(key.as_ref(), 0, maybe_more) {
NodeFind::None => Found::None,
NodeFind::AmbiguousBackTrack => Found::NeedData,
NodeFind::Exact(depth, value) => Found::Exact(depth, value.clone()),
NodeFind::AmbiguousMatch(depth, value) => Found::Ambiguous(depth, value.clone()),
}
}
}
#[cfg(test)]
mod test {
use super::*;
const NO_MORE: bool = false;
const MAYBE_MORE: bool = true;
#[test]
fn lookup_empty() {
let km: KeyMap<bool> = KeyMap::new();
assert_eq!(km.lookup("boo", true), Found::None);
}
#[test]
fn lookup() {
let mut km = KeyMap::new();
km.insert("boa", true);
km.insert("boo", true);
km.insert("boom", false);
assert_eq!(km.lookup("b", MAYBE_MORE), Found::NeedData);
assert_eq!(km.lookup("bo", MAYBE_MORE), Found::NeedData);
assert_eq!(km.lookup("boa", MAYBE_MORE), Found::Exact(3, true),);
assert_eq!(km.lookup("boo", MAYBE_MORE), Found::Ambiguous(3, true),);
assert_eq!(km.lookup("boom", MAYBE_MORE), Found::Exact(4, false),);
assert_eq!(km.lookup("boom!", MAYBE_MORE), Found::Exact(4, false),);
}
#[test]
fn lookup_with_multiple_ambiguous_matches_without_additional_input() {
let mut km = KeyMap::new();
km.insert("boa", false);
km.insert("boo", false);
km.insert("boom", true);
km.insert("boom!!", false);
assert_eq!(km.lookup("boom!", NO_MORE), Found::Exact(4, true));
}
#[test]
fn lookup_with_multiple_ambiguous_matches_with_potential_additional_input() {
let mut km = KeyMap::new();
km.insert("boa", false);
km.insert("boo", false);
km.insert("boom", true);
km.insert("boom!!", false);
assert_eq!(km.lookup("boom!", MAYBE_MORE), Found::Ambiguous(4, true));
}
#[test]
fn sequence() {
let mut km = KeyMap::new();
km.insert("\x03", true);
km.insert("\x27", true);
km.insert("\x03XYZ", true);
assert_eq!(km.lookup("\x03", MAYBE_MORE), Found::Ambiguous(1, true),);
assert_eq!(km.lookup("\x03foo", MAYBE_MORE), Found::Exact(1, true),);
assert_eq!(km.lookup("\x03X", MAYBE_MORE), Found::Ambiguous(1, true),);
}
}