rushdown/lib.rs
1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4extern crate alloc;
5
6pub mod ast;
7pub mod context;
8pub mod parser;
9pub mod renderer;
10pub mod test;
11pub mod text;
12pub mod util;
13
14#[cfg(feature = "html-entities")]
15mod html_entity;
16
17mod scanner;
18
19mod error;
20use alloc::string::String;
21pub use error::Error;
22pub use error::Result;
23
24use crate::parser::Parser;
25use crate::parser::ParserExtension;
26use crate::renderer::html;
27use crate::renderer::TextWrite;
28use crate::text::BasicReader;
29
30/// Trait for converting Markdown to HTML.
31///
32/// # Errors
33/// Parsing phase will never fail, so the only possible errors are I/O errors during rendering.
34pub trait MarkdownToHtml<W: TextWrite = String> {
35 /// Converts the given Markdown source to HTML and writes it to the output.
36 fn markdown_to_html(&self, out: &mut W, source: &str) -> Result<()>;
37}
38
39impl<W: TextWrite, F> MarkdownToHtml<W> for F
40where
41 F: Fn(&mut W, &str) -> Result<()>,
42{
43 fn markdown_to_html(&self, out: &mut W, source: &str) -> Result<()> {
44 (self)(out, source)
45 }
46}
47
48/// Creates a function that converts Markdown to HTML using the specified parser and renderer.
49///
50/// # Arguments
51/// - `parser_options`: Options for the Markdown parser.
52/// - `renderer_options`: Options for the HTML renderer.
53/// - `parser_extension`: Extension for the Markdown parser. If no extensions are needed, use [`crate::parser::NO_EXTENSIONS`].
54/// - `renderer_extension`: Extension for the HTML renderer. If no extensions are needed, use [`crate::renderer::html::NO_EXTENSIONS`].
55///
56/// # Examples
57/// ```
58/// use core::fmt::Write;
59/// use rushdown::{
60/// new_markdown_to_html,
61/// parser::{self, ParserExtension},
62/// renderer::html::{self, RendererExtension},
63/// Result,
64/// };
65///
66/// let markdown_to_html = new_markdown_to_html(
67/// parser::Options::default(),
68/// html::Options::default(),
69/// parser::gfm_table().and(parser::gfm_task_list_item()),
70/// html::NO_EXTENSIONS,
71/// );
72/// let mut output = String::new();
73/// let input = "# Hello, World!\n\nThis is a **Markdown** document.";
74/// match markdown_to_html(&mut output, input) {
75/// Ok(_) => {
76/// println!("HTML output:\n{}", output);
77/// }
78/// Err(e) => {
79/// println!("Error: {:?}", e);
80/// }
81/// }
82/// ```
83pub fn new_markdown_to_html<'r, W>(
84 parser_options: parser::Options,
85 renderer_options: html::Options,
86 parser_extension: impl ParserExtension,
87 renderer_extension: impl html::RendererExtension<'r, W>,
88) -> impl Fn(&mut W, &str) -> Result<()> + 'r
89where
90 W: TextWrite + 'r,
91{
92 let parser = Parser::with_extensions(parser_options, parser_extension);
93 let renderer = html::Renderer::<'r, W>::with_extensions(renderer_options, renderer_extension);
94 move |output: &mut W, source: &str| {
95 let mut reader = BasicReader::new(source);
96 let (arena, document_ref) = parser.parse(&mut reader);
97 renderer.render(output, source, &arena, document_ref)
98 }
99}
100
101/// Creates a function that converts Markdown to HTML using the specified parser and renderer,
102/// with output written to a `String`.
103pub fn new_markdown_to_html_string<'r>(
104 parser_options: parser::Options,
105 renderer_options: html::Options,
106 parser_extension: impl ParserExtension,
107 renderer_extension: impl html::RendererExtension<'r, String>,
108) -> impl Fn(&mut String, &str) -> Result<()> + 'r {
109 new_markdown_to_html::<String>(
110 parser_options,
111 renderer_options,
112 parser_extension,
113 renderer_extension,
114 )
115}
116
117/// Converts Markdown(CommonMark) to HTML using default parser and renderer options.
118///
119/// # Examples
120/// ```
121/// use rushdown::markdown_to_html_string;
122/// let mut output = String::new();
123/// let input = "# Hello, World!\n\nThis is a **Markdown** document.";
124/// match markdown_to_html_string(&mut output, input) {
125/// Ok(_) => {
126/// println!("HTML output:\n{}", output);
127/// }
128/// Err(e) => {
129/// println!("Error: {:?}", e);
130/// }
131/// };
132/// ```
133pub fn markdown_to_html_string(output: &mut String, source: &str) -> Result<()> {
134 let parser = Parser::with_options(parser::Options::default());
135 let renderer = html::Renderer::with_options(html::Options::default());
136 let mut reader = BasicReader::new(source);
137 let (arena, document_ref) = parser.parse(&mut reader);
138 renderer.render(output, source, &arena, document_ref)
139}
140
141// macros {{{
142
143/// Helper macro to match kind data.
144///
145/// # Examples
146/// ```
147/// use rushdown::ast::{Arena, NodeRef, KindData, Paragraph};
148/// use rushdown::matches_kind;
149///
150/// let mut arena = Arena::new();
151/// let para_ref: NodeRef = arena.new_node(Paragraph::new());
152/// assert!(matches_kind!(arena, para_ref, Paragraph));
153/// assert!(matches_kind!(arena[para_ref], Paragraph));
154/// ```
155#[macro_export]
156macro_rules! matches_kind {
157 ($arena:expr, $node_ref:expr, $variant:ident) => {
158 matches!(
159 $arena[$node_ref].kind_data(),
160 $crate::ast::KindData::$variant(_)
161 )
162 };
163 ($node:expr, $variant:ident) => {
164 matches!($node.kind_data(), $crate::ast::KindData::$variant(_))
165 };
166}
167
168/// Helper macro to match extension kind.
169///
170/// # Examples
171/// ```
172/// use core::fmt::{self, Write};
173/// use rushdown::ast::{Arena, NodeRef, NodeType, NodeKind, KindData, PrettyPrint, pp_indent};
174/// use rushdown::matches_extension_kind;
175///
176/// #[derive(Debug)]
177/// struct Admonition {
178/// kind: String,
179/// }
180///
181/// impl NodeKind for Admonition {
182/// fn typ(&self) -> NodeType { NodeType::ContainerBlock }
183///
184/// fn kind_name(&self) -> &'static str { "Admonition" }
185/// }
186///
187/// impl PrettyPrint for Admonition {
188/// fn pretty_print(&self, w: &mut dyn Write, _source: &str, level: usize) -> fmt::Result {
189/// writeln!(w, "{}kind: {}", pp_indent(level), self.kind)
190/// }
191/// }
192///
193/// impl From<Admonition> for KindData {
194/// fn from(e: Admonition) -> Self { KindData::Extension(Box::new(e)) }
195/// }
196///
197/// let mut arena = Arena::new();
198/// let ext_ref: NodeRef = arena.new_node(Admonition{kind: "note".to_string()});
199/// assert!(matches_extension_kind!(arena, ext_ref, Admonition));
200/// assert!(matches_extension_kind!(arena[ext_ref], Admonition));
201/// ```
202///
203#[macro_export]
204macro_rules! matches_extension_kind {
205 ($arena:expr, $ref:expr, $ext_type:ty) => {
206 (if let $crate::ast::KindData::Extension(ref d) = $arena[$ref].kind_data() {
207 (d.as_ref() as &dyn ::core::any::Any)
208 .downcast_ref::<$ext_type>()
209 .is_some()
210 } else {
211 false
212 })
213 };
214 ($node:expr, $ext_type:ty) => {
215 (if let $crate::ast::KindData::Extension(ref d) = $node.kind_data() {
216 (d.as_ref() as &dyn ::core::any::Any)
217 .downcast_ref::<$ext_type>()
218 .is_some()
219 } else {
220 false
221 })
222 };
223}
224
225/// Helper macro to downcast extension data.
226///
227/// # Examples
228/// ```
229/// use core::fmt::{self, Write};
230/// use rushdown::ast::{Arena, NodeRef, NodeType, NodeKind, KindData, PrettyPrint, pp_indent};
231/// use rushdown::as_extension_data;
232///
233/// #[derive(Debug)]
234/// struct Admonition {
235/// kind: String,
236/// }
237///
238/// impl NodeKind for Admonition {
239/// fn typ(&self) -> NodeType { NodeType::ContainerBlock }
240///
241/// fn kind_name(&self) -> &'static str { "Admonition" }
242/// }
243///
244/// impl PrettyPrint for Admonition {
245/// fn pretty_print(&self, w: &mut dyn Write, _source: &str, level: usize) -> fmt::Result {
246/// writeln!(w, "{}kind: {}", pp_indent(level), self.kind)
247/// }
248/// }
249///
250/// impl From<Admonition> for KindData {
251/// fn from(e: Admonition) -> Self { KindData::Extension(Box::new(e)) }
252/// }
253///
254/// let mut arena = Arena::new();
255/// let ext_ref: NodeRef = arena.new_node(Admonition{kind: "note".to_string()});
256/// let ext_data = as_extension_data!(arena, ext_ref, Admonition);
257/// assert_eq!(ext_data.kind, "note");
258/// let ext_data = as_extension_data!(arena[ext_ref], Admonition);
259/// assert_eq!(ext_data.kind, "note");
260/// ```
261///
262#[macro_export]
263macro_rules! as_extension_data {
264 ($arena:expr, $ref:expr, $ext_type:ty) => {
265 (if let $crate::ast::KindData::Extension(ref d) = $arena[$ref].kind_data() {
266 (d.as_ref() as &dyn ::core::any::Any)
267 .downcast_ref::<$ext_type>()
268 .expect("Failed to downcast extension data")
269 } else {
270 panic!("Node is not an extension node")
271 })
272 };
273 ($node:expr, $ext_type:ty) => {
274 (if let $crate::ast::KindData::Extension(ref d) = $node.kind_data() {
275 (d.as_ref() as &dyn ::core::any::Any)
276 .downcast_ref::<$ext_type>()
277 .expect("Failed to downcast extension data")
278 } else {
279 panic!("Node is not an extension node")
280 })
281 };
282}
283
284/// Helper macro to downcast mutable extension data.
285///
286/// See [`as_extension_data!`] for examples.
287#[macro_export]
288macro_rules! as_extension_data_mut {
289 ($arena:expr, $ref:expr, $ext_type:ty) => {
290 (if let $crate::ast::KindData::Extension(ref mut d) = $arena[$ref].kind_data_mut() {
291 (d.as_mut() as &mut dyn ::core::any::Any)
292 .downcast_mut::<$ext_type>()
293 .expect("Failed to downcast extension data")
294 } else {
295 panic!("Node is not an extension node")
296 })
297 };
298 ($node:expr, $ext_type:ty) => {
299 (if let $crate::ast::KindData::Extension(ref mut d) = $node.kind_data_mut() {
300 (d.as_mut() as &mut dyn ::core::any::Any)
301 .downcast_mut::<$ext_type>()
302 .expect("Failed to downcast extension data")
303 } else {
304 panic!("Node is not an extension node")
305 })
306 };
307}
308
309/// Helper macro to work with kind data.
310///
311/// # Examples
312/// ```
313/// use rushdown::ast::{Arena, NodeRef, KindData, Emphasis};
314/// use rushdown::as_kind_data;
315///
316/// let mut arena = Arena::new();
317/// let para_ref: NodeRef = arena.new_node(Emphasis::new(1));
318/// let data = as_kind_data!(arena, para_ref, Emphasis);
319/// assert_eq!(data.level(), 1);
320/// let data = as_kind_data!(arena[para_ref], Emphasis);
321/// assert_eq!(data.level(), 1);
322/// ```
323#[macro_export]
324macro_rules! as_kind_data {
325 ($arena:expr, $node_ref:expr, $variant:ident) => {
326 (if let $crate::ast::KindData::$variant(ref d) = $arena[$node_ref].kind_data() {
327 d
328 } else {
329 panic!(
330 "Expected kind data variant {} but found {:?}",
331 stringify!($variant),
332 $arena[$node_ref].kind_data()
333 )
334 })
335 };
336 ($node:expr, $variant:ident) => {
337 (if let $crate::ast::KindData::$variant(ref d) = $node.kind_data() {
338 d
339 } else {
340 panic!(
341 "Expected kind data variant {} but found {:?}",
342 stringify!($variant),
343 $node.kind_data()
344 )
345 })
346 };
347}
348
349/// Helper macro to work with mutable kind data.
350///
351/// See [`as_kind_data!`] for examples.
352#[macro_export]
353macro_rules! as_kind_data_mut {
354 ($arena:expr, $node_ref:expr, $variant:ident) => {
355 (if let $crate::ast::KindData::$variant(ref mut d) = $arena[$node_ref].kind_data_mut() {
356 d
357 } else {
358 panic!(
359 "Expected kind data variant {} but found {:?}",
360 stringify!($variant),
361 $arena[$node_ref].kind_data()
362 )
363 })
364 };
365 ($node:expr, $variant:ident) => {
366 (if let $crate::ast::KindData::$variant(ref mut d) = $node.kind_data_mut() {
367 d
368 } else {
369 panic!(
370 "Expected kind data variant {} but found {:?}",
371 stringify!($variant),
372 $node.kind_data()
373 )
374 })
375 };
376}
377
378/// Helper macro to work with type data.
379///
380/// # Examples
381/// ```
382/// use rushdown::ast::{Arena, NodeRef, TypeData, Block, Paragraph};
383/// use rushdown::as_type_data;
384///
385/// let mut arena = Arena::new();
386/// let para_ref: NodeRef = arena.new_node(Paragraph::new());
387/// let data = as_type_data!(arena, para_ref, Block);
388/// assert!(data.lines().is_empty());
389/// let data = as_type_data!(arena[para_ref], Block);
390/// assert!(data.lines().is_empty());
391/// ```
392///
393#[macro_export]
394macro_rules! as_type_data {
395 ($arena:expr, $node_ref:expr, $variant:ident) => {
396 (if let $crate::ast::TypeData::$variant(ref d) = $arena[$node_ref].type_data() {
397 d
398 } else {
399 panic!(
400 "Expected type data variant {} but found {:?}",
401 stringify!($variant),
402 $arena[$node_ref].type_data()
403 )
404 })
405 };
406 ($node:expr, $variant:ident) => {
407 (if let $crate::ast::TypeData::$variant(ref d) = $node.type_data() {
408 d
409 } else {
410 panic!(
411 "Expected type data variant {} but found {:?}",
412 stringify!($variant),
413 $node.type_data()
414 )
415 })
416 };
417}
418
419/// Helper macro to work with mutable type data.
420///
421/// See [`as_type_data!`] for examples.
422#[macro_export]
423macro_rules! as_type_data_mut {
424 ($arena:expr, $node_ref:expr, $variant:ident) => {
425 (if let $crate::ast::TypeData::$variant(ref mut d) = $arena[$node_ref].type_data_mut() {
426 d
427 } else {
428 panic!(
429 "Expected type data variant {} but found {:?}",
430 stringify!($variant),
431 $arena[$node_ref].type_data()
432 )
433 })
434 };
435 ($node:expr, $variant:ident) => {
436 (if let $crate::ast::TypeData::$variant(ref mut d) = $node.type_data_mut() {
437 d
438 } else {
439 panic!(
440 "Expected type data variant {} but found {:?}",
441 stringify!($variant),
442 $node.type_data()
443 )
444 })
445 };
446}
447
448// }}} macros
449
450// debug stuff {{{
451
452#[cfg(all(not(feature = "std"), feature = "no-std-unix-debug"))]
453pub mod debug {
454 extern crate libc;
455 use core::fmt::{self, Write};
456
457 #[allow(dead_code)]
458 pub struct Stdout;
459
460 impl Write for Stdout {
461 fn write_str(&mut self, s: &str) -> fmt::Result {
462 unsafe {
463 libc::write(1, s.as_ptr() as *const _, s.len());
464 }
465 Ok(())
466 }
467 }
468
469 #[macro_export]
470 macro_rules! print {
471 ($($arg:tt)*) => {{
472 use core::fmt::Write;
473 let mut out = $crate::debug::Stdout;
474 core::write!(&mut out, $($arg)*).ok();
475 }};
476 }
477
478 #[macro_export]
479 macro_rules! println {
480 ($($arg:tt)*) => {{
481 $crate::print!("{}\n", format_args!($($arg)*));
482 }};
483 }
484}
485
486// }}}