#![deny(missing_docs)]
use std::collections::{HashMap, VecDeque};
use std::{cell::RefCell, fmt, rc::Rc};
pub mod utils;
#[derive(Clone, Debug)]
pub struct Node {
children: RefCell<HashMap<char, Rc<Node>>>,
pub depth: usize,
pub key: char,
value: RefCell<Option<String>>,
}
impl Default for Node {
fn default() -> Self {
Self::new('\0', 0)
}
}
impl Node {
pub fn new(key: char, depth: usize) -> Self {
Self {
children: HashMap::new().into(),
depth,
key,
value: None.into(),
}
}
pub fn insert(&self, sequence: Vec<char>, value: String) {
if let Some(character) = sequence.first() {
self.children
.borrow_mut()
.entry(*character)
.or_insert_with(|| Rc::new(Self::new(*character, self.depth + 1)))
.insert(sequence.into_iter().skip(1).collect(), value);
} else {
*self.value.borrow_mut() = Some(value);
};
}
pub fn goto(&self, character: char) -> Option<Rc<Self>> {
self.children.borrow().get(&character).map(Rc::clone)
}
pub fn take(&self) -> Option<String> {
self.value.borrow().as_ref().map(ToOwned::to_owned)
}
pub fn is_root(&self) -> bool {
self.depth == 0
}
}
#[derive(Clone)]
pub struct Cursor {
buffer: VecDeque<Rc<Node>>,
root: Rc<Node>,
}
impl fmt::Debug for Cursor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_sequence().fmt(f)
}
}
impl Cursor {
pub fn new(root: Rc<Node>, capacity: usize) -> Self {
Self {
buffer: VecDeque::with_capacity(capacity),
root,
}
}
pub fn hit(&mut self, character: char) -> Option<String> {
let node = self
.buffer
.iter()
.last()
.and_then(|node| node.goto(character))
.or_else(|| {
self.insert(Rc::new(Node::default()));
self.root.goto(character)
})
.unwrap_or_else(|| Rc::new(Node::new(character, 0)));
let out = node.take();
self.insert(node);
out
}
fn insert(&mut self, node: Rc<Node>) {
if self.buffer.len() == self.buffer.capacity() {
self.buffer.pop_front();
}
self.buffer.push_back(node);
}
pub fn undo(&mut self) -> Option<String> {
let node = self.buffer.pop_back();
node.and_then(|node| {
if node.key == '\0' {
self.undo()
} else {
node.take()
}
})
}
pub fn resume(&mut self) {
if self
.buffer
.iter()
.last()
.map_or(false, |node| node.is_root())
{
self.buffer.pop_back();
}
}
pub fn state(&self) -> (Option<String>, usize, char) {
self.buffer
.iter()
.last()
.map(|n| (n.take(), n.depth, n.key))
.unwrap_or_default()
}
pub fn to_sequence(&self) -> Vec<char> {
self.buffer.iter().map(|node| node.key).collect()
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn is_empty(&self) -> bool {
return self.buffer.iter().filter(|c| c.key != '\0').count() == 0;
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_node() {
use crate::Node;
let root = Node::default();
assert!(root.is_root());
root.insert(vec!['a', 'f'], "ɑ".to_owned());
root.insert(vec!['a', 'f', '1'], "ɑ̀".to_owned());
assert!(root.goto('a').is_some());
assert!(!root.goto('a').unwrap().is_root());
assert!(root.goto('b').is_none());
let node = root.goto('a').and_then(|e| e.goto('f'));
assert_eq!(node.as_ref().unwrap().take(), Some("ɑ".to_owned()));
let node = node.and_then(|e| e.goto('1'));
assert_eq!(node.as_ref().unwrap().take(), Some("ɑ̀".to_owned()));
}
#[test]
fn test_cursor() {
use crate::{utils, Cursor};
use std::rc::Rc;
macro_rules! hit {
( $cursor:ident $( $c:expr ),* ) => (
$( $cursor.hit($c); )*
);
}
macro_rules! undo {
( $cursor:ident $occ:expr ) => {
(0..$occ).into_iter().for_each(|_| {
$cursor.undo();
});
};
}
let data = include_str!("../data/sample.txt");
let root = utils::build_map(utils::load_data(data));
let mut cursor = Cursor::new(Rc::new(root), 8);
assert_eq!(cursor.state(), (None, 0, '\0'));
hit!(cursor '2', 'i', 'a', 'f');
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', 'f']);
assert_eq!(cursor.state(), (Some("íɑ́".to_owned()), 4, 'f'));
undo!(cursor 1);
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a']);
hit!(cursor 'x');
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', '\0', 'x']);
undo!(cursor 1);
cursor.resume();
hit!(cursor 'f');
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', 'f']);
undo!(cursor 2);
cursor.hit('e');
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'e']);
undo!(cursor 2);
hit!(cursor 'o', 'o');
assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'o', 'o']);
undo!(cursor 3);
assert_eq!(cursor.to_sequence(), vec!['\0']);
hit!(cursor '2', '2', 'u', 'a');
assert_eq!(
cursor.to_sequence(),
vec!['\0', '\0', '2', '\0', '2', 'u', 'a']
);
undo!(cursor 4);
assert_eq!(cursor.to_sequence(), vec!['\0', '\0']);
assert!(cursor.is_empty());
undo!(cursor 1);
assert_eq!(cursor.to_sequence(), vec![]);
hit!(
cursor
'a', 'a', '2', 'a', 'e', 'a', '2', 'f', 'a',
'2', '2', 'x', 'x', '2', 'i', 'a', '2', '2', '_', 'f',
'2', 'a', '2', 'a', '_'
);
assert_eq!(
cursor.to_sequence(),
vec!['f', '\0', '2', 'a', '\0', '2', 'a', '_']
);
assert_eq!(
format!("{:?}", cursor),
format!("{:?}", cursor.to_sequence())
);
cursor.clear();
assert_eq!(cursor.to_sequence(), vec![]);
}
}