diff --git a/src/nodes/accordion.rs b/src/nodes/accordion.rs index b0fe599..1bb6cc2 100644 --- a/src/nodes/accordion.rs +++ b/src/nodes/accordion.rs @@ -73,7 +73,7 @@ impl Display for Accordion { impl Node for Accordion { fn len(&self) -> usize { - let delimiter_len = if self.nodes.is_empty() { + let delimiter_len = if self.is_empty() { 0 } else { self.nodes.len() - 1 @@ -100,6 +100,10 @@ impl Branch for Accordion { fn get_outer_token_length(&self) -> usize { 8 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Deserializer for Accordion { @@ -190,5 +194,6 @@ mod test { fn empty_accordion() { let accordion = Accordion::default(); assert_eq!(accordion.len(), 8); + assert_eq!(accordion.is_empty(), true); } } diff --git a/src/nodes/accordion_tab.rs b/src/nodes/accordion_tab.rs index 34d1bd0..29864d4 100644 --- a/src/nodes/accordion_tab.rs +++ b/src/nodes/accordion_tab.rs @@ -150,7 +150,7 @@ impl Display for AccordionTab { impl Node for AccordionTab { fn len(&self) -> usize { - let delimeter_len = if self.nodes.is_empty() { + let delimeter_len = if self.is_empty() { 0 } else { (self.nodes.len() - 1) * 2 @@ -186,6 +186,10 @@ impl Branch for AccordionTab { fn get_outer_token_length(&self) -> usize { 6 + self.header.as_ref().map_or(0, |header| header.len() + 3) } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Deserializer for AccordionTab { @@ -214,7 +218,10 @@ mod cfg { list::ListTypes::*, list_item::ListItem, list_item_content::ListItemContent, paragraph::Paragraph, text::Text, }, - toolkit::{deserializer::Deserializer, node::Node}, + toolkit::{ + deserializer::{Branch, Deserializer}, + node::Node, + }, }; #[test] @@ -350,5 +357,6 @@ t**b** fn empty_tab() { let tab = AccordionTab::new::<&str>(None, vec![]); assert_eq!(tab.len(), 6); + assert_eq!(tab.is_empty(), true); } } diff --git a/src/nodes/bold.rs b/src/nodes/bold.rs index 5c91364..ef1d4b1 100644 --- a/src/nodes/bold.rs +++ b/src/nodes/bold.rs @@ -87,6 +87,10 @@ impl Branch for Bold { fn get_outer_token_length(&self) -> usize { 4 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Display for Bold { @@ -188,5 +192,6 @@ mod tests { fn empty_bold() { let b = Bold::new(vec![]); assert_eq!(b.len(), 4); + assert_eq!(b.is_empty(), true); } } diff --git a/src/nodes/highlight.rs b/src/nodes/highlight.rs index 2cb206f..00b202d 100644 --- a/src/nodes/highlight.rs +++ b/src/nodes/highlight.rs @@ -86,7 +86,7 @@ impl Display for Highlight { impl Node for Highlight { fn len(&self) -> usize { - let delimiter_length = if self.nodes.is_empty() { + let delimiter_length = if self.is_empty() { 0 } else { (self.nodes.len() - 1) * 2 @@ -114,6 +114,10 @@ impl Branch for Highlight { 8 + self.header.as_ref().map_or(0, |header| header.len() + 4) + self.icon.as_ref().map_or(0, |icon| icon.len() + 3) } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Deserializer for Highlight { @@ -138,7 +142,10 @@ impl Deserializer for Highlight { mod tests { use crate::{ nodes::{highlight::Highlight, paragraph::Paragraph, text::Text}, - toolkit::{deserializer::Deserializer, node::Node}, + toolkit::{ + deserializer::{Branch, Deserializer}, + node::Node, + }, }; use pretty_assertions::assert_eq; @@ -228,5 +235,6 @@ mod tests { fn empty_highlight() { let highlight = Highlight::new::(None, None, vec![]); assert_eq!(highlight.len(), 8); + assert_eq!(highlight.is_empty(), true); } } diff --git a/src/nodes/image_gallery.rs b/src/nodes/image_gallery.rs index b6d27a3..5c27052 100644 --- a/src/nodes/image_gallery.rs +++ b/src/nodes/image_gallery.rs @@ -74,7 +74,7 @@ impl Display for ImageGallery { impl Node for ImageGallery { fn len(&self) -> usize { - let delimiter_len = if self.nodes.is_empty() { + let delimiter_len = if self.is_empty() { 0 } else { self.nodes.len() - 1 @@ -111,6 +111,10 @@ impl Branch for ImageGallery { fn get_outer_token_length(&self) -> usize { 8 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } #[cfg(test)] @@ -118,7 +122,10 @@ mod tests { use super::ImageGallery; use crate::{ nodes::image::Image, - toolkit::{deserializer::Deserializer, node::Node}, + toolkit::{ + deserializer::{Branch, Deserializer}, + node::Node, + }, }; use pretty_assertions::assert_eq; @@ -176,5 +183,6 @@ mod tests { fn empty_gallery() { let gal = ImageGallery::new(vec![]); assert_eq!(gal.len(), 8); + assert_eq!(gal.is_empty(), true); } } diff --git a/src/nodes/list.rs b/src/nodes/list.rs index 87e206c..37cd926 100644 --- a/src/nodes/list.rs +++ b/src/nodes/list.rs @@ -100,7 +100,7 @@ impl Display for List { impl Node for List { fn len(&self) -> usize { - let add = if self.nodes.is_empty() { + let add = if self.is_empty() { 0 } else { self.nodes.len() - 1 @@ -154,6 +154,10 @@ impl Branch for List { fn get_outer_token_length(&self) -> usize { 0 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } #[cfg(test)] diff --git a/src/nodes/list_item_content.rs b/src/nodes/list_item_content.rs index eff50ea..186f7f6 100644 --- a/src/nodes/list_item_content.rs +++ b/src/nodes/list_item_content.rs @@ -146,6 +146,10 @@ impl Branch for ListItemContent { fn get_outer_token_length(&self) -> usize { 0 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Deserializer for ListItemContent { @@ -166,7 +170,10 @@ mod test { anchor::Anchor, bold::Bold, inline_code::InlineCode, italic::Italic, strikethrough::Strikethrough, text::Text, }, - toolkit::{deserializer::Deserializer, node::Node}, + toolkit::{ + deserializer::{Branch, Deserializer}, + node::Node, + }, }; use pretty_assertions::assert_eq; @@ -246,5 +253,6 @@ mod test { fn empty_list_item_content() { let list_item_content = ListItemContent::default(); assert_eq!(list_item_content.len(), 0); + assert_eq!(list_item_content.is_empty(), true); } } diff --git a/src/nodes/paragraph.rs b/src/nodes/paragraph.rs index 3224ea3..c98033f 100644 --- a/src/nodes/paragraph.rs +++ b/src/nodes/paragraph.rs @@ -119,6 +119,10 @@ impl Branch for Paragraph { fn get_outer_token_length(&self) -> usize { 0 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Default for Paragraph { fn default() -> Self { @@ -227,5 +231,6 @@ mod tests { #[test] fn len() { assert_eq!(Paragraph::default().len(), 0); + assert_eq!(Paragraph::default().is_empty(), true); } } diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index e1d24a3..e5b1d9b 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -165,6 +165,10 @@ impl Branch for Yamd { fn get_outer_token_length(&self) -> usize { 0 } + + fn is_empty(&self) -> bool { + self.nodes.is_empty() + } } impl Deserializer for Yamd { @@ -192,7 +196,7 @@ impl Display for Yamd { impl Node for Yamd { fn len(&self) -> usize { - let delimeter_len = if self.nodes.is_empty() { + let delimeter_len = if self.is_empty() { 0 } else { (self.nodes.len() - 1) * 2 @@ -499,5 +503,17 @@ end"#; fn empty_yamd() { let yamd = Yamd::new(None, vec![]); assert_eq!(yamd.len(), 0); + assert_eq!(yamd.is_empty(), true); + } + + #[test] + fn node_should_start_from_delimiter() { + let input = "text - text"; + let expected = Yamd::new( + None, + vec![Paragraph::new(vec![Text::new("text - text").into()]).into()], + ); + let actual = Yamd::deserialize(input).unwrap(); + assert_eq!(expected, actual); } } diff --git a/src/toolkit/deserializer.rs b/src/toolkit/deserializer.rs index eca985c..7c66671 100644 --- a/src/toolkit/deserializer.rs +++ b/src/toolkit/deserializer.rs @@ -10,36 +10,38 @@ where fn get_maybe_nodes() -> Vec>; fn get_fallback_node() -> Option>; fn get_outer_token_length(&self) -> usize; + fn is_empty(&self) -> bool; fn parse_branch(input: &str, delimeter: &str, mut branch: Self) -> Option where Self: Sized + Deserializer + Node, { let mut current_position = 0; + let mut next_position = 0; let mut fallback_position = 0; let maybe_nodes = Self::get_maybe_nodes(); while current_position < input.len() { let slice = &input[current_position..]; - let char_len = slice.chars().next().unwrap().len_utf8(); - current_position += char_len; - for parser in &maybe_nodes { - if let Some(node) = parser(slice, branch.context()) { - while fallback_position != current_position - char_len { - fallback_position = branch.fallback( - &input[fallback_position..current_position - char_len], - delimeter, - ); + next_position += slice.chars().next().unwrap().len_utf8(); + if delimeter.is_empty() || slice.starts_with(delimeter) || current_position == 0 { + let slice = if current_position == 0 { + slice + } else { + &slice[delimeter.len()..] + }; + for parser in &maybe_nodes { + if let Some(node) = parser(slice, branch.context()) { + while fallback_position != current_position { + fallback_position = branch + .fallback(&input[fallback_position..current_position], delimeter); + } + branch.push(node); + next_position = branch.len() - branch.get_outer_token_length(); + fallback_position = next_position; + break; } - branch.push(node); - current_position = branch.len() - branch.get_outer_token_length(); - fallback_position = current_position; - if !delimeter.is_empty() && input[current_position..].starts_with(delimeter) { - current_position += delimeter.len(); - fallback_position = current_position; - } - - break; } } + current_position = next_position; } while fallback_position < input.len() { if Self::get_fallback_node().is_none() { @@ -55,16 +57,19 @@ where where Self: Node, { - let node = Self::get_fallback_node() - .map(|f| f(slice)) - .expect("Fallback node should always be available"); - let node_len = node.len(); - self.push(node); - let mut fallback_position = self.len() - self.get_outer_token_length(); - if !delimeter.is_empty() && slice[node_len..].starts_with(delimeter) { - fallback_position += delimeter.len(); + let slice = if self.is_empty() { + slice + } else { + &slice[delimeter.len()..] + }; + if !slice.is_empty() { + self.push( + Self::get_fallback_node() + .map(|f| f(slice)) + .expect("Fallback node should always be available"), + ); } - fallback_position + self.len() - self.get_outer_token_length() } }