diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8cad372c..e2a80e17 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,5 +20,5 @@ jobs: - uses: dtolnay/rust-toolchain@v1 with: toolchain: stable - - run: cargo install cargo-tarpaulin && cargo tarpaulin --out xml + - run: cargo install cargo-tarpaulin && cargo tarpaulin --features json --out xml - uses: codecov/codecov-action@v4 diff --git a/Cargo.toml b/Cargo.toml index 8a53c928..45bfd139 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ serde = { version = "1", features = ["derive"], optional = true } env_logger = "0.11" criterion = "0.5" pretty_assertions = "1" +serde_json = { version = "1" } swc_core = { version = "0.100", features = [ "ecma_ast", "ecma_visit", diff --git a/readme.md b/readme.md index ad24a61f..2e0909d6 100644 --- a/readme.md +++ b/readme.md @@ -254,15 +254,15 @@ The following bash scripts are useful when working on this project: ``` * format: ```sh - cargo fmt && cargo fix --all-targets + cargo fmt && cargo fix --all-targets --all-features ``` * lint: ```sh - cargo fmt --check && cargo clippy --examples --tests --benches --all-features + cargo fmt --check && cargo clippy --examples --tests --benches --all-features --all-features ``` * test: ```sh - RUST_BACKTRACE=1 cargo test + RUST_BACKTRACE=1 cargo test --all-features ``` * docs: ```sh diff --git a/src/mdast.rs b/src/mdast.rs index 1a9e0ee3..241c9a50 100644 --- a/src/mdast.rs +++ b/src/mdast.rs @@ -86,7 +86,7 @@ pub enum AlignKind { #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "type") + serde(tag = "type", rename_all = "camelCase") )] pub enum Node { // Document: @@ -95,7 +95,7 @@ pub enum Node { // Container: /// Block quote. - BlockQuote(BlockQuote), + Blockquote(Blockquote), /// Footnote definition. FootnoteDefinition(FootnoteDefinition), /// MDX: JSX element (container). @@ -183,7 +183,7 @@ impl fmt::Debug for Node { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Node::Root(x) => x.fmt(f), - Node::BlockQuote(x) => x.fmt(f), + Node::Blockquote(x) => x.fmt(f), Node::FootnoteDefinition(x) => x.fmt(f), Node::MdxJsxFlowElement(x) => x.fmt(f), Node::List(x) => x.fmt(f), @@ -231,7 +231,7 @@ impl ToString for Node { match self { // Parents. Node::Root(x) => children_to_string(&x.children), - Node::BlockQuote(x) => children_to_string(&x.children), + Node::Blockquote(x) => children_to_string(&x.children), Node::FootnoteDefinition(x) => children_to_string(&x.children), Node::MdxJsxFlowElement(x) => children_to_string(&x.children), Node::List(x) => children_to_string(&x.children), @@ -280,7 +280,7 @@ impl Node { Node::Root(x) => Some(&x.children), Node::Paragraph(x) => Some(&x.children), Node::Heading(x) => Some(&x.children), - Node::BlockQuote(x) => Some(&x.children), + Node::Blockquote(x) => Some(&x.children), Node::List(x) => Some(&x.children), Node::ListItem(x) => Some(&x.children), Node::Emphasis(x) => Some(&x.children), @@ -305,7 +305,7 @@ impl Node { Node::Root(x) => Some(&mut x.children), Node::Paragraph(x) => Some(&mut x.children), Node::Heading(x) => Some(&mut x.children), - Node::BlockQuote(x) => Some(&mut x.children), + Node::Blockquote(x) => Some(&mut x.children), Node::List(x) => Some(&mut x.children), Node::ListItem(x) => Some(&mut x.children), Node::Emphasis(x) => Some(&mut x.children), @@ -328,7 +328,7 @@ impl Node { pub fn position(&self) -> Option<&Position> { match self { Node::Root(x) => x.position.as_ref(), - Node::BlockQuote(x) => x.position.as_ref(), + Node::Blockquote(x) => x.position.as_ref(), Node::FootnoteDefinition(x) => x.position.as_ref(), Node::MdxJsxFlowElement(x) => x.position.as_ref(), Node::List(x) => x.position.as_ref(), @@ -367,7 +367,7 @@ impl Node { pub fn position_mut(&mut self) -> Option<&mut Position> { match self { Node::Root(x) => x.position.as_mut(), - Node::BlockQuote(x) => x.position.as_mut(), + Node::Blockquote(x) => x.position.as_mut(), Node::FootnoteDefinition(x) => x.position.as_mut(), Node::MdxJsxFlowElement(x) => x.position.as_mut(), Node::List(x) => x.position.as_mut(), @@ -406,7 +406,7 @@ impl Node { pub fn position_set(&mut self, position: Option) { match self { Node::Root(x) => x.position = position, - Node::BlockQuote(x) => x.position = position, + Node::Blockquote(x) => x.position = position, Node::FootnoteDefinition(x) => x.position = position, Node::MdxJsxFlowElement(x) => x.position = position, Node::List(x) => x.position = position, @@ -448,7 +448,7 @@ impl Node { #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxJsxExpressionAttribute") + serde(untagged, rename = "mdxJsxExpressionAttribute") )] pub enum AttributeContent { /// JSX expression. @@ -483,7 +483,7 @@ pub struct AttributeValueExpression { #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "type") + serde(untagged) )] pub enum AttributeValue { /// Expression value. @@ -499,7 +499,6 @@ pub enum AttributeValue { /// > | /// ^^^ /// ``` - #[cfg_attr(feature = "serde", serde(rename = "literal"))] Literal(String), } @@ -510,11 +509,7 @@ pub enum AttributeValue { /// ^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "root") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Root { // Parent. /// Content model. @@ -530,11 +525,7 @@ pub struct Root { /// ^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "paragraph") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Paragraph { // Parent. /// Content model. @@ -550,11 +541,7 @@ pub struct Paragraph { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "heading") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Heading { // Parent. /// Content model. @@ -573,11 +560,7 @@ pub struct Heading { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "thematicBreak") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ThematicBreak { // Void. /// Positional info. @@ -591,12 +574,8 @@ pub struct ThematicBreak { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "blockquote") -)] -pub struct BlockQuote { +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Blockquote { // Parent. /// Content model. pub children: Vec, @@ -611,11 +590,7 @@ pub struct BlockQuote { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "list") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct List { // Parent. /// Content model. @@ -640,11 +615,7 @@ pub struct List { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "listItem") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ListItem { // Parent. /// Content model. @@ -667,11 +638,7 @@ pub struct ListItem { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "html") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Html { // Text. /// Content model. @@ -691,11 +658,7 @@ pub struct Html { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "code") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Code { // Text. /// Content model. @@ -720,11 +683,7 @@ pub struct Code { /// ^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "math") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Math { // Text. /// Content model. @@ -743,11 +702,7 @@ pub struct Math { /// ^^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "definition") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Definition { // Void. /// Positional info. @@ -780,11 +735,7 @@ pub struct Definition { /// ^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "text") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Text { // Text. /// Content model. @@ -800,11 +751,7 @@ pub struct Text { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "emphasis") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Emphasis { // Parent. /// Content model. @@ -820,11 +767,7 @@ pub struct Emphasis { /// ^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "strong") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Strong { // Parent. /// Content model. @@ -840,11 +783,7 @@ pub struct Strong { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "inlineCode") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InlineCode { // Text. /// Content model. @@ -860,11 +799,7 @@ pub struct InlineCode { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "inlineMath") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InlineMath { // Text. /// Content model. @@ -881,11 +816,7 @@ pub struct InlineMath { /// | b /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "break") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Break { // Void. /// Positional info. @@ -899,11 +830,7 @@ pub struct Break { /// ^^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "link") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Link { // Parent. /// Content model. @@ -925,11 +852,7 @@ pub struct Link { /// ^^^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "image") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Image { // Void. /// Positional info. @@ -953,11 +876,7 @@ pub struct Image { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "linkReference") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LinkReference { // Parent. /// Content model. @@ -990,11 +909,7 @@ pub struct LinkReference { /// ^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "imageReference") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ImageReference { // Void. /// Positional info. @@ -1029,11 +944,7 @@ pub struct ImageReference { /// ^^^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "footnoteDefinition") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FootnoteDefinition { // Parent. /// Content model. @@ -1062,11 +973,7 @@ pub struct FootnoteDefinition { /// ^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "footnoteReference") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FootnoteReference { // Void. /// Positional info. @@ -1095,11 +1002,7 @@ pub struct FootnoteReference { /// ^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "table") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Table { // Parent. /// Content model. @@ -1118,11 +1021,7 @@ pub struct Table { /// ^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "tableRow") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TableRow { // Parent. /// Content model. @@ -1138,11 +1037,7 @@ pub struct TableRow { /// ^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "tableCell") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TableCell { // Parent. /// Content model. @@ -1158,11 +1053,7 @@ pub struct TableCell { /// ^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "delete") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Delete { // Parent. /// Content model. @@ -1182,11 +1073,7 @@ pub struct Delete { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "yaml") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Yaml { // Void. /// Content model. @@ -1206,11 +1093,7 @@ pub struct Yaml { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "toml") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Toml { // Void. /// Content model. @@ -1226,11 +1109,7 @@ pub struct Toml { /// ^^^^^^^^^^^^^^^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxjsEsm") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MdxjsEsm { // Literal. /// Content model. @@ -1249,11 +1128,7 @@ pub struct MdxjsEsm { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxFlowExpression") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MdxFlowExpression { // Literal. /// Content model. @@ -1272,11 +1147,7 @@ pub struct MdxFlowExpression { /// ^^^ /// ``` #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxTextExpression") -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MdxTextExpression { // Literal. /// Content model. @@ -1298,7 +1169,7 @@ pub struct MdxTextExpression { #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxJsxFlowElement") + serde(rename_all = "camelCase") )] pub struct MdxJsxFlowElement { // Parent. @@ -1325,7 +1196,7 @@ pub struct MdxJsxFlowElement { #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), - serde(tag = "type", rename = "mdxJsxTextElement") + serde(rename_all = "camelCase") )] pub struct MdxJsxTextElement { // Parent. @@ -1841,14 +1712,14 @@ mod tests { #[test] fn block_quote() { - let mut node = Node::BlockQuote(BlockQuote { + let mut node = Node::Blockquote(Blockquote { position: None, children: vec![], }); assert_eq!( format!("{:?}", node), - "BlockQuote { children: [], position: None }", + "Blockquote { children: [], position: None }", "should support `Debug`" ); assert_eq!(node.to_string(), "", "should support `ToString`"); @@ -1863,7 +1734,7 @@ mod tests { node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1))); assert_eq!( format!("{:?}", node), - "BlockQuote { children: [], position: Some(1:1-1:2 (0-1)) }", + "Blockquote { children: [], position: Some(1:1-1:2 (0-1)) }", "should support `position_set`" ); } diff --git a/src/to_mdast.rs b/src/to_mdast.rs index 526e6c4a..15135a1b 100644 --- a/src/to_mdast.rs +++ b/src/to_mdast.rs @@ -2,7 +2,7 @@ use crate::event::{Event, Kind, Name}; use crate::mdast::{ - AttributeContent, AttributeValue, AttributeValueExpression, BlockQuote, Break, Code, + AttributeContent, AttributeValue, AttributeValueExpression, Blockquote, Break, Code, Definition, Delete, Emphasis, FootnoteDefinition, FootnoteReference, Heading, Html, Image, ImageReference, InlineCode, InlineMath, Link, LinkReference, List, ListItem, Math, MdxFlowExpression, MdxJsxAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, @@ -470,7 +470,7 @@ fn on_enter_autolink(context: &mut CompileContext) { /// Handle [`Enter`][Kind::Enter]:[`BlockQuote`][Name::BlockQuote]. fn on_enter_block_quote(context: &mut CompileContext) { - context.tail_push(Node::BlockQuote(BlockQuote { + context.tail_push(Node::Blockquote(Blockquote { children: vec![], position: None, })); diff --git a/tests/block_quote.rs b/tests/block_quote.rs index 800cbfb2..f14b3fc8 100644 --- a/tests/block_quote.rs +++ b/tests/block_quote.rs @@ -1,5 +1,5 @@ use markdown::{ - mdast::{BlockQuote, Node, Paragraph, Root, Text}, + mdast::{Blockquote, Node, Paragraph, Root, Text}, message, to_html, to_html_with_options, to_mdast, unist::Position, Constructs, Options, ParseOptions, @@ -221,7 +221,7 @@ fn block_quote() -> Result<(), message::Message> { assert_eq!( to_mdast("> a", &Default::default())?, Node::Root(Root { - children: vec![Node::BlockQuote(BlockQuote { + children: vec![Node::Blockquote(Blockquote { children: vec![Node::Paragraph(Paragraph { children: vec![Node::Text(Text { value: "a".into(), diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 00000000..c5026852 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,122 @@ +use markdown::mdast::Node; +use markdown::message::Message; + +#[allow(unused)] +#[derive(Debug)] +enum Error { + Mdast(Message), + Serde(serde_json::Error), +} + +#[cfg_attr(feature = "serde", test)] +fn serde() -> Result<(), Error> { + let source = markdown::to_mdast( + r#"--- +title: Serde +--- + +import Test from 'test'; +import Inner from 'test'; +import {Another, YetAnother} from 'another'; + +# , {username}! + +> Blockquote +Add test constructs below! + +## Test serialization and deserialization of mdast + + + + ## Inner + [Link](./link.md) + + + + + +{test} this is text expression + + + +# Text + +~~The world is flat.~~ We now know that the world is round. + +*Emphasis* + +*Strong Emphasis* + +$This is math$ + +Let's break\ +yes! + +*** + +## List + +* item1 +* item2 +* item3 + +## Code block + +```shell +cargo test --features json +``` + +## Inline + +`Inline code` with backticks + +## Image + +![Image](http://url/a.png) + +## Table + +| Syntax | Description | +|-----------|-------------| +| Header | Title | +| Paragraph | Text | + +## Task lists + +- [x] Write the press release +- [ ] Update the website +- [ ] Contact the media + +## Footnotes + +Here's a simple footnote,[^1] and here's a longer one.[^bignote] + +[^1]: This is the first footnote. + +[^bignote]: Here's one with multiple paragraphs and code. + + Indent paragraphs to include them in the footnote. + + `{ my code }` + + Add as many paragraphs as you like. + +"#, + &markdown::ParseOptions { + constructs: markdown::Constructs { + frontmatter: true, + ..markdown::Constructs::mdx() + }, + ..markdown::ParseOptions::gfm() + }, + ) + .map_err(Error::Mdast)?; + + let value: String = serde_json::to_string(&source).map_err(Error::Serde)?; + + let target: Node = serde_json::from_slice(value.as_bytes()).map_err(Error::Serde)?; + + pretty_assertions::assert_eq!(source, target); + + Ok(()) +}