use crate::Node;
use std::fmt::Debug;
#[derive(Clone, Debug, PartialEq)]
pub struct Zipper<NS, TAG, ATT, VAL>
where
NS: PartialEq + Clone + Debug,
TAG: PartialEq + Clone + Debug,
ATT: PartialEq + Clone + Debug,
VAL: PartialEq + Clone + Debug,
{
node: Node<NS, TAG, ATT, VAL>,
parent: Option<Box<Zipper<NS, TAG, ATT, VAL>>>,
index_in_parent: usize,
}
impl<NS, TAG, ATT, VAL> Zipper<NS, TAG, ATT, VAL>
where
NS: PartialEq + Clone + Debug,
TAG: PartialEq + Clone + Debug,
ATT: PartialEq + Clone + Debug,
VAL: PartialEq + Clone + Debug,
{
pub fn child(mut self, index: usize) -> Option<Zipper<NS, TAG, ATT, VAL>> {
if let Some(children) = self.node.get_children() {
if children.get(index).is_some() {
let child = self.node.swap_remove_child(index);
Some(Zipper {
node: child,
parent: Some(Box::new(self)),
index_in_parent: index,
})
} else {
None
}
} else {
None
}
}
pub fn parent(self) -> Zipper<NS, TAG, ATT, VAL> {
let Zipper {
node,
parent,
index_in_parent,
} = self;
let Zipper {
node: mut parent_node,
parent: parent_parent,
index_in_parent: parent_index_in_parent,
} = *parent.unwrap();
parent_node.add_children_ref_mut(vec![node]);
let len = parent_node.get_children_count();
parent_node.swap_children(index_in_parent, len - 1);
Zipper {
node: parent_node,
parent: parent_parent,
index_in_parent: parent_index_in_parent,
}
}
pub fn finish(mut self) -> Node<NS, TAG, ATT, VAL> {
while let Some(_) = self.parent {
self = self.parent();
}
self.node
}
}
impl<NS, TAG, ATT, VAL> Node<NS, TAG, ATT, VAL>
where
NS: PartialEq + Clone + Debug,
TAG: PartialEq + Clone + Debug,
ATT: PartialEq + Clone + Debug,
VAL: PartialEq + Clone + Debug,
{
pub fn zipper(self) -> Zipper<NS, TAG, ATT, VAL> {
Zipper {
node: self,
parent: None,
index_in_parent: 0,
}
}
}
#[allow(unused)]
fn zipper_traverse_node<NS, TAG, ATT, VAL>(
node: Node<NS, TAG, ATT, VAL>,
path: &mut Vec<usize>,
) -> Option<Node<NS, TAG, ATT, VAL>>
where
NS: PartialEq + Clone + Debug,
TAG: PartialEq + Clone + Debug,
ATT: PartialEq + Clone + Debug,
VAL: PartialEq + Clone + Debug,
{
if path.is_empty() {
Some(node)
} else {
let zipper = node.zipper();
let idx = path.remove(0);
if let Some(child_zipper) = zipper.child(idx) {
zipper_traverse_node(child_zipper.node, path)
} else {
None
}
}
}
#[allow(unused)]
fn find_node_by_zipper<NS, TAG, ATT, VAL>(
node: Node<NS, TAG, ATT, VAL>,
path: &[usize],
) -> Option<Node<NS, TAG, ATT, VAL>>
where
NS: PartialEq + Clone + Debug,
TAG: PartialEq + Clone + Debug,
ATT: PartialEq + Clone + Debug,
VAL: PartialEq + Clone + Debug,
{
let mut path = path.to_vec();
let root_idx = path.remove(0); assert_eq!(0, root_idx, "path must start with 0");
let node = node.clone();
zipper_traverse_node(node, &mut path)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
type MyNode = Node<&'static str, &'static str, &'static str, &'static str>;
fn sample_node() -> MyNode {
let node: MyNode = element(
"div",
vec![attr("class", "[0]"), attr("id", "0")],
vec![
element(
"div",
vec![attr("class", "[0,0]"), attr("id", "1")],
vec![
element(
"div",
vec![attr("class", "[0,0,0]"), attr("id", "2")],
vec![],
),
element(
"div",
vec![attr("class", "[0,0,1]"), attr("id", "3")],
vec![],
),
],
),
element(
"div",
vec![attr("class", "[0,1]"), attr("id", "4")],
vec![
element(
"div",
vec![attr("class", "[0,1,0]"), attr("id", "5")],
vec![],
),
element(
"div",
vec![attr("class", "[0,1,1]"), attr("id", "6")],
vec![],
),
element(
"div",
vec![attr("class", "[0,1,2]"), attr("id", "7")],
vec![],
),
],
),
],
);
node
}
#[test]
fn should_traverse_correctly() {
let node = sample_node();
let expected = element(
"div",
vec![attr("class", "[0,0]"), attr("id", "1")],
vec![
element(
"div",
vec![attr("class", "[0,0,0]"), attr("id", "2")],
vec![],
),
element(
"div",
vec![attr("class", "[0,0,1]"), attr("id", "3")],
vec![],
),
],
);
assert_eq!(Some(expected), find_node_by_zipper(node, &[0, 0]));
}
#[test]
fn should_find_root_node() {
let node = sample_node();
let root = find_node_by_zipper(node.clone(), &[0]);
dbg!(&root);
assert_eq!(Some(node), root);
}
#[test]
fn should_find_node1() {
let node = sample_node();
let found = find_node_by_zipper(node, &[0, 0]);
dbg!(&found);
let expected = element(
"div",
vec![attr("class", "[0,0]"), attr("id", "1")],
vec![
element(
"div",
vec![attr("class", "[0,0,0]"), attr("id", "2")],
vec![],
),
element(
"div",
vec![attr("class", "[0,0,1]"), attr("id", "3")],
vec![],
),
],
);
assert_eq!(Some(expected), found);
}
#[test]
fn should_find_node2() {
let node = sample_node();
let found = find_node_by_zipper(node, &[0, 0, 0]);
dbg!(&found);
let expected = element(
"div",
vec![attr("class", "[0,0,0]"), attr("id", "2")],
vec![],
);
assert_eq!(Some(expected), found);
}
#[test]
fn should_find_node3() {
let node = sample_node();
let found = find_node_by_zipper(node, &[0, 0, 1]);
dbg!(&found);
let expected = element(
"div",
vec![attr("class", "[0,0,1]"), attr("id", "3")],
vec![],
);
assert_eq!(Some(expected), found);
}
#[test]
fn should_find_node4() {
let node = sample_node();
let node4 = find_node_by_zipper(node, &[0, 1]);
dbg!(&node4);
let expected = element(
"div",
vec![attr("class", "[0,1]"), attr("id", "4")],
vec![
element(
"div",
vec![attr("class", "[0,1,0]"), attr("id", "5")],
vec![],
),
element(
"div",
vec![attr("class", "[0,1,1]"), attr("id", "6")],
vec![],
),
element(
"div",
vec![attr("class", "[0,1,2]"), attr("id", "7")],
vec![],
),
],
);
assert_eq!(Some(expected), node4);
}
#[test]
fn should_find_node5() {
let node = sample_node();
let node5 = find_node_by_zipper(node, &[0, 1, 0]);
dbg!(&node5);
let expected = element(
"div",
vec![attr("class", "[0,1,0]"), attr("id", "5")],
vec![],
);
assert_eq!(Some(expected), node5);
}
#[test]
fn should_find_node6() {
let node = sample_node();
let node6 = find_node_by_zipper(node, &[0, 1, 1]);
dbg!(&node6);
let expected = element(
"div",
vec![attr("class", "[0,1,1]"), attr("id", "6")],
vec![],
);
assert_eq!(Some(expected), node6);
}
#[test]
fn should_find_node7() {
let node = sample_node();
let node7 = find_node_by_zipper(node, &[0, 1, 2]);
dbg!(&node7);
let expected = element(
"div",
vec![attr("class", "[0,1,2]"), attr("id", "7")],
vec![],
);
assert_eq!(Some(expected), node7);
}
#[test]
fn should_find_none_in_013() {
let node = sample_node();
let found = find_node_by_zipper(node, &[0, 1, 3]);
dbg!(&found);
assert_eq!(None, found);
}
#[test]
fn should_find_none_in_00000() {
let node = sample_node();
let found = find_node_by_zipper(node, &[0, 0, 0, 0]);
dbg!(&found);
assert_eq!(None, found);
}
#[test]
fn should_find_none_007() {
let node = sample_node();
assert_eq!(None, find_node_by_zipper(node, &[0, 0, 7]));
}
}