Skip to main content

mq_markdown/
lib.rs

1//! # mq-markdown: Markdown parsing and manipulation for mq
2//!
3//! This crate provides comprehensive markdown parsing, manipulation, and conversion
4//! functionality used in [mq](https://github.com/harehare/mq). It offers a robust
5//! API to work with markdown content and generate different output formats.
6//!
7//! ## Features
8//!
9//! - **Parse Markdown**: Convert markdown strings to structured AST
10//! - **HTML Conversion**: Convert between markdown and HTML formats
11//! - **MDX Support**: Parse and manipulate MDX (Markdown + JSX) content
12//! - **JSON Export**: Serialize markdown AST to JSON (with `json` feature)
13//! - **Configurable Rendering**: Customize output formatting and styles
14//!
15//! ## Quick Start
16//!
17//! ### Basic HTML Conversion
18//!
19//! ```rust
20//! use mq_markdown::to_html;
21//!
22//! let markdown = "# Hello, world!";
23//! let html = to_html(markdown);
24//! assert_eq!(html, "<h1>Hello, world!</h1>");
25//! ```
26//!
27//! ### Working with Markdown AST
28//!
29//! ```rust
30//! use mq_markdown::Markdown;
31//!
32//! let markdown = "# Heading\n\nParagraph with *emphasis*";
33//! let doc = markdown.parse::<Markdown>().unwrap();
34//!
35//! println!("Found {} nodes", doc.nodes.len());
36//! println!("HTML: {}", doc.to_html());
37//! println!("Text: {}", doc.to_text());
38//! ```
39//!
40//! ### Custom Rendering Options
41//!
42//! ```rust
43//! use mq_markdown::{Markdown, RenderOptions, ListStyle};
44//!
45//! let mut doc = "- Item 1\n- Item 2".parse::<Markdown>().unwrap();
46//! doc.set_options(RenderOptions {
47//!     list_style: ListStyle::Plus,
48//!     ..Default::default()
49//! });
50//!
51//! // Now renders with "+" instead of "-"
52//! println!("{}", doc);
53//! ```
54//!
55//! ## Performance Considerations
56//!
57//! - Use `&str` methods when possible to avoid unnecessary allocations
58//! - The AST uses structural equality checking for efficient comparisons
59//! - Consider using `CompactString` for memory-efficient string storage
60//! - Position information can be omitted to reduce memory usage
61//!
62//! ## HTML to Markdown (optional feature)
63//!
64//! When the `html-to-markdown` feature is enabled, this crate can also convert HTML to Markdown.
65//!
66//! ```rust,ignore
67//! // This example requires the `html-to-markdown` feature.
68//! // Add `mq-markdown = { version = "...", features = ["html-to-markdown"] }` to your Cargo.toml.
69//! # #[cfg(feature = "html-to-markdown")]
70//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
71//! use mq_markdown::convert_html_to_markdown;
72//!
73//! let html = "<p>Hello <strong>world</strong>!</p>";
74//! let markdown = convert_html_to_markdown(html)?;
75//! assert_eq!(markdown, "Hello **world**!");
76//! # Ok(())
77//! # }
78//! # #[cfg(not(feature = "html-to-markdown"))]
79//! # fn main() {}
80//! ```
81mod html_to_markdown;
82mod markdown;
83mod node;
84pub use markdown::{Markdown, to_html};
85pub use node::{
86    Blockquote, Break, Code, CodeInline, ColorTheme, Definition, Delete, Emphasis, Footnote, FootnoteRef, Fragment,
87    Heading, HorizontalRule, Html, Image, ImageRef, Link, LinkRef, List, ListStyle, Math, MathInline,
88    MdxFlowExpression, MdxJsEsm, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, Node, Point, Position,
89    RenderOptions, Strong, TableAlign, TableAlignKind, TableCell, TableRow, Text, Title, TitleSurroundStyle, Toml, Url,
90    UrlSurroundStyle, Yaml, attr_value::AttrValue,
91};
92
93#[cfg(feature = "wikilink")]
94pub use node::WikiLink;
95
96#[cfg(feature = "callout")]
97pub use node::Callout;
98
99#[cfg(feature = "embed")]
100pub use node::Embed;
101
102#[cfg(feature = "html-to-markdown")]
103pub use html_to_markdown::{ConversionOptions, convert_html_to_markdown};
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_to_html() {
111        let markdown = "# Hello, world!";
112        let html = to_html(markdown);
113        assert_eq!(html, "<h1>Hello, world!</h1>");
114    }
115
116    #[cfg(feature = "html-to-markdown")]
117    #[test]
118    fn test_html_to_markdown_simple_paragraph() {
119        let html = "<p>Hello world</p>";
120        match convert_html_to_markdown(html, ConversionOptions::default()) {
121            Ok(markdown) => assert_eq!(markdown.trim(), "Hello world"),
122            Err(e) => panic!("HTML to Markdown conversion failed: {:?}", e),
123        }
124    }
125
126    #[cfg(feature = "html-to-markdown")]
127    #[test]
128    fn test_html_to_markdown_empty_input() {
129        let html = "";
130        match convert_html_to_markdown(html, ConversionOptions::default()) {
131            Ok(markdown) => assert_eq!(markdown, ""),
132            Err(e) => panic!("HTML to Markdown conversion failed for empty input: {:?}", e),
133        }
134    }
135
136    #[cfg(feature = "html-to-markdown")]
137    #[test]
138    fn test_html_to_markdown_div_element() {
139        // Test that div elements are properly handled
140        let html = "<div>Content in div</div>";
141        let result = convert_html_to_markdown(html, ConversionOptions::default());
142        assert!(result.is_ok());
143        let markdown = result.unwrap();
144        assert_eq!(markdown.trim(), "Content in div");
145    }
146}