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}