#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
pub mod ast;
pub mod context;
pub mod parser;
pub mod renderer;
pub mod test;
pub mod text;
pub mod util;
#[cfg(feature = "html-entities")]
mod html_entity;
mod scanner;
mod error;
use alloc::string::String;
pub use error::{Error, Result};
use crate::{
parser::{Parser, ParserExtension},
renderer::{html, TextWrite},
text::BasicReader,
};
pub trait MarkdownToHtml<W: TextWrite = String> {
fn markdown_to_html(&self, out: &mut W, source: &str) -> Result<()>;
}
impl<W: TextWrite, F> MarkdownToHtml<W> for F
where
F: Fn(&mut W, &str) -> Result<()>,
{
fn markdown_to_html(&self, out: &mut W, source: &str) -> Result<()> {
(self)(out, source)
}
}
pub fn new_markdown_to_html<'r, W>(
parser_options: parser::Options,
renderer_options: html::Options,
parser_extension: impl ParserExtension,
renderer_extension: impl html::RendererExtension<'r, W>,
) -> impl Fn(&mut W, &str) -> Result<()> + 'r
where
W: TextWrite + 'r,
{
let parser = Parser::with_extensions(parser_options, parser_extension);
let renderer = html::Renderer::<'r, W>::with_extensions(renderer_options, renderer_extension);
move |output: &mut W, source: &str| {
let mut reader = BasicReader::new(source);
let (arena, document_ref) = parser.parse(&mut reader);
renderer.render(output, source, &arena, document_ref)
}
}
pub fn new_markdown_to_html_string<'r>(
parser_options: parser::Options,
renderer_options: html::Options,
parser_extension: impl ParserExtension,
renderer_extension: impl html::RendererExtension<'r, String>,
) -> impl Fn(&mut String, &str) -> Result<()> + 'r {
new_markdown_to_html::<String>(
parser_options,
renderer_options,
parser_extension,
renderer_extension,
)
}
pub fn markdown_to_html_string(output: &mut String, source: &str) -> Result<()> {
let parser = Parser::with_options(parser::Options::default());
let renderer = html::Renderer::with_options(html::Options::default());
let mut reader = BasicReader::new(source);
let (arena, document_ref) = parser.parse(&mut reader);
renderer.render(output, source, &arena, document_ref)
}
#[macro_export]
macro_rules! matches_kind {
($arena:expr, $node_ref:expr, $variant:ident) => {
matches!(
$arena[$node_ref].kind_data(),
$crate::ast::KindData::$variant(_)
)
};
($node:expr, $variant:ident) => {
matches!($node.kind_data(), $crate::ast::KindData::$variant(_))
};
}
#[macro_export]
macro_rules! matches_extension_kind {
($arena:expr, $ref:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref d) = $arena[$ref].kind_data() {
(d.as_ref() as &dyn ::core::any::Any)
.downcast_ref::<$ext_type>()
.is_some()
} else {
false
})
};
($node:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref d) = $node.kind_data() {
(d.as_ref() as &dyn ::core::any::Any)
.downcast_ref::<$ext_type>()
.is_some()
} else {
false
})
};
}
#[macro_export]
macro_rules! as_extension_data {
($arena:expr, $ref:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref d) = $arena[$ref].kind_data() {
(d.as_ref() as &dyn ::core::any::Any)
.downcast_ref::<$ext_type>()
.expect("Failed to downcast extension data")
} else {
panic!("Node is not an extension node")
})
};
($node:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref d) = $node.kind_data() {
(d.as_ref() as &dyn ::core::any::Any)
.downcast_ref::<$ext_type>()
.expect("Failed to downcast extension data")
} else {
panic!("Node is not an extension node")
})
};
}
#[macro_export]
macro_rules! as_extension_data_mut {
($arena:expr, $ref:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref mut d) = $arena[$ref].kind_data_mut() {
(d.as_mut() as &mut dyn ::core::any::Any)
.downcast_mut::<$ext_type>()
.expect("Failed to downcast extension data")
} else {
panic!("Node is not an extension node")
})
};
($node:expr, $ext_type:ty) => {
(if let $crate::ast::KindData::Extension(ref mut d) = $node.kind_data_mut() {
(d.as_mut() as &mut dyn ::core::any::Any)
.downcast_mut::<$ext_type>()
.expect("Failed to downcast extension data")
} else {
panic!("Node is not an extension node")
})
};
}
#[macro_export]
macro_rules! as_kind_data {
($arena:expr, $node_ref:expr, $variant:ident) => {
(if let $crate::ast::KindData::$variant(ref d) = $arena[$node_ref].kind_data() {
d
} else {
panic!(
"Expected kind data variant {} but found {:?}",
stringify!($variant),
$arena[$node_ref].kind_data()
)
})
};
($node:expr, $variant:ident) => {
(if let $crate::ast::KindData::$variant(ref d) = $node.kind_data() {
d
} else {
panic!(
"Expected kind data variant {} but found {:?}",
stringify!($variant),
$node.kind_data()
)
})
};
}
#[macro_export]
macro_rules! as_kind_data_mut {
($arena:expr, $node_ref:expr, $variant:ident) => {
(if let $crate::ast::KindData::$variant(ref mut d) = $arena[$node_ref].kind_data_mut() {
d
} else {
panic!(
"Expected kind data variant {} but found {:?}",
stringify!($variant),
$arena[$node_ref].kind_data()
)
})
};
($node:expr, $variant:ident) => {
(if let $crate::ast::KindData::$variant(ref mut d) = $node.kind_data_mut() {
d
} else {
panic!(
"Expected kind data variant {} but found {:?}",
stringify!($variant),
$node.kind_data()
)
})
};
}
#[macro_export]
macro_rules! as_type_data {
($arena:expr, $node_ref:expr, $variant:ident) => {
(if let $crate::ast::TypeData::$variant(ref d) = $arena[$node_ref].type_data() {
d
} else {
panic!(
"Expected type data variant {} but found {:?}",
stringify!($variant),
$arena[$node_ref].type_data()
)
})
};
($node:expr, $variant:ident) => {
(if let $crate::ast::TypeData::$variant(ref d) = $node.type_data() {
d
} else {
panic!(
"Expected type data variant {} but found {:?}",
stringify!($variant),
$node.type_data()
)
})
};
}
#[macro_export]
macro_rules! as_type_data_mut {
($arena:expr, $node_ref:expr, $variant:ident) => {
(if let $crate::ast::TypeData::$variant(ref mut d) = $arena[$node_ref].type_data_mut() {
d
} else {
panic!(
"Expected type data variant {} but found {:?}",
stringify!($variant),
$arena[$node_ref].type_data()
)
})
};
($node:expr, $variant:ident) => {
(if let $crate::ast::TypeData::$variant(ref mut d) = $node.type_data_mut() {
d
} else {
panic!(
"Expected type data variant {} but found {:?}",
stringify!($variant),
$node.type_data()
)
})
};
}
#[macro_export]
macro_rules! md_ast {
($arena:expr, $root:expr => { $($children:tt)* }) => {{
let __root = $arena.new_node($root);
md_ast!(@children $arena, __root, { $($children)* });
__root
}};
(@mk $arena:expr, $spec:expr) => {{
$arena.new_node($spec)
}};
(@mk $arena:expr, $spec:expr, @{ $post:expr }) => {{
let __n = $arena.new_node($spec);
($post)(&mut $arena[__n]);
__n
}};
(@children $arena:expr, $parent:ident, { }) => {};
(@children $arena:expr, $parent:ident, {
$child:expr $( ;{ $post:expr } )? => { $($grand:tt)* } $(, $($rest:tt)*)?
}) => {{
let __child = md_ast!(@mk $arena, $child $(, @{ $post })?);
$parent.append_child($arena, __child);
md_ast!(@children $arena, __child, { $($grand)* });
md_ast!(@children $arena, $parent, { $($($rest)*)? });
}};
(@children $arena:expr, $parent:ident, {
$child:expr $( ;{ $post:expr } )? $(, $($rest:tt)*)?
}) => {{
let __child = md_ast!(@mk $arena, $child $(, ;{ $post })?);
$parent.append_child($arena, __child);
md_ast!(@children $arena, $parent, { $($($rest)*)? });
}};
}
#[macro_export]
macro_rules! node_path {
($arena:expr, $start:expr $(, $method:ident )* ) => {{
let mut node_opt = Some($start);
$(
node_opt = node_opt.and_then(|n| $arena[n].$method());
)*
node_opt
}};
}
#[cfg(not(feature = "std"))]
pub mod debug {
#[cfg(feature = "no-std-unix-debug")]
extern crate libc;
use core::fmt::{self, Write};
#[allow(dead_code)]
pub struct Stdout;
impl Write for Stdout {
#[allow(unreachable_code, unused)]
fn write_str(&mut self, s: &str) -> fmt::Result {
#[cfg(feature = "no-std-unix-debug")]
unsafe {
libc::write(1, s.as_ptr() as *const _, s.len());
}
Ok(())
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
use core::fmt::Write;
let mut out = $crate::debug::Stdout;
core::write!(&mut out, $($arg)*).ok();
}};
}
#[macro_export]
macro_rules! println {
($($arg:tt)*) => {{
$crate::print!("{}\n", format_args!($($arg)*));
}};
}
}