From 9be4d5169b5870d535293e0529e3ca1e2c68f728 Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Thu, 2 Nov 2023 23:20:58 +0100 Subject: [PATCH 1/4] update readme --- readme.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 6751370..461d11e 100644 --- a/readme.md +++ b/readme.md @@ -20,10 +20,14 @@ Deserialize markdown to YAMD struct, Serialize YAMD struct to markdown. ```rust use yamd::{deserialize, serialize}; -let input = r#"header: YAMD documnet showcase -timestamp: 2023-08-13 15:42:00 +02:00 -tags: yamd, markdown +let input = r#"--- +title: YAMD documnet showcase +date: 2023-08-13T15:42:00+02:00 preview: here is how you can serialize ande deserialize YAMD document +tags: +- yamd +- markdown +--- # This is a new Yamd document From 4cdaf65ca4c57ec6f5494995da2aea808aeb77c6 Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Fri, 3 Nov 2023 11:17:02 +0100 Subject: [PATCH 2/4] last delimiter --- src/nodes/yamd.rs | 14 ++++++++++++++ src/toolkit/deserializer.rs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index 850aa7d..19078cb 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -523,4 +523,18 @@ end"#; let actual = Yamd::deserialize(input).unwrap(); assert_eq!(expected, actual); } + + #[test] + fn last_delimiter() { + let input = "text\n\n"; + let expected = Yamd::new( + None, + vec![ + Paragraph::new(vec![Text::new("text").into()]).into(), + Paragraph::new(vec![]).into(), + ], + ); + let actual = Yamd::deserialize(input).unwrap(); + assert_eq!(expected, actual); + } } diff --git a/src/toolkit/deserializer.rs b/src/toolkit/deserializer.rs index 7c66671..b11099f 100644 --- a/src/toolkit/deserializer.rs +++ b/src/toolkit/deserializer.rs @@ -57,7 +57,7 @@ where where Self: Node, { - let slice = if self.is_empty() { + let slice = if self.is_empty() || slice.len() <= delimeter.len() { slice } else { &slice[delimeter.len()..] From 31ffbac045a544de9065e6111b060d2677e897d4 Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Fri, 3 Nov 2023 22:13:02 +0100 Subject: [PATCH 3/4] simplify highlight --- src/nodes/highlight.rs | 110 ++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/src/nodes/highlight.rs b/src/nodes/highlight.rs index 00b202d..1566d1f 100644 --- a/src/nodes/highlight.rs +++ b/src/nodes/highlight.rs @@ -2,55 +2,22 @@ use std::fmt::Display; use serde::Serialize; -use crate::toolkit::{ - context::Context, - deserializer::{Branch, DefinitelyNode, Deserializer, MaybeNode}, - matcher::Matcher, - node::Node, -}; +use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; use super::paragraph::Paragraph; -#[derive(Debug, PartialEq, Serialize, Clone)] -#[serde(tag = "type")] -pub enum HighlightNodes { - Paragraph(Paragraph), -} - -impl Display for HighlightNodes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - HighlightNodes::Paragraph(node) => write!(f, "{}", node), - } - } -} - -impl Node for HighlightNodes { - fn len(&self) -> usize { - match self { - HighlightNodes::Paragraph(node) => node.len(), - } - } -} - -impl From for HighlightNodes { - fn from(value: Paragraph) -> Self { - Self::Paragraph(value) - } -} - #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Highlight { pub header: Option, pub icon: Option, - pub nodes: Vec, + pub nodes: Vec, } impl Highlight { pub fn new, I: Into>( header: Option, icon: Option, - nodes: Vec, + nodes: Vec, ) -> Self { Self { header: header.map(|header| header.into()), @@ -86,38 +53,17 @@ impl Display for Highlight { impl Node for Highlight { fn len(&self) -> usize { - let delimiter_length = if self.is_empty() { + let delimiter_length = if self.nodes.len() == 0 { 0 } else { (self.nodes.len() - 1) * 2 }; self.nodes.iter().map(|node| node.len()).sum::() + delimiter_length - + self.get_outer_token_length() - } -} - -impl Branch for Highlight { - fn push>(&mut self, node: CanBeNode) { - self.nodes.push(node.into()); - } - - fn get_maybe_nodes() -> Vec> { - vec![Paragraph::maybe_node()] - } - - fn get_fallback_node() -> Option> { - None - } - - fn get_outer_token_length(&self) -> usize { - 8 + self.header.as_ref().map_or(0, |header| header.len() + 4) + + 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 { @@ -130,8 +76,17 @@ impl Deserializer for Highlight { .map(|header| header.body); let icon = matcher.get_match("> ", "\n", false).map(|icon| icon.body); - - return Self::parse_branch(matcher.get_rest(), "\n\n", Self::new(header, icon, vec![])); + return Some(Self::new( + header, + icon, + matcher + .get_rest() + .split("\n\n") + .map(|paragraph| { + Paragraph::deserialize(paragraph).expect("Paragraph always deserializes") + }) + .collect::>(), + )); } None @@ -142,10 +97,7 @@ impl Deserializer for Highlight { mod tests { use crate::{ nodes::{highlight::Highlight, paragraph::Paragraph, text::Text}, - toolkit::{ - deserializer::{Branch, Deserializer}, - node::Node, - }, + toolkit::{deserializer::Deserializer, node::Node}, }; use pretty_assertions::assert_eq; @@ -235,6 +187,30 @@ mod tests { fn empty_highlight() { let highlight = Highlight::new::(None, None, vec![]); assert_eq!(highlight.len(), 8); - assert_eq!(highlight.is_empty(), true); + } + + #[test] + fn starts_with_delimeter() { + let input = ">>> + + +test + +test2 +>>>"; + let highlight = Highlight::deserialize(input).unwrap(); + assert_eq!(highlight.len(), input.len()); + assert_eq!( + highlight, + Highlight::new::<&str, &str>( + None, + None, + vec![ + Paragraph::new(vec![]).into(), + Paragraph::new(vec![Text::new("test").into()]).into(), + Paragraph::new(vec![Text::new("test2").into()]).into(), + ] + ) + ); } } From d269f2e27e723ed670860a4b9727bca9b214fb4b Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Fri, 3 Nov 2023 23:45:08 +0100 Subject: [PATCH 4/4] deserialize metadata with quotes --- src/nodes/highlight.rs | 2 +- src/nodes/metadata.rs | 49 ++++++++++++++++++++++++++++++++---------- src/nodes/yamd.rs | 15 +++++++------ src/toolkit/matcher.rs | 6 ++++++ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/nodes/highlight.rs b/src/nodes/highlight.rs index 1566d1f..67806df 100644 --- a/src/nodes/highlight.rs +++ b/src/nodes/highlight.rs @@ -53,7 +53,7 @@ impl Display for Highlight { impl Node for Highlight { fn len(&self) -> usize { - let delimiter_length = if self.nodes.len() == 0 { + let delimiter_length = if self.nodes.is_empty() { 0 } else { (self.nodes.len() - 1) * 2 diff --git a/src/nodes/metadata.rs b/src/nodes/metadata.rs index 278d437..7505889 100644 --- a/src/nodes/metadata.rs +++ b/src/nodes/metadata.rs @@ -16,6 +16,8 @@ pub struct Metadata { pub preview: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option>, + #[serde(skip)] + pub consumed_length: Option, } impl Metadata { @@ -32,15 +34,17 @@ impl Metadata { image: image.map(|i| i.into()), preview: preview.map(|p| p.into()), tags, + consumed_length: None, } } pub fn deserialize(input: &str) -> Option { let mut matcher = Matcher::new(input); if let Some(metadata) = matcher.get_match("---\n", "---", false) { - let meta: Metadata = serde_yaml::from_str(metadata.body).unwrap_or_else(|e| { + let mut meta: Metadata = serde_yaml::from_str(metadata.body).unwrap_or_else(|e| { panic!("Failed to deserialize metadata: {}\n{}\n", metadata.body, e) }); + meta.consumed_length = Some(metadata.len()); return Some(meta); } @@ -65,7 +69,8 @@ impl Display for Metadata { impl Node for Metadata { fn len(&self) -> usize { - self.to_string().len() + self.consumed_length + .unwrap_or_else(|| self.to_string().len()) } } @@ -124,16 +129,17 @@ mod tests { #[test] fn test_deserialize() { - let metadata = Metadata::new( - Some("title"), - Some( + let metadata = Metadata { + title: Some("title".to_string()), + date: Some( DateTime::parse_from_str("2022-01-01 00:00:00 +02:00", "%Y-%m-%d %H:%M:%S %z") .unwrap(), ), - Some("image"), - Some("preview"), - Some(vec!["tag1".to_string(), "tag2".to_string()]), - ); + image: Some("image".to_string()), + preview: Some("preview".to_string()), + tags: Some(vec!["tag1".to_string(), "tag2".to_string()]), + consumed_length: Some(102), + }; assert_eq!( Metadata::deserialize(metadata.to_string().as_str()), Some(metadata) @@ -144,7 +150,14 @@ mod tests { fn deserialize_empty() { assert_eq!( Metadata::deserialize("---\n---"), - Some(Metadata::new::<&str>(None, None, None, None, None)) + Some(Metadata { + title: None, + date: None, + image: None, + preview: None, + tags: None, + consumed_length: Some(7) + }) ); } @@ -157,7 +170,14 @@ mod tests { fn deserialize_only_with_title() { assert_eq!( Metadata::deserialize("---\ntitle: header\n---"), - Some(Metadata::new(Some("header"), None, None, None, None)) + Some(Metadata { + title: Some("header".to_string()), + preview: None, + date: None, + image: None, + tags: None, + consumed_length: Some(21) + }) ); } @@ -170,4 +190,11 @@ mod tests { assert_eq!(Metadata::default().to_string(), ""); assert_eq!(Metadata::default().len(), 0); } + + #[test] + fn deserialize_with_quotes() { + let input = "---\ntitle: \"header\"\n---"; + let m = Metadata::deserialize(input); + assert_eq!(input.len(), m.unwrap().len()); + } } diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index 19078cb..e5ed5b4 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -321,19 +321,20 @@ end"#; assert_eq!( Yamd::deserialize(TEST_CASE), Some(Yamd::new( - Some(Metadata::new( - Some("test"), - Some( + Some(Metadata { + title: Some("test".to_string()), + date: Some( DateTime::parse_from_str( "2022-01-01 00:00:00 +02:00", "%Y-%m-%d %H:%M:%S %z" ) .unwrap() ), - Some("image"), - Some("preview"), - Some(vec!["tag1".to_string(), "tag2".to_string()]), - )), + image: Some("image".to_string()), + preview: Some("preview".to_string()), + tags: Some(vec!["tag1".to_string(), "tag2".to_string()]), + consumed_length: Some(101), + }), vec![ Heading::new("hello", 1).into(), Code::new("rust", "let a=1;").into(), diff --git a/src/toolkit/matcher.rs b/src/toolkit/matcher.rs index ac35017..b08264c 100644 --- a/src/toolkit/matcher.rs +++ b/src/toolkit/matcher.rs @@ -10,6 +10,12 @@ pub struct Match<'input> { pub end_token: &'input str, } +impl Match<'_> { + pub fn len(&self) -> usize { + self.start_token.len() + self.body.len() + self.end_token.len() + } +} + impl<'input> Matcher<'input> { pub fn new(input: &'input str) -> Self { Self { input, position: 0 }