From 5ad1bb1f1d6aaafc34b4cf3dda3eb02fdebd9769 Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Sun, 8 Oct 2023 20:11:05 +0200 Subject: [PATCH 1/3] derive serde::Serialize, implement Display for Yamd --- Cargo.toml | 3 +- src/lib.rs | 4 +- src/nodes/accordion.rs | 27 ++++++++---- src/nodes/accordion_tab.rs | 49 +++++++++++++--------- src/nodes/anchor.rs | 17 +++++--- src/nodes/bold.rs | 36 ++++++++++------ src/nodes/cloudinary_image_gallery.rs | 22 +++++----- src/nodes/code.rs | 19 ++++++--- src/nodes/divider.rs | 18 +++++--- src/nodes/embed.rs | 18 +++++--- src/nodes/heading.rs | 27 +++++++----- src/nodes/highlight.rs | 38 +++++++++++------ src/nodes/image.rs | 20 ++++++--- src/nodes/image_gallery.rs | 33 ++++++++++----- src/nodes/inline_code.rs | 19 ++++++--- src/nodes/italic.rs | 17 +++++--- src/nodes/list.rs | 37 +++++++++++------ src/nodes/list_item.rs | 25 +++++++---- src/nodes/list_item_content.rs | 47 ++++++++++++--------- src/nodes/metadata.rs | 38 +++++++---------- src/nodes/paragraph.rs | 50 ++++++++++++---------- src/nodes/strikethrough.rs | 17 +++++--- src/nodes/text.rs | 19 ++++++--- src/nodes/yamd.rs | 60 ++++++++++++++++----------- src/toolkit/node.rs | 1 - 25 files changed, 410 insertions(+), 251 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d69a2c..4f087a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ keywords = ["markdown", "parser"] pretty_assertions = "1.4.0" [dependencies] -chrono = "0.4.26" +serde = { version = "1.0.188", features = ["derive"] } +chrono = { version = "0.4.31", features = ["serde"] } diff --git a/src/lib.rs b/src/lib.rs index 30db8e4..f27cf30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,7 +194,7 @@ //! ``` use nodes::yamd::Yamd; -use toolkit::{deserializer::Deserializer, node::Node}; +use toolkit::deserializer::Deserializer; pub mod nodes; mod toolkit; @@ -219,7 +219,7 @@ pub fn deserialize(input: &str) -> Option { /// let output = serialize(&yamd); /// ``` pub fn serialize(input: &Yamd) -> String { - input.serialize() + input.to_string() } #[cfg(test)] diff --git a/src/nodes/accordion.rs b/src/nodes/accordion.rs index af10d9e..33ad7a9 100644 --- a/src/nodes/accordion.rs +++ b/src/nodes/accordion.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, MaybeNode}, @@ -7,18 +11,20 @@ use crate::toolkit::{ use super::accordion_tab::AccordionTab; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum AccordionNodes { AccordionTab(AccordionTab), } -impl Node for AccordionNodes { - fn serialize(&self) -> String { +impl Display for AccordionNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AccordionNodes::AccordionTab(tab) => tab.serialize(), + AccordionNodes::AccordionTab(tab) => write!(f, "{}", tab), } } +} +impl Node for AccordionNodes { fn len(&self) -> usize { match self { AccordionNodes::AccordionTab(tab) => tab.len(), @@ -32,7 +38,7 @@ impl From for AccordionNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Accordion { consumed_all_input: bool, pub nodes: Vec, @@ -51,20 +57,23 @@ impl Accordion { } } -impl Node for Accordion { - fn serialize(&self) -> String { - format!( +impl Display for Accordion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "///\n{nodes}\n\\\\\\{end}", nodes = self .nodes .iter() - .map(|n| n.serialize()) + .map(|n| n.to_string()) .collect::>() .join(""), end = if self.consumed_all_input { "" } else { "\n\n" } ) } +} +impl Node for Accordion { fn len(&self) -> usize { self.nodes.iter().map(|n| n.len()).sum::() + self.get_outer_token_length() } diff --git a/src/nodes/accordion_tab.rs b/src/nodes/accordion_tab.rs index 849f047..467726d 100644 --- a/src/nodes/accordion_tab.rs +++ b/src/nodes/accordion_tab.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, FallbackNode, MaybeNode}, @@ -11,7 +15,7 @@ use super::{ list::List, paragraph::Paragraph, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum AccordionTabNodes { Pargaraph(Paragraph), Heading(Heading), @@ -25,22 +29,24 @@ pub enum AccordionTabNodes { Code(Code), } -impl Node for AccordionTabNodes { - fn serialize(&self) -> String { +impl Display for AccordionTabNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AccordionTabNodes::Pargaraph(node) => node.serialize(), - AccordionTabNodes::Heading(node) => node.serialize(), - AccordionTabNodes::Image(node) => node.serialize(), - AccordionTabNodes::ImageGallery(node) => node.serialize(), - AccordionTabNodes::CloudinaryImageGallery(node) => node.serialize(), - AccordionTabNodes::List(node) => node.serialize(), - AccordionTabNodes::Embed(node) => node.serialize(), - AccordionTabNodes::Accordion(node) => node.serialize(), - AccordionTabNodes::Divider(node) => node.serialize(), - AccordionTabNodes::Code(node) => node.serialize(), + AccordionTabNodes::Pargaraph(node) => write!(f, "{}", node), + AccordionTabNodes::Heading(node) => write!(f, "{}", node), + AccordionTabNodes::Image(node) => write!(f, "{}", node), + AccordionTabNodes::ImageGallery(node) => write!(f, "{}", node), + AccordionTabNodes::CloudinaryImageGallery(node) => write!(f, "{}", node), + AccordionTabNodes::List(node) => write!(f, "{}", node), + AccordionTabNodes::Embed(node) => write!(f, "{}", node), + AccordionTabNodes::Accordion(node) => write!(f, "{}", node), + AccordionTabNodes::Divider(node) => write!(f, "{}", node), + AccordionTabNodes::Code(node) => write!(f, "{}", node), } } +} +impl Node for AccordionTabNodes { fn len(&self) -> usize { match self { AccordionTabNodes::Pargaraph(node) => node.len(), @@ -117,7 +123,7 @@ impl From for AccordionTabNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct AccordionTab { pub header: Option, pub nodes: Vec, @@ -141,9 +147,10 @@ impl AccordionTab { } } -impl Node for AccordionTab { - fn serialize(&self) -> String { - format!( +impl Display for AccordionTab { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "//\n{header}{nodes}\n\\\\{end}", header = self .header @@ -152,13 +159,15 @@ impl Node for AccordionTab { nodes = self .nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .join(""), end = if self.consumed_all_input { "" } else { "\n" } ) } +} +impl Node for AccordionTab { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -298,7 +307,7 @@ mod cfg { Some("Header"), vec![Heading::new(true, "Heading", 1).into()] ) - .serialize(), + .to_string(), "//\n/ Header\n# Heading\n\\\\\n" ); } @@ -393,7 +402,7 @@ t**b** CloudinaryImageGallery::new("username", "tag", true).into(), ], ); - assert_eq!(tab.serialize(), input); + assert_eq!(tab.to_string(), input); assert_eq!(AccordionTab::deserialize(input), Some(tab)); } } diff --git a/src/nodes/anchor.rs b/src/nodes/anchor.rs index 14c9084..94b1c84 100644 --- a/src/nodes/anchor.rs +++ b/src/nodes/anchor.rs @@ -1,3 +1,7 @@ +use std::fmt::{Display, Formatter}; + +use serde::Serialize; + use crate::{ toolkit::context::Context, toolkit::deserializer::Deserializer, @@ -5,7 +9,7 @@ use crate::{ }; /// Representation of an anchor -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Anchor { pub text: String, pub url: String, @@ -20,10 +24,13 @@ impl Anchor { } } -impl Node for Anchor { - fn serialize(&self) -> String { - format!("[{}]({})", self.text, self.url) +impl Display for Anchor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}]({})", self.text, self.url) } +} + +impl Node for Anchor { fn len(&self) -> usize { self.text.len() + self.url.len() + 4 } @@ -56,7 +63,7 @@ mod tests { #[test] fn serialize() { - let a: String = Anchor::new("nice link", "https://test.io").serialize(); + let a: String = Anchor::new("nice link", "https://test.io").to_string(); assert_eq!(a, "[nice link](https://test.io)".to_string()); } diff --git a/src/nodes/bold.rs b/src/nodes/bold.rs index 556d312..da916da 100644 --- a/src/nodes/bold.rs +++ b/src/nodes/bold.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::{ nodes::italic::Italic, nodes::strikethrough::Strikethrough, @@ -10,7 +14,7 @@ use crate::{ }, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum BoldNodes { Text(Text), I(Italic), @@ -35,15 +39,17 @@ impl From for BoldNodes { } } -impl Node for BoldNodes { - fn serialize(&self) -> String { +impl Display for BoldNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - BoldNodes::Text(v) => v.serialize(), - BoldNodes::I(v) => v.serialize(), - BoldNodes::S(v) => v.serialize(), + BoldNodes::Text(node) => write!(f, "{}", node), + BoldNodes::I(node) => write!(f, "{}", node), + BoldNodes::S(node) => write!(f, "{}", node), } } +} +impl Node for BoldNodes { fn len(&self) -> usize { match self { BoldNodes::Text(node) => node.len(), @@ -53,7 +59,7 @@ impl Node for BoldNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Bold { pub nodes: Vec, } @@ -91,17 +97,21 @@ impl Default for Bold { } } -impl Node for Bold { - fn serialize(&self) -> String { - format!( +impl Display for Bold { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "**{}**", self.nodes .iter() - .map(|element| { element.serialize() }) + .map(|element| { element.to_string() }) .collect::>() .concat() ) } +} + +impl Node for Bold { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -135,7 +145,7 @@ mod tests { fn only_text() { let mut b = Bold::new(); b.push(Text::new("B as bold")); - let str = b.serialize(); + let str = b.to_string(); assert_eq!(str, "**B as bold**".to_string()); } @@ -146,7 +156,7 @@ mod tests { Italic::new("Italic").into(), Strikethrough::new("Strikethrough").into(), ]) - .serialize(); + .to_string(); assert_eq!(b, "**B as bold _Italic_~~Strikethrough~~**".to_string()); } diff --git a/src/nodes/cloudinary_image_gallery.rs b/src/nodes/cloudinary_image_gallery.rs index 4d34573..44d210c 100644 --- a/src/nodes/cloudinary_image_gallery.rs +++ b/src/nodes/cloudinary_image_gallery.rs @@ -1,6 +1,10 @@ +use std::fmt::{Display, Formatter}; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize)] pub struct CloudinaryImageGallery { username: String, pub tag: String, @@ -17,16 +21,14 @@ impl CloudinaryImageGallery { } } -impl Node for CloudinaryImageGallery { - fn serialize(&self) -> String { - format!( - "!!!!\n! {username}\n! {tag}\n!!!!{end}", - username = self.username, - tag = self.tag, - end = if self.consumed_all_input { "" } else { "\n\n" } - ) +impl Display for CloudinaryImageGallery { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let end = if self.consumed_all_input { "" } else { "\n\n" }; + write!(f, "!!!!\n! {}\n! {}\n!!!!{}", self.username, self.tag, end) } +} +impl Node for CloudinaryImageGallery { fn len(&self) -> usize { self.username.len() + self.tag.len() + 15 + if self.consumed_all_input { 0 } else { 2 } } @@ -63,7 +65,7 @@ mod test { CloudinaryImageGallery::deserialize(input), Some(expected.clone()), ); - assert_eq!(expected.serialize(), input); + assert_eq!(expected.to_string(), input); } #[test] diff --git a/src/nodes/code.rs b/src/nodes/code.rs index 9c326c1..84220fe 100644 --- a/src/nodes/code.rs +++ b/src/nodes/code.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Code { pub lang: String, pub code: String, @@ -17,11 +21,14 @@ impl Code { } } -impl Node for Code { - fn serialize(&self) -> String { +impl Display for Code { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!("```{}\n{}\n```{end}", self.lang, self.code) + write!(f, "```{}\n{}\n```{}", self.lang, self.code, end) } +} + +impl Node for Code { fn len(&self) -> usize { let end = if self.consumed_all_input { 0 } else { 2 }; self.lang.len() + self.code.len() + 8 + end @@ -52,11 +59,11 @@ mod tests { #[test] fn serialize() { assert_eq!( - Code::new(true, "rust", "let foo:usize=1;").serialize(), + Code::new(true, "rust", "let foo:usize=1;").to_string(), String::from("```rust\nlet foo:usize=1;\n```") ); assert_eq!( - Code::new(false, "rust", "let foo:usize=1;").serialize(), + Code::new(false, "rust", "let foo:usize=1;").to_string(), String::from("```rust\nlet foo:usize=1;\n```\n\n") ); } diff --git a/src/nodes/divider.rs b/src/nodes/divider.rs index 50b50bc..3a9b93d 100644 --- a/src/nodes/divider.rs +++ b/src/nodes/divider.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Divider { consumed_all_input: bool, } @@ -11,12 +15,14 @@ impl Divider { } } -impl Node for Divider { - fn serialize(&self) -> String { +impl Display for Divider { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!("-----{end}") + write!(f, "-----{end}") } +} +impl Node for Divider { fn len(&self) -> usize { if self.consumed_all_input { 5 @@ -71,7 +77,7 @@ mod tests { #[test] fn serialize() { - assert_eq!(Divider::new(true).serialize(), String::from("-----")); - assert_eq!(Divider::new(false).serialize(), String::from("-----\n\n")); + assert_eq!(Divider::new(true).to_string(), String::from("-----")); + assert_eq!(Divider::new(false).to_string(), String::from("-----\n\n")); } } diff --git a/src/nodes/embed.rs b/src/nodes/embed.rs index 8f38d94..6199bba 100644 --- a/src/nodes/embed.rs +++ b/src/nodes/embed.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Embed { pub url: String, pub kind: String, @@ -17,12 +21,14 @@ impl Embed { } } -impl Node for Embed { - fn serialize(&self) -> String { +impl Display for Embed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!("{{{{{}|{}}}}}{end}", self.kind, self.url) + write!(f, "{{{{{}|{}}}}}{}", self.kind, self.url, end) } +} +impl Node for Embed { fn len(&self) -> usize { let end = if self.consumed_all_input { 0 } else { 2 }; 5 + self.kind.len() + self.url.len() + end @@ -65,11 +71,11 @@ mod tests { "https://www.youtube.com/embed/wsfdjlkjsdf", false, ) - .serialize(), + .to_string(), "{{youtube|https://www.youtube.com/embed/wsfdjlkjsdf}}\n\n" ); assert_eq!( - Embed::new("youtube", "https://www.youtube.com/embed/wsfdjlkjsdf", true).serialize(), + Embed::new("youtube", "https://www.youtube.com/embed/wsfdjlkjsdf", true).to_string(), "{{youtube|https://www.youtube.com/embed/wsfdjlkjsdf}}" ); } diff --git a/src/nodes/heading.rs b/src/nodes/heading.rs index 40f80e8..e083d58 100644 --- a/src/nodes/heading.rs +++ b/src/nodes/heading.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Heading { pub level: u8, pub text: String, @@ -41,12 +45,15 @@ impl Deserializer for Heading { } } -impl Node for Heading { - fn serialize(&self) -> String { +impl Display for Heading { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let level = String::from('#').repeat(self.level as usize); let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!("{} {}{end}", level, self.text) + write!(f, "{} {}{}", level, self.text, end) } +} + +impl Node for Heading { fn len(&self) -> usize { let end = if self.consumed_all_input { 0 } else { 2 }; self.text.len() + self.level as usize + 1 + end @@ -61,27 +68,27 @@ mod tests { #[test] fn level_one() { - assert_eq!(Heading::new(true, "Header", 1).serialize(), "# Header"); - assert_eq!(Heading::new(false, "Header", 1).serialize(), "# Header\n\n"); + assert_eq!(Heading::new(true, "Header", 1).to_string(), "# Header"); + assert_eq!(Heading::new(false, "Header", 1).to_string(), "# Header\n\n"); } #[test] fn level_gt_six() { - let h = Heading::new(true, "Header", 7).serialize(); + let h = Heading::new(true, "Header", 7).to_string(); assert_eq!(h, "###### Header"); - let h = Heading::new(true, "Header", 34).serialize(); + let h = Heading::new(true, "Header", 34).to_string(); assert_eq!(h, "###### Header"); } #[test] fn level_eq_zero() { - let h = Heading::new(true, "Header", 0).serialize(); + let h = Heading::new(true, "Header", 0).to_string(); assert_eq!(h, "# Header"); } #[test] fn level_eq_four() { - let h = Heading::new(true, "Header", 4).serialize(); + let h = Heading::new(true, "Header", 4).to_string(); assert_eq!(h, "#### Header"); } diff --git a/src/nodes/highlight.rs b/src/nodes/highlight.rs index a5aa264..d5889be 100644 --- a/src/nodes/highlight.rs +++ b/src/nodes/highlight.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, MaybeNode}, @@ -7,18 +11,20 @@ use crate::toolkit::{ use super::paragraph::Paragraph; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum HighlightNodes { Paragraph(Paragraph), } -impl Node for HighlightNodes { - fn serialize(&self) -> String { +impl Display for HighlightNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - HighlightNodes::Paragraph(node) => node.serialize(), + HighlightNodes::Paragraph(node) => write!(f, "{}", node), } } +} +impl Node for HighlightNodes { fn len(&self) -> usize { match self { HighlightNodes::Paragraph(node) => node.len(), @@ -32,7 +38,7 @@ impl From for HighlightNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Highlight { pub header: Option, pub icon: Option, @@ -64,8 +70,8 @@ impl Highlight { } } -impl Node for Highlight { - fn serialize(&self) -> String { +impl Display for Highlight { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let header = match &self.header { Some(header) => format!(">> {header}\n"), None => String::new(), @@ -75,16 +81,22 @@ impl Node for Highlight { None => String::new(), }; let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!( + write!( + f, ">>>\n{header}{icon}{}\n>>>{end}", self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() - .join("") + .join(""), + header = header, + icon = icon, + end = end ) } +} +impl Node for Highlight { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -202,7 +214,7 @@ mod tests { Paragraph::new_with_nodes(true, vec![Text::new("t").into()]).into() ] ) - .serialize(), + .to_string(), String::from(">>>\n>> h\n> i\nt\n\nt\n>>>") ); assert_eq!( @@ -215,7 +227,7 @@ mod tests { Paragraph::new_with_nodes(true, vec![Text::new("t").into()]).into() ] ) - .serialize(), + .to_string(), String::from(">>>\n>> h\n> i\nt\n\nt\n>>>\n\n") ); assert_eq!( @@ -228,7 +240,7 @@ mod tests { Paragraph::new_with_nodes(true, vec![Text::new("t").into()]).into() ] ) - .serialize(), + .to_string(), String::from(">>>\nt\n\nt\n>>>\n\n") ); } diff --git a/src/nodes/image.rs b/src/nodes/image.rs index fa23e06..f13e1ea 100644 --- a/src/nodes/image.rs +++ b/src/nodes/image.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Image { pub alt: String, pub url: String, @@ -17,15 +21,19 @@ impl Image { } } -impl Node for Image { - fn serialize(&self) -> String { +impl Display for Image { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "\n" } else { "\n\n" }; - format!("![{}]({}){end}", self.alt, self.url) + + write!(f, "![{}]({}){}", self.alt, self.url, end) } +} + +impl Node for Image { fn len(&self) -> usize { let end = if self.consumed_all_input { 1 } else { 2 }; self.alt.len() + self.url.len() + 5 + end @@ -54,11 +62,11 @@ mod tests { #[test] fn serializer() { assert_eq!( - Image::new(true, 'a', 'u').serialize(), + Image::new(true, 'a', 'u').to_string(), String::from("![a](u)\n") ); assert_eq!( - Image::new(false, 'a', 'u').serialize(), + Image::new(false, 'a', 'u').to_string(), String::from("![a](u)\n\n") ) } diff --git a/src/nodes/image_gallery.rs b/src/nodes/image_gallery.rs index 562255e..7e18222 100644 --- a/src/nodes/image_gallery.rs +++ b/src/nodes/image_gallery.rs @@ -1,3 +1,7 @@ +use std::fmt::{Display, Formatter}; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, MaybeNode}, @@ -7,17 +11,20 @@ use crate::toolkit::{ use super::image::Image; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum ImageGalleryNodes { Image(Image), } -impl Node for ImageGalleryNodes { - fn serialize(&self) -> String { +impl Display for ImageGalleryNodes { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - ImageGalleryNodes::Image(node) => node.serialize(), + ImageGalleryNodes::Image(node) => write!(f, "{}", node), } } +} + +impl Node for ImageGalleryNodes { fn len(&self) -> usize { match self { ImageGalleryNodes::Image(node) => node.len(), @@ -33,7 +40,7 @@ impl From for ImageGalleryNodes { /// Image Gallery node is a node that contains multiple Image nodes /// it starts with `!!!\n` and ends with `\n!!!` -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct ImageGallery { pub nodes: Vec, consumed_all_input: bool, @@ -52,18 +59,22 @@ impl ImageGallery { } } -impl Node for ImageGallery { - fn serialize(&self) -> String { +impl Display for ImageGallery { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!( + write!( + f, "!!!\n{}!!!{end}", self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .join("") ) } +} + +impl Node for ImageGallery { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -123,7 +134,7 @@ mod tests { ], true ) - .serialize(), + .to_string(), "!!!\n![a](u)\n![a2](u2)\n!!!" ); assert_eq!( @@ -134,7 +145,7 @@ mod tests { ], false ) - .serialize(), + .to_string(), "!!!\n![a](u)\n![a2](u2)\n!!!\n\n" ); } diff --git a/src/nodes/inline_code.rs b/src/nodes/inline_code.rs index b264ce2..105ec61 100644 --- a/src/nodes/inline_code.rs +++ b/src/nodes/inline_code.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct InlineCode { pub text: String, } @@ -11,10 +15,13 @@ impl InlineCode { } } -impl Node for InlineCode { - fn serialize(&self) -> String { - format!("`{}`", self.text) +impl Display for InlineCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "`{}`", self.text) } +} + +impl Node for InlineCode { fn len(&self) -> usize { self.text.len() + 2 } @@ -33,12 +40,12 @@ impl Deserializer for InlineCode { #[cfg(test)] mod tests { use super::InlineCode; - use crate::toolkit::{deserializer::Deserializer, node::Node}; + use crate::toolkit::deserializer::Deserializer; use pretty_assertions::assert_eq; #[test] fn to_string() { - let inline_code: String = InlineCode::new("const bar = 'baz'").serialize(); + let inline_code: String = InlineCode::new("const bar = 'baz'").to_string(); assert_eq!(inline_code, "`const bar = 'baz'`".to_string()) } diff --git a/src/nodes/italic.rs b/src/nodes/italic.rs index ed660a7..24c53eb 100644 --- a/src/nodes/italic.rs +++ b/src/nodes/italic.rs @@ -1,10 +1,14 @@ +use std::fmt::{Display, Formatter}; + +use serde::Serialize; + use crate::{ toolkit::{context::Context, deserializer::Deserializer}, toolkit::{matcher::Matcher, node::Node}, }; /// Representation of an Italic text -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Italic { pub text: String, } @@ -15,10 +19,13 @@ impl Italic { } } -impl Node for Italic { - fn serialize(&self) -> String { - format!("_{}_", self.text) +impl Display for Italic { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "_{}_", self.text) } +} + +impl Node for Italic { fn len(&self) -> usize { self.text.len() + 2 } @@ -49,7 +56,7 @@ mod tests { #[test] fn to_string() { - let i = Italic::new("italic").serialize(); + let i = Italic::new("italic").to_string(); assert_eq!(i, "_italic_".to_string()); } diff --git a/src/nodes/list.rs b/src/nodes/list.rs index 8720859..1b56a14 100644 --- a/src/nodes/list.rs +++ b/src/nodes/list.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, MaybeNode}, @@ -7,23 +11,26 @@ use crate::toolkit::{ use super::list_item::ListItem; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize)] pub enum ListTypes { Unordered, Ordered, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum ListNodes { ListItem(ListItem), } -impl Node for ListNodes { - fn serialize(&self) -> String { +impl Display for ListNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ListNodes::ListItem(node) => node.serialize(), + ListNodes::ListItem(node) => write!(f, "{}", node), } } +} + +impl Node for ListNodes { fn len(&self) -> usize { match self { ListNodes::ListItem(node) => node.len(), @@ -37,7 +44,7 @@ impl From for ListNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct List { pub list_type: ListTypes, pub level: usize, @@ -87,18 +94,22 @@ impl List { } } -impl Node for List { - fn serialize(&self) -> String { +impl Display for List { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let end = if self.consumed_all_input { "" } else { "\n\n" }; - format!( + write!( + f, "{}{end}", self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .join("") ) } +} + +impl Node for List { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -193,7 +204,7 @@ mod tests { .into(), ], } - .serialize(), + .to_string(), "- unordered list item\n- unordered list item" ); assert_eq!( @@ -222,7 +233,7 @@ mod tests { .into(), ], } - .serialize(), + .to_string(), "- unordered list item\n- unordered list item\n\n" ); } @@ -255,7 +266,7 @@ mod tests { ], ); - assert_eq!(list.serialize(), "+ ordered list item\n+ ordered list item"); + assert_eq!(list.to_string(), "+ ordered list item\n+ ordered list item"); } #[test] diff --git a/src/nodes/list_item.rs b/src/nodes/list_item.rs index f041d4b..adf0da2 100644 --- a/src/nodes/list_item.rs +++ b/src/nodes/list_item.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; use super::{ @@ -5,7 +9,7 @@ use super::{ list_item_content::ListItemContent, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct ListItem { pub list_type: ListTypes, pub level: usize, @@ -50,23 +54,26 @@ impl ListItem { } } -impl Node for ListItem { - fn serialize(&self) -> String { +impl Display for ListItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let list_type = match self.list_type { ListTypes::Unordered => '-', ListTypes::Ordered => '+', }; - format!( + write!( + f, "{}{} {}{}", String::from(' ').repeat(self.level), list_type, - self.text.serialize(), + self.text, self.nested_list .as_ref() - .map_or("".to_string(), |list| list.serialize()) + .map_or("".to_string(), |list| list.to_string()) ) } +} +impl Node for ListItem { fn len(&self) -> usize { self.nested_list.as_ref().map_or(0, |list| list.len()) + self.text.len() + self.level + 2 } @@ -137,7 +144,7 @@ mod tests { 0, ListItemContent::new_with_nodes(true, vec![Text::new("test").into()]) ) - .serialize(), + .to_string(), "- test".to_string() ); @@ -147,7 +154,7 @@ mod tests { 0, ListItemContent::new_with_nodes(true, vec![Text::new("test").into()]) ) - .serialize(), + .to_string(), "+ test".to_string() ); @@ -168,7 +175,7 @@ mod tests { .into()] )) ) - .serialize(), + .to_string(), "- test\n - test".to_string() ); } diff --git a/src/nodes/list_item_content.rs b/src/nodes/list_item_content.rs index 85685de..3b10af7 100644 --- a/src/nodes/list_item_content.rs +++ b/src/nodes/list_item_content.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{Branch, DefinitelyNode, Deserializer, FallbackNode, MaybeNode}, @@ -10,7 +14,7 @@ use super::{ strikethrough::Strikethrough, text::Text, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum ListItemContentNodes { A(Anchor), B(Bold), @@ -56,18 +60,20 @@ impl From for ListItemContentNodes { } } -impl Node for ListItemContentNodes { - fn serialize(&self) -> String { +impl Display for ListItemContentNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ListItemContentNodes::A(node) => node.serialize(), - ListItemContentNodes::B(node) => node.serialize(), - ListItemContentNodes::I(node) => node.serialize(), - ListItemContentNodes::S(node) => node.serialize(), - ListItemContentNodes::Text(node) => node.serialize(), - ListItemContentNodes::InlineCode(node) => node.serialize(), + ListItemContentNodes::A(node) => write!(f, "{}", node), + ListItemContentNodes::B(node) => write!(f, "{}", node), + ListItemContentNodes::I(node) => write!(f, "{}", node), + ListItemContentNodes::S(node) => write!(f, "{}", node), + ListItemContentNodes::Text(node) => write!(f, "{}", node), + ListItemContentNodes::InlineCode(node) => write!(f, "{}", node), } } +} +impl Node for ListItemContentNodes { fn len(&self) -> usize { match self { ListItemContentNodes::A(node) => node.len(), @@ -80,25 +86,28 @@ impl Node for ListItemContentNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct ListItemContent { consumed_all_input: bool, pub nodes: Vec, } -impl Node for ListItemContent { - fn serialize(&self) -> String { - format!( +impl Display for ListItemContent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "{}{}", self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .join(""), if self.consumed_all_input { "" } else { "\n" } ) } +} +impl Node for ListItemContent { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -198,14 +207,14 @@ mod test { #[test] fn serialize() { - assert_eq!(ListItemContent::new(true).serialize(), ""); - assert_eq!(ListItemContent::new(false).serialize(), "\n"); + assert_eq!(ListItemContent::new(true).to_string(), ""); + assert_eq!(ListItemContent::new(false).to_string(), "\n"); assert_eq!( - ListItemContent::new_with_nodes(true, vec![Text::new("Hello").into()]).serialize(), + ListItemContent::new_with_nodes(true, vec![Text::new("Hello").into()]).to_string(), "Hello" ); assert_eq!( - ListItemContent::new_with_nodes(false, vec![Text::new("Hello").into()]).serialize(), + ListItemContent::new_with_nodes(false, vec![Text::new("Hello").into()]).to_string(), "Hello\n" ); } @@ -252,7 +261,7 @@ mod test { Strikethrough::new("S").into(), ] ) - .serialize() + .to_string() ); } } diff --git a/src/nodes/metadata.rs b/src/nodes/metadata.rs index ee186c3..1b0048e 100644 --- a/src/nodes/metadata.rs +++ b/src/nodes/metadata.rs @@ -1,7 +1,10 @@ +use std::fmt::Display; + use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matcher, node::Node}; use chrono::{DateTime, FixedOffset}; +use serde::Serialize; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Default)] pub struct Metadata { pub header: Option, pub timestamp: Option>, @@ -10,18 +13,6 @@ pub struct Metadata { pub tags: Vec, } -impl Default for Metadata { - fn default() -> Self { - Self { - header: None, - timestamp: None, - image: None, - preview: None, - tags: vec![], - } - } -} - impl Metadata { pub fn new>( header: Option, @@ -40,12 +31,13 @@ impl Metadata { } } -impl Node for Metadata { - fn serialize(&self) -> String { +impl Display for Metadata { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.len() == 0 { - return "".to_string(); + return write!(f, ""); } - format!( + write!( + f, "{}{}{}{}{}^^^\n\n", self.header .as_ref() @@ -66,7 +58,9 @@ impl Node for Metadata { }, ) } +} +impl Node for Metadata { fn len(&self) -> usize { let len = self.header.as_ref().map_or(0, |h| h.len() + 9) + self @@ -143,7 +137,7 @@ mod tests { Some(vec!["tag1".to_string(), "tag2".to_string()]), ); assert_eq!( - metadata.serialize(), + metadata.to_string(), "header: header\ntimestamp: 2022-01-01 00:00:00 +02:00\nimage: image\npreview: preview\ntags: tag1, tag2\n^^^\n\n" ); } @@ -160,7 +154,7 @@ mod tests { Some("preview"), Some(vec!["tag1".to_string(), "tag2".to_string()]), ); - assert_eq!(metadata.len(), metadata.serialize().len()); + assert_eq!(metadata.len(), metadata.to_string().len()); } #[test] @@ -175,7 +169,7 @@ mod tests { Some("preview"), Some(vec!["tag1".to_string()]), ); - assert_eq!(metadata.len(), metadata.serialize().len()); + assert_eq!(metadata.len(), metadata.to_string().len()); } #[test] @@ -191,7 +185,7 @@ mod tests { Some(vec!["tag1".to_string(), "tag2".to_string()]), ); assert_eq!( - Metadata::deserialize(metadata.serialize().as_str()), + Metadata::deserialize(metadata.to_string().as_str()), Some(metadata) ); } @@ -231,7 +225,7 @@ mod tests { Metadata::default(), Metadata::new::<&str>(None, None, None, None, None) ); - assert_eq!(Metadata::default().serialize(), ""); + assert_eq!(Metadata::default().to_string(), ""); assert_eq!(Metadata::default().len(), 0); } } diff --git a/src/nodes/paragraph.rs b/src/nodes/paragraph.rs index dabc53f..0ccc406 100644 --- a/src/nodes/paragraph.rs +++ b/src/nodes/paragraph.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::nodes::{ anchor::Anchor, bold::Bold, inline_code::InlineCode, italic::Italic, strikethrough::Strikethrough, text::Text, @@ -9,7 +13,7 @@ use crate::toolkit::{ matcher::Matcher, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum ParagraphNodes { A(Anchor), B(Bold), @@ -55,17 +59,20 @@ impl From for ParagraphNodes { } } -impl Node for ParagraphNodes { - fn serialize(&self) -> String { +impl Display for ParagraphNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ParagraphNodes::A(node) => node.serialize(), - ParagraphNodes::B(node) => node.serialize(), - ParagraphNodes::I(node) => node.serialize(), - ParagraphNodes::S(node) => node.serialize(), - ParagraphNodes::Text(node) => node.serialize(), - ParagraphNodes::InlineCode(node) => node.serialize(), + ParagraphNodes::A(node) => write!(f, "{}", node), + ParagraphNodes::B(node) => write!(f, "{}", node), + ParagraphNodes::I(node) => write!(f, "{}", node), + ParagraphNodes::S(node) => write!(f, "{}", node), + ParagraphNodes::Text(node) => write!(f, "{}", node), + ParagraphNodes::InlineCode(node) => write!(f, "{}", node), } } +} + +impl Node for ParagraphNodes { fn len(&self) -> usize { match self { ParagraphNodes::A(node) => node.len(), @@ -78,7 +85,7 @@ impl Node for ParagraphNodes { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Paragraph { consumed_all_input: bool, pub nodes: Vec, @@ -133,23 +140,22 @@ impl Deserializer for Paragraph { } } -impl Node for Paragraph { - fn serialize(&self) -> String { - let end_token = match self.consumed_all_input { - true => "", - false => "\n\n", - }; - format!( +impl Display for Paragraph { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "{}{}", self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .concat(), - end_token + if self.consumed_all_input { "" } else { "\n\n" } ) } +} +impl Node for Paragraph { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -190,7 +196,7 @@ mod tests { p.push(InlineCode::new("let foo='bar';")); assert_eq!( - p.serialize(), + p.to_string(), "simple text **bold text**`let foo='bar';`".to_string() ); } @@ -209,11 +215,11 @@ mod tests { Strikethrough::new("S").into() ], ) - .serialize(), + .to_string(), "simple text **bold text**`let foo='bar';`[a](u)_I_~~S~~".to_string() ); assert_eq!( - Paragraph::new_with_nodes(false, vec![Text::new("t").into()]).serialize(), + Paragraph::new_with_nodes(false, vec![Text::new("t").into()]).to_string(), "t\n\n".to_string() ); } diff --git a/src/nodes/strikethrough.rs b/src/nodes/strikethrough.rs index 0d665ef..d675a4b 100644 --- a/src/nodes/strikethrough.rs +++ b/src/nodes/strikethrough.rs @@ -1,10 +1,14 @@ +use std::fmt::{Display, Formatter}; + +use serde::Serialize; + use crate::{ toolkit::{context::Context, deserializer::Deserializer}, toolkit::{matcher::Matcher, node::Node}, }; /// Representation of strike through -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Strikethrough { pub text: String, } @@ -15,10 +19,13 @@ impl Strikethrough { } } -impl Node for Strikethrough { - fn serialize(&self) -> String { - format!("~~{}~~", self.text) +impl Display for Strikethrough { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "~~{}~~", self.text) } +} + +impl Node for Strikethrough { fn len(&self) -> usize { self.text.len() + 4 } @@ -48,7 +55,7 @@ mod tests { #[test] fn to_string() { - let s: String = Strikethrough::new("2+2=5").serialize(); + let s: String = Strikethrough::new("2+2=5").to_string(); assert_eq!(s, "~~2+2=5~~".to_string()); } diff --git a/src/nodes/text.rs b/src/nodes/text.rs index 465c8ca..12794c2 100644 --- a/src/nodes/text.rs +++ b/src/nodes/text.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::toolkit::{ context::Context, deserializer::{DefinitelyNode, Deserializer, FallbackNode}, @@ -5,7 +9,7 @@ use crate::toolkit::{ }; /// Representation of a regular text -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Text { pub text: String, } @@ -22,10 +26,13 @@ impl Deserializer for Text { } } -impl Node for Text { - fn serialize(&self) -> String { - self.text.clone() +impl Display for Text { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.text) } +} + +impl Node for Text { fn len(&self) -> usize { self.text.len() } @@ -43,7 +50,7 @@ impl FallbackNode for Text { #[cfg(test)] mod tests { use super::Text; - use crate::toolkit::{deserializer::Deserializer, node::Node}; + use crate::toolkit::deserializer::Deserializer; use pretty_assertions::assert_eq; #[test] @@ -54,7 +61,7 @@ mod tests { #[test] fn to_string() { - let text: String = Text::new("shiny text").serialize(); + let text: String = Text::new("shiny text").to_string(); assert_eq!(text, "shiny text".to_string()); } diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index 1685028..13e2832 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use serde::Serialize; + use crate::{ nodes::heading::Heading, nodes::paragraph::Paragraph, @@ -11,7 +15,7 @@ use super::{ image_gallery::ImageGallery, list::List, metadata::Metadata, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum YamdNodes { P(Paragraph), H(Heading), @@ -92,22 +96,25 @@ impl From for YamdNodes { } } -impl Node for YamdNodes { - fn serialize(&self) -> String { +impl Display for YamdNodes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - YamdNodes::P(node) => node.serialize(), - YamdNodes::H(node) => node.serialize(), - YamdNodes::Image(node) => node.serialize(), - YamdNodes::Code(node) => node.serialize(), - YamdNodes::List(node) => node.serialize(), - YamdNodes::ImageGallery(node) => node.serialize(), - YamdNodes::Highlight(node) => node.serialize(), - YamdNodes::Divider(node) => node.serialize(), - YamdNodes::Embed(node) => node.serialize(), - YamdNodes::CloudinaryImageGallery(node) => node.serialize(), - YamdNodes::Accordion(node) => node.serialize(), + YamdNodes::P(node) => write!(f, "{}", node), + YamdNodes::H(node) => write!(f, "{}", node), + YamdNodes::Image(node) => write!(f, "{}", node), + YamdNodes::Code(node) => write!(f, "{}", node), + YamdNodes::List(node) => write!(f, "{}", node), + YamdNodes::ImageGallery(node) => write!(f, "{}", node), + YamdNodes::Highlight(node) => write!(f, "{}", node), + YamdNodes::Divider(node) => write!(f, "{}", node), + YamdNodes::Embed(node) => write!(f, "{}", node), + YamdNodes::CloudinaryImageGallery(node) => write!(f, "{}", node), + YamdNodes::Accordion(node) => write!(f, "{}", node), } } +} + +impl Node for YamdNodes { fn len(&self) -> usize { match self { YamdNodes::P(node) => node.len(), @@ -126,7 +133,7 @@ impl Node for YamdNodes { } /// Yamd is a parent node for every node. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub struct Yamd { pub metadata: Metadata, pub nodes: Vec, @@ -188,19 +195,22 @@ impl Default for Yamd { } } -impl Node for Yamd { - fn serialize(&self) -> String { - format!( +impl Display for Yamd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "{}{}", - self.metadata.serialize(), + self.metadata, self.nodes .iter() - .map(|node| node.serialize()) + .map(|node| node.to_string()) .collect::>() .join("") ) } +} +impl Node for Yamd { fn len(&self) -> usize { self.nodes.iter().map(|node| node.len()).sum::() + self.get_outer_token_length() } @@ -232,7 +242,7 @@ mod tests { text::Text, }, toolkit::deserializer::Branch, - toolkit::{deserializer::Deserializer, node::Node}, + toolkit::deserializer::Deserializer, }; use chrono::DateTime; use pretty_assertions::assert_eq; @@ -300,7 +310,7 @@ end"#; vec![Text::new("text").into()], )); - assert_eq!(t.serialize(), "# header\n\ntext".to_string()); + assert_eq!(t.to_string(), "# header\n\ntext".to_string()); } #[test] @@ -312,7 +322,7 @@ end"#; Paragraph::new_with_nodes(true, vec![Text::new("text").into()]).into(), ], ) - .serialize(); + .to_string(); assert_eq!(t, "# header\n\ntext".to_string()); } @@ -499,13 +509,13 @@ end"#; Paragraph::new_with_nodes(true, vec![Text::new("end").into()]).into() ] ) - .serialize(), + .to_string(), String::from(TEST_CASE) ) } #[test] fn default() { - assert_eq!(Yamd::default().serialize(), String::new()); + assert_eq!(Yamd::default().to_string(), String::new()); } } diff --git a/src/toolkit/node.rs b/src/toolkit/node.rs index 020797b..d0a315b 100644 --- a/src/toolkit/node.rs +++ b/src/toolkit/node.rs @@ -4,7 +4,6 @@ use super::{ }; pub trait Node { - fn serialize(&self) -> String; fn len(&self) -> usize; fn maybe_node() -> MaybeNode where From f71c3f071481a3f550f2fcc5f4296c3bfc533d6f Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Thu, 12 Oct 2023 21:18:33 +0200 Subject: [PATCH 2/3] make clippy happy --- src/nodes/list_item.rs | 4 ++-- src/nodes/metadata.rs | 2 +- src/nodes/yamd.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nodes/list_item.rs b/src/nodes/list_item.rs index adf0da2..dc37cd9 100644 --- a/src/nodes/list_item.rs +++ b/src/nodes/list_item.rs @@ -88,8 +88,8 @@ impl Deserializer for ListItem { }; let mut matcher = Matcher::new(input); if let Some(list_item) = matcher.get_match( - format!("{}{} ", " ".repeat(level), list_type.clone()).as_str(), - format!("\n{}{} ", " ".repeat(level), list_type.clone()).as_str(), + format!("{}{} ", " ".repeat(level), list_type).as_str(), + format!("\n{}{} ", " ".repeat(level), list_type).as_str(), true, ) { let content_body = if list_item.end_token.is_empty() { diff --git a/src/nodes/metadata.rs b/src/nodes/metadata.rs index 1b0048e..2bc90b4 100644 --- a/src/nodes/metadata.rs +++ b/src/nodes/metadata.rs @@ -26,7 +26,7 @@ impl Metadata { timestamp, image: image.map(|i| i.into()), preview: preview.map(|p| p.into()), - tags: tags.unwrap_or(vec![]), + tags: tags.unwrap_or_default(), } } } diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index 13e2832..cadc36b 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -146,7 +146,7 @@ impl Yamd { pub fn new_with_nodes(metadata: Option, nodes: Vec) -> Self { Self { - metadata: metadata.unwrap_or(Metadata::default()), + metadata: metadata.unwrap_or_default(), nodes, } } From 81419f9636cfc99e09d1825569e045984434544c Mon Sep 17 00:00:00 2001 From: Serhiy Barhamon Date: Sat, 14 Oct 2023 11:29:15 +0200 Subject: [PATCH 3/3] serde serialize update enums are serialized to tags skip serialization of consumed_all_input field --- src/nodes/accordion.rs | 2 ++ src/nodes/accordion_tab.rs | 2 ++ src/nodes/bold.rs | 1 + src/nodes/cloudinary_image_gallery.rs | 1 + src/nodes/code.rs | 1 + src/nodes/divider.rs | 1 + src/nodes/embed.rs | 1 + src/nodes/heading.rs | 1 + src/nodes/highlight.rs | 2 ++ src/nodes/image.rs | 9 +++++---- src/nodes/image_gallery.rs | 2 ++ src/nodes/list.rs | 2 ++ src/nodes/list_item_content.rs | 2 ++ src/nodes/paragraph.rs | 2 ++ src/nodes/yamd.rs | 1 + 15 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/nodes/accordion.rs b/src/nodes/accordion.rs index 33ad7a9..09d760e 100644 --- a/src/nodes/accordion.rs +++ b/src/nodes/accordion.rs @@ -12,6 +12,7 @@ use crate::toolkit::{ use super::accordion_tab::AccordionTab; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum AccordionNodes { AccordionTab(AccordionTab), } @@ -40,6 +41,7 @@ impl From for AccordionNodes { #[derive(Debug, PartialEq, Serialize)] pub struct Accordion { + #[serde(skip_serializing)] consumed_all_input: bool, pub nodes: Vec, } diff --git a/src/nodes/accordion_tab.rs b/src/nodes/accordion_tab.rs index 467726d..9701920 100644 --- a/src/nodes/accordion_tab.rs +++ b/src/nodes/accordion_tab.rs @@ -16,6 +16,7 @@ use super::{ }; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum AccordionTabNodes { Pargaraph(Paragraph), Heading(Heading), @@ -127,6 +128,7 @@ impl From for AccordionTabNodes { pub struct AccordionTab { pub header: Option, pub nodes: Vec, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/bold.rs b/src/nodes/bold.rs index da916da..f3f33f1 100644 --- a/src/nodes/bold.rs +++ b/src/nodes/bold.rs @@ -15,6 +15,7 @@ use crate::{ }; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum BoldNodes { Text(Text), I(Italic), diff --git a/src/nodes/cloudinary_image_gallery.rs b/src/nodes/cloudinary_image_gallery.rs index 44d210c..bb000ee 100644 --- a/src/nodes/cloudinary_image_gallery.rs +++ b/src/nodes/cloudinary_image_gallery.rs @@ -8,6 +8,7 @@ use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matc pub struct CloudinaryImageGallery { username: String, pub tag: String, + #[serde(skip_serializing)] pub consumed_all_input: bool, } diff --git a/src/nodes/code.rs b/src/nodes/code.rs index 84220fe..a207b1c 100644 --- a/src/nodes/code.rs +++ b/src/nodes/code.rs @@ -8,6 +8,7 @@ use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matc pub struct Code { pub lang: String, pub code: String, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/divider.rs b/src/nodes/divider.rs index 3a9b93d..a458948 100644 --- a/src/nodes/divider.rs +++ b/src/nodes/divider.rs @@ -6,6 +6,7 @@ use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matc #[derive(Debug, PartialEq, Serialize)] pub struct Divider { + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/embed.rs b/src/nodes/embed.rs index 6199bba..96449eb 100644 --- a/src/nodes/embed.rs +++ b/src/nodes/embed.rs @@ -8,6 +8,7 @@ use crate::toolkit::{deserializer::Deserializer, matcher::Matcher, node::Node}; pub struct Embed { pub url: String, pub kind: String, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/heading.rs b/src/nodes/heading.rs index e083d58..d00ee62 100644 --- a/src/nodes/heading.rs +++ b/src/nodes/heading.rs @@ -8,6 +8,7 @@ use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matc pub struct Heading { pub level: u8, pub text: String, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/highlight.rs b/src/nodes/highlight.rs index d5889be..d432f97 100644 --- a/src/nodes/highlight.rs +++ b/src/nodes/highlight.rs @@ -12,6 +12,7 @@ use crate::toolkit::{ use super::paragraph::Paragraph; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum HighlightNodes { Paragraph(Paragraph), } @@ -43,6 +44,7 @@ pub struct Highlight { pub header: Option, pub icon: Option, pub nodes: Vec, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/image.rs b/src/nodes/image.rs index f13e1ea..58f3101 100644 --- a/src/nodes/image.rs +++ b/src/nodes/image.rs @@ -7,7 +7,8 @@ use crate::toolkit::{context::Context, deserializer::Deserializer, matcher::Matc #[derive(Debug, PartialEq, Serialize)] pub struct Image { pub alt: String, - pub url: String, + pub src: String, + #[serde(skip_serializing)] consumed_all_input: bool, } @@ -15,7 +16,7 @@ impl Image { pub fn new>(consumed_all_input: bool, alt: S, url: S) -> Self { Self { alt: alt.into(), - url: url.into(), + src: url.into(), consumed_all_input, } } @@ -29,14 +30,14 @@ impl Display for Image { "\n\n" }; - write!(f, "![{}]({}){}", self.alt, self.url, end) + write!(f, "![{}]({}){}", self.alt, self.src, end) } } impl Node for Image { fn len(&self) -> usize { let end = if self.consumed_all_input { 1 } else { 2 }; - self.alt.len() + self.url.len() + 5 + end + self.alt.len() + self.src.len() + 5 + end } } diff --git a/src/nodes/image_gallery.rs b/src/nodes/image_gallery.rs index 7e18222..adfad36 100644 --- a/src/nodes/image_gallery.rs +++ b/src/nodes/image_gallery.rs @@ -12,6 +12,7 @@ use crate::toolkit::{ use super::image::Image; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum ImageGalleryNodes { Image(Image), } @@ -43,6 +44,7 @@ impl From for ImageGalleryNodes { #[derive(Debug, PartialEq, Serialize)] pub struct ImageGallery { pub nodes: Vec, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/list.rs b/src/nodes/list.rs index 1b56a14..19415a8 100644 --- a/src/nodes/list.rs +++ b/src/nodes/list.rs @@ -18,6 +18,7 @@ pub enum ListTypes { } #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum ListNodes { ListItem(ListItem), } @@ -49,6 +50,7 @@ pub struct List { pub list_type: ListTypes, pub level: usize, pub nodes: Vec, + #[serde(skip_serializing)] consumed_all_input: bool, } diff --git a/src/nodes/list_item_content.rs b/src/nodes/list_item_content.rs index 3b10af7..10b1106 100644 --- a/src/nodes/list_item_content.rs +++ b/src/nodes/list_item_content.rs @@ -15,6 +15,7 @@ use super::{ }; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum ListItemContentNodes { A(Anchor), B(Bold), @@ -88,6 +89,7 @@ impl Node for ListItemContentNodes { #[derive(Debug, PartialEq, Serialize)] pub struct ListItemContent { + #[serde(skip_serializing)] consumed_all_input: bool, pub nodes: Vec, } diff --git a/src/nodes/paragraph.rs b/src/nodes/paragraph.rs index 0ccc406..49601cf 100644 --- a/src/nodes/paragraph.rs +++ b/src/nodes/paragraph.rs @@ -14,6 +14,7 @@ use crate::toolkit::{ }; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum ParagraphNodes { A(Anchor), B(Bold), @@ -87,6 +88,7 @@ impl Node for ParagraphNodes { #[derive(Debug, PartialEq, Serialize)] pub struct Paragraph { + #[serde(skip_serializing)] consumed_all_input: bool, pub nodes: Vec, } diff --git a/src/nodes/yamd.rs b/src/nodes/yamd.rs index cadc36b..d279698 100644 --- a/src/nodes/yamd.rs +++ b/src/nodes/yamd.rs @@ -16,6 +16,7 @@ use super::{ }; #[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum YamdNodes { P(Paragraph), H(Heading),