use std::cell::RefCell;
use std::rc::{Rc, Weak};
use std::path::Path;
use serde::ser::{Serialize, Serializer, SerializeMap};
use serde::de::{Deserialize, Deserializer, Visitor, MapAccess, Error as deError};
pub struct HistoryTreeEntry {
label: String,
parent: RefCell<Weak<Self>>, last_visited_child: RefCell<Option<Weak<Self>>>,
children: RefCell<Vec<Rc<Self>>>,
}
impl HistoryTreeEntry {
pub fn new(label: &str) -> Self {
Self {
label: label.to_string(),
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
last_visited_child: RefCell::new(None),
}
}
pub fn last_visited_child_label(&self) -> Option<String> {
self.last_visited_child
.borrow()
.as_ref()
.and_then(|ptr| ptr.upgrade())
.map(|child| child.label.clone())
}
}
pub struct HistoryTree {
root: Rc<HistoryTreeEntry>,
current_entry: Rc<HistoryTreeEntry>,
}
impl HistoryTree {
pub fn current_entry(&self) -> &Rc<HistoryTreeEntry> {
&self.current_entry
}
pub fn from_abs_path<P: AsRef<Path>>(path: P) -> Self
{
let root = Rc::new(HistoryTreeEntry::new("/"));
let mut tree = Self {
root: Rc::clone(&root),
current_entry: root,
};
path.as_ref().components()
.skip(1) .for_each(|component| tree.visit(&component.as_os_str().to_string_lossy()));
tree
}
pub fn visit(&mut self, fname: &str) {
let found_child = self.current_entry.children.borrow().iter()
.find(|child| child.label == fname).cloned();
let child = found_child.unwrap_or_else(|| {
let child = HistoryTreeEntry::new(fname);
child.parent.replace(Rc::downgrade(&self.current_entry));
let child = Rc::new(child);
self.current_entry.children.borrow_mut().push(Rc::clone(&child));
child
});
self.current_entry.last_visited_child.replace(Some(Rc::downgrade(&child)));
self.current_entry = child;
}
#[allow(dead_code)] pub fn go_up(&mut self) {
let maybe_parent = self.current_entry.parent.borrow().upgrade();
if let Some(parent) = maybe_parent {
self.current_entry = Rc::clone(&parent);
} }
pub fn go_to_root(&mut self) {
self.current_entry = Rc::clone(&self.root);
}
pub fn change_dir<P: AsRef<Path>>(&mut self, abs_path: P) {
self.go_to_root();
for component in abs_path.as_ref().components().skip(1) {
self.visit(&component.as_os_str().to_string_lossy())
}
}
}
impl std::fmt::Debug for HistoryTreeEntry {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.debug_map()
.entry(&"parent", &self.parent.borrow().upgrade().map(|p| p.label.clone()).unwrap_or_default())
.entry(&"label", &self.label)
.entry(&"last_visited_child", &self.last_visited_child_label().unwrap_or_default())
.entry(&"children", &self.children.borrow())
.finish()
}
}
impl std::fmt::Debug for HistoryTree {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let mut initial_entry = Some(self.current_entry.clone());
let mut cur_parents = vec![];
while let Some(cur) = initial_entry {
cur_parents.push(cur.label.clone());
initial_entry = cur.parent.borrow().upgrade();
}
let cur_parents: std::path::PathBuf = cur_parents.iter().rev().collect();
f.debug_struct("HistoryTree")
.field("root", &self.root)
.field("current_entry", &cur_parents)
.finish()
}
}
impl Serialize for HistoryTreeEntry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
let mut map = serializer.serialize_map(Some(3))?;
map.serialize_entry("label", &self.label)?;
map.serialize_entry("last_visited_child", &self.last_visited_child_label())?;
map.serialize_entry("children", &*self.children.borrow())?;
map.end()
}
}
struct HistoryTreeEntryPtr(Rc<HistoryTreeEntry>);
impl<'de> Deserialize<'de> for HistoryTreeEntryPtr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
struct HistoryTreeEntryVisitor;
impl<'de> Visitor<'de> for HistoryTreeEntryVisitor {
type Value = HistoryTreeEntryPtr;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("valid history tree data")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>
{
let mut label: Option<String> = None;
let mut last_visited_child: Option<Option<String>> = None;
let mut children: Option<Vec<Self::Value>> = None;
while let Some(key) = access.next_key()? {
match key {
"label" => {
if label.is_some() {
return Err(deError::duplicate_field("label"));
}
label = Some(access.next_value()?);
},
"last_visited_child" => {
if last_visited_child.is_some() {
return Err(deError::duplicate_field("last_visited_child"));
}
let val: Option<String> = access.next_value()?;
last_visited_child = Some(val); },
"children" => {
if children.is_some() {
return Err(deError::duplicate_field("children"));
}
let val: Vec<Self::Value> = access.next_value()?;
children = Some(val);
},
k => return Err(deError::unknown_field(k, &["label", "last_visited_child", "children"])),
}
}
let label = label.ok_or_else(|| deError::missing_field("label"))?;
let children: Vec<Rc<HistoryTreeEntry>> = children
.ok_or_else(|| deError::missing_field("children"))?
.drain(..).map(|p| p.0).collect();
let last_visited_child = last_visited_child
.ok_or_else(|| deError::missing_field("last_visited_child"))?
.and_then(|label| children.iter().find(|c| c.label == label).map(Rc::downgrade));
let ret = HistoryTreeEntry {
label,
last_visited_child: RefCell::new(last_visited_child),
parent: RefCell::new(Weak::new()), children: RefCell::new(children),
};
let ret = Rc::new(ret);
for child in ret.children.borrow_mut().iter() {
child.parent.replace(Rc::downgrade(&ret));
}
Ok(HistoryTreeEntryPtr(ret))
}
}
deserializer.deserialize_map(HistoryTreeEntryVisitor)
}
}
impl Serialize for HistoryTree {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
self.root.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for HistoryTree {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
let root = HistoryTreeEntryPtr::deserialize(deserializer)?.0;
Ok(Self {
root: Rc::clone(&root),
current_entry: root,
})
}
}
#[cfg(test)]
mod tests_for_history_tree {
use super::*;
fn init_history_tree() -> HistoryTree {
let root = Rc::new(HistoryTreeEntry {
label: "/".to_string(),
parent: RefCell::new(Weak::new()),
last_visited_child: RefCell::new(None),
children: RefCell::new(vec![]),
});
HistoryTree {
root: Rc::clone(&root),
current_entry: root,
}
}
#[test]
fn test_history_tree_visit() {
let mut tree = init_history_tree();
tree.visit("foo");
assert_eq!(tree.current_entry().label, "foo");
assert_eq!(tree.current_entry().parent.borrow().upgrade().unwrap().label, "/");
tree.visit("bar");
assert_eq!(tree.current_entry().label, "bar");
assert_eq!(tree.current_entry().parent.borrow().upgrade().unwrap().label, "foo");
assert_eq!(tree.current_entry().parent.borrow().upgrade().unwrap().parent.borrow().upgrade().unwrap().label, "/");
}
#[test]
fn test_history_tree_go_up_down() {
let mut tree = init_history_tree();
tree.visit("foo");
tree.visit("bar");
tree.go_up();
assert_eq!(tree.current_entry().label, "foo");
assert_eq!(tree.current_entry().children.borrow()[0].label, "bar");
tree.go_up();
assert_eq!(tree.current_entry().label, "/");
assert_eq!(tree.current_entry().children.borrow()[0].label, "foo");
tree.go_up();
assert_eq!(tree.current_entry().label, "/");
assert_eq!(tree.current_entry().children.borrow()[0].label, "foo");
}
#[test]
fn test_tree_pointer_counts() {
let mut tree = init_history_tree();
tree.visit("foo");
let foo = Rc::downgrade(&tree.current_entry());
tree.visit("bar");
let bar = Rc::downgrade(&tree.current_entry());
assert_eq!(Rc::weak_count(&tree.root), 1);
assert_eq!(Weak::strong_count(&foo), 1); assert_eq!(Weak::weak_count(&foo), 3);
assert_eq!(Weak::strong_count(&bar), 2); assert_eq!(Weak::weak_count(&bar), 2);
tree.go_up(); tree.go_up();
assert_eq!(Weak::strong_count(&bar), 1); assert_eq!(Weak::weak_count(&bar), 2);
tree.visit("baz");
assert_eq!(Rc::weak_count(&tree.root), 2);
}
#[test]
fn test_last_visisted_child() {
let mut tree = init_history_tree();
tree.visit("foo");
let foo = Rc::clone(tree.current_entry());
tree.go_up();
assert!(Rc::ptr_eq(&foo, &tree.current_entry().last_visited_child.borrow().as_ref().unwrap().upgrade().unwrap()));
}
#[test]
fn test_go_to_root() {
let mut tree = init_history_tree();
let root = Rc::clone(tree.current_entry());
tree.visit("foo");
tree.visit("bar");
tree.visit("baz");
tree.go_to_root();
assert!(Rc::ptr_eq(&root, tree.current_entry()));
assert_eq!(tree.current_entry().label, "/");
}
#[test]
fn test_from_abs_path() {
let mut tree = HistoryTree::from_abs_path("/foo/bar/baz");
assert_eq!(tree.current_entry().label, "baz");
tree.go_to_root();
assert_eq!(tree.current_entry().last_visited_child_label().unwrap(), "foo");
tree.visit("foo");
assert_eq!(tree.current_entry().label, "foo");
assert_eq!(tree.current_entry().last_visited_child_label().unwrap(), "bar");
tree.visit("bar");
assert_eq!(tree.current_entry().label, "bar");
assert_eq!(tree.current_entry().last_visited_child_label().unwrap(), "baz");
tree.visit("baz");
assert_eq!(tree.current_entry().label, "baz");
assert_eq!(tree.current_entry().last_visited_child_label(), None);
}
#[test]
fn test_change_dir() {
let mut tree = HistoryTree::from_abs_path("/foo/bar/baz");
tree.change_dir("/foo/bax");
assert_eq!(tree.current_entry().label, "bax");
tree.go_up();
assert_eq!(
vec!["bar".to_string(), "bax".to_string()],
tree.current_entry().children.borrow().iter()
.map(|child| child.label.clone()).collect::<Vec<String>>()
);
tree.visit("bar");
assert_eq!(tree.current_entry().last_visited_child_label(), Some("baz".to_string()));
tree.change_dir("/");
assert!(Rc::ptr_eq(&tree.current_entry(), &tree.root));
tree.change_dir("/foo/bax");
}
#[test]
fn test_debug_print() {
let mut tree = HistoryTree::from_abs_path("/foo/bar/baz");
tree.change_dir("/foo/bar/boo");
tree.change_dir("/qux/zoo");
println!("{:#?}", tree);
}
#[test]
fn test_serialize() {
let mut tree = HistoryTree::from_abs_path("/foo/bar");
tree.change_dir("/foo/baz");
let ser = serde_json::to_string(&tree.root.as_ref()).unwrap();
assert_eq!(ser, r#"{"label":"/","last_visited_child":"foo","children":[{"label":"foo","last_visited_child":"baz","children":[{"label":"bar","last_visited_child":null,"children":[]},{"label":"baz","last_visited_child":null,"children":[]}]}]}"#);
}
#[test]
fn test_deserialize() {
let mut tree = HistoryTree::from_abs_path("/foo/bar");
tree.change_dir("/foo/baz");
println!("{:#?}", tree);
let ser = serde_json::to_string(&tree).unwrap();
println!("{}", ser); let tree2: HistoryTree = serde_json::from_str(&ser).unwrap();
println!("{:#?}", tree2);
assert_eq!(ser, serde_json::to_string(&tree2).unwrap());
}
}