cmark_writer/
options.rs

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