cmark_writer/
options.rs

1//! CommonMark formatting options.
2//!
3//! This module provides configuration options for the CommonMark writer.
4
5use crate::writer::html::HtmlWriterOptions;
6#[cfg(feature = "gfm")]
7use ecow::EcoString;
8
9/// CommonMark formatting options
10#[derive(Debug, Clone)]
11pub struct WriterOptions {
12    /// Whether to enable strict mode (strictly following CommonMark specification)
13    pub strict: bool,
14    /// Hard break mode (true uses two spaces followed by a newline, false uses backslash followed by a newline)
15    pub hard_break_spaces: bool,
16    /// Number of spaces to use for indentation levels
17    pub indent_spaces: usize,
18    /// Character to use for unordered list markers (-, +, or *)
19    pub list_marker: char,
20    /// Character to use for thematic breaks (-, *, or _)
21    pub thematic_break_char: char,
22    /// Character to use for emphasis (_, or *)
23    pub emphasis_char: char,
24    /// Character to use for strong emphasis (_, or *)
25    pub strong_char: char,
26    /// Whether to escape special characters in text content
27    pub escape_special_chars: bool,
28    /// Whether to trim trailing hard breaks from paragraphs
29    pub trim_paragraph_trailing_hard_breaks: bool,
30
31    /// Whether to enable GitHub Flavored Markdown (GFM) extensions
32    #[cfg(feature = "gfm")]
33    pub enable_gfm: bool,
34
35    /// Whether to enable GFM strikethrough syntax
36    #[cfg(feature = "gfm")]
37    pub gfm_strikethrough: bool,
38
39    /// Whether to enable GFM task lists
40    #[cfg(feature = "gfm")]
41    pub gfm_tasklists: bool,
42
43    /// Whether to enable GFM tables with alignment
44    #[cfg(feature = "gfm")]
45    pub gfm_tables: bool,
46
47    /// Whether to enable GFM autolinks without angle brackets
48    #[cfg(feature = "gfm")]
49    pub gfm_autolinks: bool,
50
51    /// List of disallowed HTML tag names in GFM mode
52    #[cfg(feature = "gfm")]
53    pub gfm_disallowed_html_tags: Vec<EcoString>,
54
55    /// HTML writer options for rendering HtmlElement nodes
56    /// If None, options will be automatically derived from CommonMark options
57    pub html_writer_options: Option<HtmlWriterOptions>,
58}
59
60impl Default for WriterOptions {
61    fn default() -> Self {
62        Self {
63            strict: true,
64            hard_break_spaces: false,
65            indent_spaces: 4,
66            list_marker: '-',
67            thematic_break_char: '-',
68            emphasis_char: '_',
69            strong_char: '*',
70            escape_special_chars: false,
71            trim_paragraph_trailing_hard_breaks: true,
72
73            #[cfg(feature = "gfm")]
74            enable_gfm: false,
75
76            #[cfg(feature = "gfm")]
77            gfm_strikethrough: false,
78
79            #[cfg(feature = "gfm")]
80            gfm_tasklists: false,
81
82            #[cfg(feature = "gfm")]
83            gfm_tables: false,
84
85            #[cfg(feature = "gfm")]
86            gfm_autolinks: false,
87
88            #[cfg(feature = "gfm")]
89            gfm_disallowed_html_tags: vec![
90                "title".into(),
91                "textarea".into(),
92                "style".into(),
93                "xmp".into(),
94                "iframe".into(),
95                "noembed".into(),
96                "noframes".into(),
97                "script".into(),
98                "plaintext".into(),
99            ],
100
101            html_writer_options: None,
102        }
103    }
104}
105
106impl WriterOptions {
107    /// Set custom HTML writer options for rendering HtmlElement nodes
108    pub fn html_writer_options(mut self, options: Option<HtmlWriterOptions>) -> Self {
109        self.html_writer_options = options;
110        self
111    }
112}
113
114/// Builder for WriterOptions
115pub struct WriterOptionsBuilder {
116    options: WriterOptions,
117}
118
119impl WriterOptionsBuilder {
120    /// Create a new WriterOptionsBuilder with default options
121    pub fn new() -> Self {
122        Self {
123            options: WriterOptions::default(),
124        }
125    }
126
127    /// Set strict mode (whether to strictly follow CommonMark specification)
128    pub fn strict(mut self, strict: bool) -> Self {
129        self.options.strict = strict;
130        self
131    }
132
133    /// Set hard break mode (true uses two spaces followed by a newline, false uses backslash)
134    pub fn hard_break_spaces(mut self, hard_break_spaces: bool) -> Self {
135        self.options.hard_break_spaces = hard_break_spaces;
136        self
137    }
138
139    /// Set number of spaces for indentation
140    pub fn indent_spaces(mut self, indent_spaces: usize) -> Self {
141        self.options.indent_spaces = indent_spaces;
142        self
143    }
144
145    /// Set the marker character for unordered lists (-, +, or *)
146    pub fn list_marker(mut self, marker: char) -> Self {
147        if marker == '-' || marker == '+' || marker == '*' {
148            self.options.list_marker = marker;
149        }
150        self
151    }
152
153    /// Set whether to escape special characters in text content
154    pub fn escape_special_chars(mut self, escape: bool) -> Self {
155        self.options.escape_special_chars = escape;
156        self
157    }
158
159    /// Set whether to trim trailing hard breaks from paragraphs
160    pub fn trim_paragraph_trailing_hard_breaks(mut self, trim: bool) -> Self {
161        self.options.trim_paragraph_trailing_hard_breaks = trim;
162        self
163    }
164
165    /// Set the character for thematic breaks (-, *, or _)
166    pub fn thematic_break_char(mut self, char: char) -> Self {
167        if char == '-' || char == '*' || char == '_' {
168            self.options.thematic_break_char = char;
169        }
170        self
171    }
172
173    /// Set the character for emphasis (_, or *)
174    pub fn emphasis_char(mut self, char: char) -> Self {
175        if char == '_' || char == '*' {
176            self.options.emphasis_char = char;
177        }
178        self
179    }
180
181    /// Set the character for strong emphasis (_, or *)
182    pub fn strong_char(mut self, char: char) -> Self {
183        if char == '_' || char == '*' {
184            self.options.strong_char = char;
185        }
186        self
187    }
188
189    /// Enable all GitHub Flavored Markdown (GFM) extensions
190    #[cfg(feature = "gfm")]
191    pub fn enable_gfm(mut self) -> Self {
192        self.options.enable_gfm = true;
193        self.options.gfm_strikethrough = true;
194        self.options.gfm_tasklists = true;
195        self.options.gfm_tables = true;
196        self.options.gfm_autolinks = true;
197        self
198    }
199
200    /// Enable or disable GFM strikethrough syntax
201    #[cfg(feature = "gfm")]
202    pub fn gfm_strikethrough(mut self, enable: bool) -> Self {
203        self.options.gfm_strikethrough = enable;
204        if enable {
205            self.options.enable_gfm = true;
206        }
207        self
208    }
209
210    /// Enable or disable GFM task lists
211    #[cfg(feature = "gfm")]
212    pub fn gfm_tasklists(mut self, enable: bool) -> Self {
213        self.options.gfm_tasklists = enable;
214        if enable {
215            self.options.enable_gfm = true;
216        }
217        self
218    }
219
220    /// Enable or disable GFM tables with alignment
221    #[cfg(feature = "gfm")]
222    pub fn gfm_tables(mut self, enable: bool) -> Self {
223        self.options.gfm_tables = enable;
224        if enable {
225            self.options.enable_gfm = true;
226        }
227        self
228    }
229
230    /// Enable or disable GFM autolinks without angle brackets
231    #[cfg(feature = "gfm")]
232    pub fn gfm_autolinks(mut self, enable: bool) -> Self {
233        self.options.gfm_autolinks = enable;
234        if enable {
235            self.options.enable_gfm = true;
236        }
237        self
238    }
239
240    /// Set list of disallowed HTML tags in GFM mode
241    #[cfg(feature = "gfm")]
242    pub fn gfm_disallowed_html_tags(mut self, tags: Vec<EcoString>) -> Self {
243        self.options.gfm_disallowed_html_tags = tags;
244        self
245    }
246
247    /// Set custom HTML writer options for rendering HtmlElement nodes
248    pub fn html_writer_options(mut self, options: Option<HtmlWriterOptions>) -> Self {
249        self.options.html_writer_options = options;
250        self
251    }
252
253    /// Build the WriterOptions
254    pub fn build(self) -> WriterOptions {
255        self.options
256    }
257}
258
259impl Default for WriterOptionsBuilder {
260    fn default() -> Self {
261        Self::new()
262    }
263}