Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update readme #41

Merged
merged 4 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
110 changes: 43 additions & 67 deletions src/nodes/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Paragraph> for HighlightNodes {
fn from(value: Paragraph) -> Self {
Self::Paragraph(value)
}
}

#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct Highlight {
pub header: Option<String>,
pub icon: Option<String>,
pub nodes: Vec<HighlightNodes>,
pub nodes: Vec<Paragraph>,
}

impl Highlight {
pub fn new<H: Into<String>, I: Into<String>>(
header: Option<H>,
icon: Option<I>,
nodes: Vec<HighlightNodes>,
nodes: Vec<Paragraph>,
) -> Self {
Self {
header: header.map(|header| header.into()),
Expand Down Expand Up @@ -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.is_empty() {
0
} else {
(self.nodes.len() - 1) * 2
};
self.nodes.iter().map(|node| node.len()).sum::<usize>()
+ delimiter_length
+ self.get_outer_token_length()
}
}

impl Branch<HighlightNodes> for Highlight {
fn push<CanBeNode: Into<HighlightNodes>>(&mut self, node: CanBeNode) {
self.nodes.push(node.into());
}

fn get_maybe_nodes() -> Vec<MaybeNode<HighlightNodes>> {
vec![Paragraph::maybe_node()]
}

fn get_fallback_node() -> Option<DefinitelyNode<HighlightNodes>> {
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 {
Expand All @@ -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::<Vec<Paragraph>>(),
));
}

None
Expand All @@ -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;

Expand Down Expand Up @@ -235,6 +187,30 @@ mod tests {
fn empty_highlight() {
let highlight = Highlight::new::<String, String>(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(),
]
)
);
}
}
49 changes: 38 additions & 11 deletions src/nodes/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct Metadata {
pub preview: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip)]
pub consumed_length: Option<usize>,
}

impl Metadata {
Expand All @@ -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<Self> {
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);
}

Expand All @@ -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())
}
}

Expand Down Expand Up @@ -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)
Expand All @@ -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)
})
);
}

Expand All @@ -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)
})
);
}

Expand All @@ -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());
}
}
29 changes: 22 additions & 7 deletions src/nodes/yamd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -523,4 +524,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);
}
}
2 changes: 1 addition & 1 deletion src/toolkit/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()..]
Expand Down
6 changes: 6 additions & 0 deletions src/toolkit/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
Loading