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}