yamd/
lib.rs

1//! YAMD - Yet Another Markdown Document (flavour)
2//!
3//! Simplified version of [CommonMark](https://spec.commonmark.org/).
4//!
5//! For formatting check [`YAMD`](nodes::Yamd) struct documentation.
6//!
7//! # Reasoning
8//!
9//! Simplified set of rules allows to have simpler, more efficient, parser and renderer.
10//! [YAMD](nodes::Yamd) does not provide render functionality, instead it is a [serde]
11//! serializable structure that allows you to write any renderer for that structure. All HTML
12//! equivalents in this doc are provided as an example to what it can be rendered.
13//!
14//! # Difference from CommonMark
15//!
16//! While YAMD tries to utilize as much CommonMark syntax as possible, there are differences.
17//!
18//! ## Escaping
19//!
20//! Escaping done on a [lexer] level. Every symbol following the `\` symbol will be treated as a
21//! [literal](lexer::TokenKind::Literal).
22//!
23//! Example:
24//!
25//! | YAMD      | HTML equivalent |
26//! |-----------|-----------------|
27//! | `\**foo**`|`<p>**foo**</p>` |
28//!
29//! ## Precedence
30//!
31//! [CommonMark](https://spec.commonmark.org/0.31.2/#precedence) defines container blocks and leaf
32//! blocks. And that container block indicator has higher precedence. YAMD does not discriminate by
33//! block type, every node (block) is the same. In practice, there are no additional rules to encode
34//! and remember.
35//!
36//! Example:
37//!
38//! | YAMD                  | HTML equivalent                               |
39//! |-----------------------|-----------------------------------------------|
40//! | ``- `one\n- two` ``   | `<ol><li><code>one\n- two</code></li></ol>`   |
41//!
42//!
43//! If you want to have two [ListItem](nodes::ListItem)'s use escaping:
44//!
45//! | YAMD                      | HTML equivalent                           |
46//! |---------------------------|-------------------------------------------|
47//! | ``- \`one\n- two\` ``     | ``<ol><li>`one</li><li>two`</li><ol>``    |
48//!
49//! The reasoning is that those kind issues can be caught with tooling like linters/lsp. That tooling
50//! does not exist yet.
51//!
52//! ## Nodes
53//!
54//! List of supported [nodes] and their formatting. The best starting point is [YAMD](nodes::Yamd).
55//!
56//! # MSRV
57//!
58//! YAMD minimal supported Rust version is 1.80.0 due to [Option::take_if] usage
59
60#[deny(missing_docs, rustdoc::broken_intra_doc_links)]
61pub mod lexer;
62pub mod nodes;
63mod parser;
64
65#[doc(inline)]
66pub use nodes::Yamd;
67use parser::{yamd, Parser};
68
69/// Deserialize a string into a Yamd struct
70/// # Example
71/// ```
72/// use yamd::deserialize;
73/// let input = "# header";
74/// let yamd = deserialize(input);
75/// ```
76pub fn deserialize(str: &str) -> Yamd {
77    let mut p = Parser::new(str);
78    yamd(&mut p, |_| false)
79}
80
81#[cfg(test)]
82mod tests {
83    use pretty_assertions::assert_eq;
84
85    use crate::{
86        deserialize,
87        nodes::{Anchor, Heading, Paragraph, Yamd},
88    };
89
90    #[test]
91    fn test_deserialize() {
92        let input = "# header";
93        let expected = Yamd::new(
94            None,
95            vec![Heading::new(1, vec![String::from("header").into()]).into()],
96        );
97        let actual = deserialize(input);
98        assert_eq!(expected, actual);
99    }
100
101    #[test]
102    fn deserialize_text_containing_utf8() {
103        let input = "## 🤔\n\n[link 😉](url)";
104        let expected = Yamd::new(
105            None,
106            vec![
107                Heading::new(2, vec![String::from("🤔").into()]).into(),
108                Paragraph::new(vec![Anchor::new("link 😉", "url").into()]).into(),
109            ],
110        );
111        let actual = deserialize(input);
112        assert_eq!(expected, actual);
113    }
114}