diff --git a/crates/service/service-webpage/src/ast/mod.rs b/crates/service/service-webpage/src/ast/mod.rs deleted file mode 100644 index ff912a6..0000000 --- a/crates/service/service-webpage/src/ast/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod walk; -pub use walk::*; - -mod walk_mut; -pub use walk_mut::*; diff --git a/crates/service/service-webpage/src/ast/walk.rs b/crates/service/service-webpage/src/ast/walk.rs deleted file mode 100644 index b9cbe28..0000000 --- a/crates/service/service-webpage/src/ast/walk.rs +++ /dev/null @@ -1,384 +0,0 @@ -use markdown::mdast::Node; -use std::fmt::Debug; -use std::marker::PhantomPinned; - -pub enum AstWalkStep<'a, T> { - Enter(&'a T), - Exit(&'a T), -} - -impl Debug for AstWalkStep<'_, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Enter(x) => f.debug_tuple("AstWalkStep::Enter").field(x).finish(), - Self::Exit(x) => f.debug_tuple("AstWalkStep::Exit").field(x).finish(), - } - } -} - -pub struct AstWalk<'a> { - _pin: PhantomPinned, - child_stack: Vec, - node_stack: Vec<&'a Node>, -} - -impl<'a> AstWalk<'a> { - pub fn new(root: &'a Node) -> Self { - let mut res = Self { - _pin: PhantomPinned {}, - node_stack: Vec::with_capacity(32), - child_stack: Vec::with_capacity(32), - }; - - res.node_stack.push(root); - return res; - } - - fn _next_inner(&mut self) -> Option> { - if self.node_stack.is_empty() { - return None; - } - - let current_node = *self.node_stack.last().unwrap(); - - // The index of the next child we should look at. - // If `None`, we look at the parent. - let current_child = { - let n_nodes = self.node_stack.len(); - let n_childs = self.child_stack.len(); - match n_nodes - n_childs { - 2.. => unreachable!(), - 1 => None, - 0 => Some(self.child_stack.pop().unwrap()), - } - }; - - match current_child { - None => { - self.child_stack.push(0); - return Some(AstWalkStep::Enter(current_node)); - } - - Some(current_child) => { - let child = current_node - .children() - .map(|x| x.get(current_child)) - .flatten(); - - match child { - None => { - self.node_stack.pop(); - return Some(AstWalkStep::Exit(current_node)); - } - - Some(x) => { - self.child_stack.push(current_child + 1); - self.node_stack.push(&x); - self.child_stack.push(0); - return Some(AstWalkStep::Enter(x)); - } - } - } - } - } -} - -impl<'a> Iterator for AstWalk<'a> { - type Item = AstWalkStep<'a, Node>; - - fn next(self: &mut Self) -> Option { - return self._next_inner(); - } -} - -// -// MARK: tests -// - -#[cfg(test)] -mod tests { - use super::*; - use markdown::mdast::{Emphasis, Paragraph, Root, Strong, Text}; - - #[test] - fn single_leaf() { - let node = Node::Text(Text { - value: "Hello".to_string(), - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - assert_eq!(steps.len(), 2); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Exit(_))); - } - - #[test] - fn single_child() { - let node = Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "Hello".to_string(), - position: None, - })], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Leaf(Text), Exit(Paragraph) - assert_eq!(steps.len(), 4); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Exit(_))); - assert!(matches!(steps[3], AstWalkStep::Exit(_))); - } - - #[test] - fn multiple_children() { - let node = Node::Paragraph(Paragraph { - children: vec![ - Node::Text(Text { - value: "Hello".to_string(), - position: None, - }), - Node::Text(Text { - value: " ".to_string(), - position: None, - }), - Node::Text(Text { - value: "World".to_string(), - position: None, - }), - ], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Leaf(Text1), Leaf(Text2), Leaf(Text3), Exit(Paragraph) - assert_eq!(steps.len(), 8); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Exit(_))); - assert!(matches!(steps[3], AstWalkStep::Enter(_))); - assert!(matches!(steps[4], AstWalkStep::Exit(_))); - assert!(matches!(steps[5], AstWalkStep::Enter(_))); - assert!(matches!(steps[6], AstWalkStep::Exit(_))); - assert!(matches!(steps[7], AstWalkStep::Exit(_))); - } - - #[test] - fn nested_1() { - let node = Node::Paragraph(Paragraph { - children: vec![Node::Emphasis(Emphasis { - children: vec![Node::Text(Text { - value: "emphasized".to_string(), - position: None, - })], - position: None, - })], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Enter(Emphasis), Leaf(Text), Exit(Emphasis), Exit(Paragraph) - assert_eq!(steps.len(), 6); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Enter(_))); - assert!(matches!(steps[3], AstWalkStep::Exit(_))); - assert!(matches!(steps[4], AstWalkStep::Exit(_))); - assert!(matches!(steps[5], AstWalkStep::Exit(_))); - } - - #[test] - fn nested_2() { - // Create: Paragraph -> [Text, Strong -> Text, Text] - let node = Node::Paragraph(Paragraph { - children: vec![ - Node::Text(Text { - value: "Before ".to_string(), - position: None, - }), - Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "bold".to_string(), - position: None, - })], - position: None, - }), - Node::Text(Text { - value: " after".to_string(), - position: None, - }), - ], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Expected order: - // 0: Enter(Paragraph) - // 1: Leaf(Text "Before ") - // 2: Enter(Strong) - // 3: Leaf(Text "bold") - // 4: Exit(Strong) - // 5: Leaf(Text " after") - // 6: Exit(Paragraph) - assert_eq!(steps.len(), 10); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Exit(_))); - assert!(matches!(steps[3], AstWalkStep::Enter(_))); - assert!(matches!(steps[4], AstWalkStep::Enter(_))); - assert!(matches!(steps[5], AstWalkStep::Exit(_))); - assert!(matches!(steps[6], AstWalkStep::Exit(_))); - assert!(matches!(steps[7], AstWalkStep::Enter(_))); - assert!(matches!(steps[8], AstWalkStep::Exit(_))); - assert!(matches!(steps[9], AstWalkStep::Exit(_))); - } - - #[test] - fn nested_3() { - let node = Node::Paragraph(Paragraph { - children: vec![Node::Emphasis(Emphasis { - children: vec![Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "deeply nested".to_string(), - position: None, - })], - position: None, - })], - position: None, - })], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Enter(Emphasis), Enter(Strong), Leaf(Text), Exit(Strong), Exit(Emphasis), Exit(Paragraph) - assert_eq!(steps.len(), 8); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Enter(_))); - assert!(matches!(steps[3], AstWalkStep::Enter(_))); - assert!(matches!(steps[4], AstWalkStep::Exit(_))); - assert!(matches!(steps[5], AstWalkStep::Exit(_))); - assert!(matches!(steps[6], AstWalkStep::Exit(_))); - assert!(matches!(steps[7], AstWalkStep::Exit(_))); - } - - #[test] - fn empty_parent() { - let node = Node::Paragraph(Paragraph { - children: vec![], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Exit(Paragraph) - assert_eq!(steps.len(), 2); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Exit(_))); - } - - #[test] - fn multiple_paragraphs() { - let node = Node::Root(Root { - children: vec![ - Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "First".to_string(), - position: None, - })], - position: None, - }), - Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "Second".to_string(), - position: None, - })], - position: None, - }), - ], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - // Expected order: - // 0: Enter(Root) - // 1: Enter(Paragraph) - // 2: Enter(Text "First") - // 2: Exit(Text "First") - // 3: Exit(Paragraph) - // 4: Enter(Paragraph) - // 5: Enter(Text "Second") - // 5: Exit(Text "Second") - // 6: Exit(Paragraph) - // 7: Exit(Root) - assert_eq!(steps.len(), 10); - assert!(matches!(steps[0], AstWalkStep::Enter(_))); - assert!(matches!(steps[1], AstWalkStep::Enter(_))); - assert!(matches!(steps[2], AstWalkStep::Enter(_))); - assert!(matches!(steps[3], AstWalkStep::Exit(_))); - assert!(matches!(steps[4], AstWalkStep::Exit(_))); - assert!(matches!(steps[5], AstWalkStep::Enter(_))); - assert!(matches!(steps[6], AstWalkStep::Enter(_))); - assert!(matches!(steps[7], AstWalkStep::Exit(_))); - assert!(matches!(steps[8], AstWalkStep::Exit(_))); - assert!(matches!(steps[9], AstWalkStep::Exit(_))); - } - - #[test] - fn enter_exit() { - let node = Node::Root(Root { - children: vec![Node::Paragraph(Paragraph { - children: vec![ - Node::Emphasis(Emphasis { - children: vec![Node::Text(Text { - value: "a".to_string(), - position: None, - })], - position: None, - }), - Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "b".to_string(), - position: None, - })], - position: None, - }), - ], - position: None, - })], - position: None, - }); - - let walker = AstWalk::new(&node); - let steps: Vec<_> = walker.collect(); - - let enter_count = steps - .iter() - .filter(|s| matches!(s, AstWalkStep::Enter(_))) - .count(); - let exit_count = steps - .iter() - .filter(|s| matches!(s, AstWalkStep::Exit(_))) - .count(); - - assert_eq!(enter_count, exit_count); - assert_eq!(enter_count, 6); - } -} diff --git a/crates/service/service-webpage/src/ast/walk_mut.rs b/crates/service/service-webpage/src/ast/walk_mut.rs deleted file mode 100644 index 0740902..0000000 --- a/crates/service/service-webpage/src/ast/walk_mut.rs +++ /dev/null @@ -1,378 +0,0 @@ -use markdown::mdast::Node; -use std::{fmt::Debug, marker::PhantomData}; - -pub enum AstWalkMutStep<'a, T> { - Enter(&'a T), - Exit(&'a mut T), -} - -impl Debug for AstWalkMutStep<'_, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Enter(x) => f.debug_tuple("AstWalkMutStep::Enter").field(x).finish(), - Self::Exit(x) => f.debug_tuple("AstWalkMutStep::Exit").field(x).finish(), - } - } -} - -pub struct AstWalkMut<'a> { - _life: PhantomData<&'a mut Node>, - child_stack: Vec, - node_stack: Vec<*mut Node>, -} - -impl<'a> AstWalkMut<'a> { - pub fn new(root: &'a mut Node) -> Self { - let mut res = Self { - _life: PhantomData, - node_stack: Vec::with_capacity(32), - child_stack: Vec::with_capacity(32), - }; - - res.node_stack.push(root); - return res; - } - - fn _next_inner(&mut self) -> Option> { - if self.node_stack.is_empty() { - return None; - } - - let current_node = unsafe { &mut **self.node_stack.last().unwrap_unchecked() }; - - // The index of the next child we should look at. - // If `None`, we look at the parent. - let current_child = { - let n_nodes = self.node_stack.len(); - let n_childs = self.child_stack.len(); - match n_nodes - n_childs { - 2.. => unreachable!(), - 1 => None, - 0 => Some(self.child_stack.pop().unwrap()), - } - }; - - match current_child { - None => { - self.child_stack.push(0); - return Some(AstWalkMutStep::Enter(current_node)); - } - - Some(current_child) => { - if current_node.children().is_none() - || current_child >= current_node.children().unwrap().len() - { - self.node_stack.pop(); - return Some(AstWalkMutStep::Exit(current_node)); - } - - let child = &mut current_node.children_mut().unwrap()[current_child]; - - self.child_stack.push(current_child + 1); - self.node_stack.push(child); - self.child_stack.push(0); - return Some(AstWalkMutStep::Enter(child)); - } - } - } -} - -impl<'a> Iterator for AstWalkMut<'a> { - type Item = AstWalkMutStep<'a, Node>; - - fn next(self: &mut Self) -> Option { - return self._next_inner(); - } -} - -// -// MARK: tests -// - -#[cfg(test)] -mod tests { - use super::*; - use markdown::mdast::{Emphasis, Paragraph, Root, Strong, Text}; - - #[test] - fn single_leaf() { - let mut node = Node::Text(Text { - value: "Hello".to_string(), - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - assert_eq!(steps.len(), 2); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Exit(_))); - } - - #[test] - fn single_child() { - let mut node = Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "Hello".to_string(), - position: None, - })], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Leaf(Text), Exit(Paragraph) - assert_eq!(steps.len(), 4); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[3], AstWalkMutStep::Exit(_))); - } - - #[test] - fn multiple_children() { - let mut node = Node::Paragraph(Paragraph { - children: vec![ - Node::Text(Text { - value: "Hello".to_string(), - position: None, - }), - Node::Text(Text { - value: " ".to_string(), - position: None, - }), - Node::Text(Text { - value: "World".to_string(), - position: None, - }), - ], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Leaf(Text1), Leaf(Text2), Leaf(Text3), Exit(Paragraph) - assert_eq!(steps.len(), 8); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[3], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[4], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[5], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[6], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[7], AstWalkMutStep::Exit(_))); - } - - #[test] - fn nested_1() { - let mut node = Node::Paragraph(Paragraph { - children: vec![Node::Emphasis(Emphasis { - children: vec![Node::Text(Text { - value: "emphasized".to_string(), - position: None, - })], - position: None, - })], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Enter(Emphasis), Leaf(Text), Exit(Emphasis), Exit(Paragraph) - assert_eq!(steps.len(), 6); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[3], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[4], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[5], AstWalkMutStep::Exit(_))); - } - - #[test] - fn nested_2() { - // Create: Paragraph -> [Text, Strong -> Text, Text] - let mut node = Node::Paragraph(Paragraph { - children: vec![ - Node::Text(Text { - value: "Before ".to_string(), - position: None, - }), - Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "bold".to_string(), - position: None, - })], - position: None, - }), - Node::Text(Text { - value: " after".to_string(), - position: None, - }), - ], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Expected order: - // 0: Enter(Paragraph) - // 1: Leaf(Text "Before ") - // 2: Enter(Strong) - // 3: Leaf(Text "bold") - // 4: Exit(Strong) - // 5: Leaf(Text " after") - // 6: Exit(Paragraph) - assert_eq!(steps.len(), 10); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[3], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[4], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[5], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[6], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[7], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[8], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[9], AstWalkMutStep::Exit(_))); - } - - #[test] - fn nested_3() { - let mut node = Node::Paragraph(Paragraph { - children: vec![Node::Emphasis(Emphasis { - children: vec![Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "deeply nested".to_string(), - position: None, - })], - position: None, - })], - position: None, - })], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Enter(Emphasis), Enter(Strong), Leaf(Text), Exit(Strong), Exit(Emphasis), Exit(Paragraph) - assert_eq!(steps.len(), 8); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[3], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[4], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[5], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[6], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[7], AstWalkMutStep::Exit(_))); - } - - #[test] - fn empty_parent() { - let mut node = Node::Paragraph(Paragraph { - children: vec![], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Should be: Enter(Paragraph), Exit(Paragraph) - assert_eq!(steps.len(), 2); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Exit(_))); - } - - #[test] - fn multiple_paragraphs() { - let mut node = Node::Root(Root { - children: vec![ - Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "First".to_string(), - position: None, - })], - position: None, - }), - Node::Paragraph(Paragraph { - children: vec![Node::Text(Text { - value: "Second".to_string(), - position: None, - })], - position: None, - }), - ], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - // Expected order: - // 0: Enter(Root) - // 1: Enter(Paragraph) - // 2: Enter(Text "First") - // 2: Exit(Text "First") - // 3: Exit(Paragraph) - // 4: Enter(Paragraph) - // 5: Enter(Text "Second") - // 5: Exit(Text "Second") - // 6: Exit(Paragraph) - // 7: Exit(Root) - assert_eq!(steps.len(), 10); - assert!(matches!(steps[0], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[1], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[2], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[3], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[4], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[5], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[6], AstWalkMutStep::Enter(_))); - assert!(matches!(steps[7], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[8], AstWalkMutStep::Exit(_))); - assert!(matches!(steps[9], AstWalkMutStep::Exit(_))); - } - - #[test] - fn enter_exit() { - let mut node = Node::Root(Root { - children: vec![Node::Paragraph(Paragraph { - children: vec![ - Node::Emphasis(Emphasis { - children: vec![Node::Text(Text { - value: "a".to_string(), - position: None, - })], - position: None, - }), - Node::Strong(Strong { - children: vec![Node::Text(Text { - value: "b".to_string(), - position: None, - })], - position: None, - }), - ], - position: None, - })], - position: None, - }); - - let walker = AstWalkMut::new(&mut node); - let steps: Vec<_> = walker.collect(); - - let enter_count = steps - .iter() - .filter(|s| matches!(s, AstWalkMutStep::Enter(_))) - .count(); - let exit_count = steps - .iter() - .filter(|s| matches!(s, AstWalkMutStep::Exit(_))) - .count(); - - assert_eq!(enter_count, exit_count); - assert_eq!(enter_count, 6); - } -} diff --git a/crates/service/service-webpage/src/lib.rs b/crates/service/service-webpage/src/lib.rs index ad419a2..7368703 100644 --- a/crates/service/service-webpage/src/lib.rs +++ b/crates/service/service-webpage/src/lib.rs @@ -2,7 +2,6 @@ use axum::Router; use libservice::ToService; use utoipa::OpenApi; -mod ast; mod components; mod routes;