note_mark/
lib.rs

1//! note-mark is a markdown parser. It is a library that can be used to parse
2//! markdown into HTML.
3//!
4//! # Example
5//!
6//! ```
7//! use note_mark::prelude::*;
8//!
9//! let markdown = Markdown::default();
10//!
11//! let html = markdown.execute("# Hello, world!");
12//!
13//! assert_eq!(html, "<h1>Hello, world!</h1>");
14//!
15//! let html = markdown.execute("# Hello, world!\n\nThis is a paragraph.");
16//!
17//! assert_eq!(html, "<h1>Hello, world!</h1><p>This is a paragraph.</p>");
18//! ```
19
20pub mod layer;
21pub mod model;
22pub mod prelude;
23
24use layer::{
25    lexer::lex, parser::Parser, stringifier::Stringifier, toc::TocMaker, transformer::Transformer,
26};
27
28/// Markdown parser and transformer.
29///
30/// # Example
31///
32/// ```
33/// use note_mark::prelude::*;
34///
35/// let markdown = Markdown::default();
36///
37/// let html = markdown.execute("# Hello, world!\n\nThis is a paragraph.");
38///
39/// assert_eq!(html, "<h1>Hello, world!</h1><p>This is a paragraph.</p>");
40/// ```
41#[derive(Debug, Clone, Default)]
42pub struct Markdown {
43    /// Parser configuration.
44    parser: Parser,
45    /// Transformer configuration.
46    transformer: Transformer,
47    /// Stringifier configuration.
48    stringifier: Stringifier,
49    /// Table of contents maker configuration.
50    toc_maker: TocMaker,
51}
52
53impl Markdown {
54    /// Create a new `Markdown` instance.
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    /// Set the parser configuration.
60    pub fn parser(mut self, parser: Parser) -> Self {
61        self.parser = parser;
62        self
63    }
64
65    /// Set the transformer configuration.
66    pub fn transformer(mut self, transformer: Transformer) -> Self {
67        self.transformer = transformer;
68        self
69    }
70
71    /// Set the stringifier configuration.
72    pub fn stringifier(mut self, stringifier: Stringifier) -> Self {
73        self.stringifier = stringifier;
74        self
75    }
76
77    /// Set the table of contents maker configuration.
78    pub fn toc_maker(mut self, toc_maker: TocMaker) -> Self {
79        self.toc_maker = toc_maker;
80        self
81    }
82}
83
84impl Markdown {
85    /// Execute the markdown parser.
86    pub fn execute(&self, input: &str) -> String {
87        let tokens = lex(input);
88        let tree = self.parser.parse(input, tokens);
89        let document = self.transformer.transform(tree);
90        self.stringifier.stringify(document)
91    }
92
93    /// Execute the markdown parser and generate the table of contents.
94    ///
95    /// # Example
96    ///
97    /// ```
98    /// use note_mark::prelude::*;
99    ///
100    /// let markdown = Markdown::default();
101    ///
102    /// let input = concat![
103    ///     "# Headline1-1\n\n",
104    ///     "# Headline1-2\n\n",
105    ///     "## Headline2-1\n\n",
106    ///     "## Headline2-2\n\n",
107    ///     "# Headline1-3\n\n",
108    /// ];
109    ///
110    /// let (html, toc) = markdown.execute_with_toc(input);
111    ///
112    /// assert_eq!(toc, "<ul><li><a href=\"#Headline1-1\">Headline1-1</a></li><li><a href=\"#Headline1-2\">Headline1-2</a><ul><li><a href=\"#Headline2-1\">Headline2-1</a></li><li><a href=\"#Headline2-2\">Headline2-2</a></li></ul></li><li><a href=\"#Headline1-3\">Headline1-3</a></li></ul>");
113    /// ```
114    /// ## Original output
115    ///
116    /// ```html
117    /// <h1 id="Headline1-1">Headline1-1</h1>
118    /// <h1 id="Headline1-2">Headline1-2</h1>
119    /// <h2 id="Headline2-1">Headline2-1</h2>
120    /// <h2 id="Headline2-2">Headline2-2</h2>
121    /// <h1 id="Headline1-3">Headline1-3</h1>
122    /// ```
123    ///
124    /// ## Toc output
125    ///
126    /// ```html
127    /// <ul>
128    ///     <li><a href="#Headline1-1">Headline1-1</a></li>
129    ///     <li>
130    ///         <a href="#Headline1-2">Headline1-2</a>
131    ///         <ul>
132    ///             <li><a href="#Headline2-1">Headline2-1</a></li>
133    ///             <li><a href="#Headline2-2">Headline2-2</a></li>
134    ///         </ul>
135    ///     </li>
136    ///     <li><a href="#Headline1-3">Headline1-3</a></li>
137    /// </ul>
138    /// ```
139    pub fn execute_with_toc(&self, input: &str) -> (String, String) {
140        let tokens = lex(input);
141        let tree = self.parser.parse(input, tokens);
142        let mut document = self.transformer.transform(tree);
143
144        let toc = self.toc_maker.make_toc(&mut document);
145
146        (
147            self.stringifier.stringify(document),
148            self.stringifier.stringify(toc),
149        )
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_markdown() {
159        let input = concat![
160            "# Hello World\n\n",
161            "This is **TEST**\n\n",
162            "## Goodbye\n",
163            "I'm happy\n\n",
164            "See you\n",
165            "again\n"
166        ];
167
168        let output = Markdown::default().execute(input);
169
170        assert_eq!(
171            &output,
172            "<h1>Hello World</h1><p>This is <strong>TEST</strong></p><h2>Goodbye<br>I'm happy</h2><p>See you<br>again</p>"
173        );
174    }
175
176    #[test]
177    fn test_markdown2() {
178        let input = concat![
179            "- Hello\n",
180            "- World\n",
181            "  - Change the **world**\n",
182            "  - Great!\n",
183            "    1. Yeah\n",
184            "    1. Wryyyyy\n",
185            "- End of the world\n"
186        ];
187
188        let output = Markdown::default().execute(input);
189
190        assert_eq!(
191            &output,
192            "<ul><li>Hello</li><li>World<ul><li>Change the <strong>world</strong></li><li>Great!<ol><li>Yeah</li><li>Wryyyyy</li></ol></li></ul></li><li>End of the world</li></ul>")
193    }
194
195    #[test]
196    fn test_markdown3() {
197        let input = concat![
198            "- AAA\n",
199            "- BBB\n",
200            "- CCC\n",
201            "\n",
202            "Happy\n",
203            "\n",
204            "> Ok!\n",
205            "> Good!\n",
206            ">\n",
207            "> - Yeah\n",
208            "> - Wryyyyy\n",
209            ">   - Change the **world**\n",
210            ">\n",
211            "End of the world\n",
212        ];
213
214        let output = Markdown::default().execute(input);
215
216        assert_eq!(
217            &output,
218            "<ul><li>AAA</li><li>BBB</li><li>CCC</li></ul><p>Happy</p><blockquote><p>Ok!<br>Good!</p><ul><li>Yeah</li><li>Wryyyyy<ul><li>Change the <strong>world</strong></li></ul></li></ul></blockquote><p>End of the world</p>")
219    }
220}