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 = "html-to-markdown")]
94pub use html_to_markdown::{ConversionOptions, convert_html_to_markdown};
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_to_html() {
102        let markdown = "# Hello, world!";
103        let html = to_html(markdown);
104        assert_eq!(html, "<h1>Hello, world!</h1>");
105    }
106
107    #[cfg(feature = "html-to-markdown")]
108    #[test]
109    fn test_html_to_markdown_simple_paragraph() {
110        let html = "<p>Hello world</p>";
111        match convert_html_to_markdown(html, ConversionOptions::default()) {
112            Ok(markdown) => assert_eq!(markdown.trim(), "Hello world"),
113            Err(e) => panic!("HTML to Markdown conversion failed: {:?}", e),
114        }
115    }
116
117    #[cfg(feature = "html-to-markdown")]
118    #[test]
119    fn test_html_to_markdown_empty_input() {
120        let html = "";
121        match convert_html_to_markdown(html, ConversionOptions::default()) {
122            Ok(markdown) => assert_eq!(markdown, ""),
123            Err(e) => panic!("HTML to Markdown conversion failed for empty input: {:?}", e),
124        }
125    }
126
127    #[cfg(feature = "html-to-markdown")]
128    #[test]
129    fn test_html_to_markdown_div_element() {
130        // Test that div elements are properly handled
131        let html = "<div>Content in div</div>";
132        let result = convert_html_to_markdown(html, ConversionOptions::default());
133        assert!(result.is_ok());
134        let markdown = result.unwrap();
135        assert_eq!(markdown.trim(), "Content in div");
136    }
137}