Skip to main content

markdown_ppp/parser/
config.rs

1use nom::IResult;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6/// Function type for mapping elements.
7type ElementMapFn<ELT> = Rc<RefCell<Box<dyn FnMut(ELT) -> ELT>>>;
8
9/// Function type for mapping elements.
10type ElementFlatMapFn<ELT> = Rc<RefCell<Box<dyn FnMut(ELT) -> Vec<ELT>>>>;
11
12/// Function type for custom block parsers.
13type CustomBlockParserFn =
14    Rc<RefCell<Box<dyn for<'a> FnMut(&'a str) -> IResult<&'a str, Vec<crate::ast::Block>>>>>;
15
16/// Function type for custom inline parsers.
17type CustomInlineParserFn =
18    Rc<RefCell<Box<dyn for<'a> FnMut(&'a str) -> IResult<&'a str, Vec<crate::ast::Inline>>>>>;
19
20/// Default value of [`MarkdownParserConfig::with_max_nesting_depth`].
21pub const DEFAULT_MAX_NESTING_DEPTH: usize = 32;
22
23/// Behavior of the parser when encountering certain elements.
24#[derive(Clone)]
25pub enum ElementBehavior<ELT> {
26    /// The parser will parse the element normally.
27    Parse,
28
29    /// The parser will ignore the element and not parse it. In this case, alternative
30    /// parsers will be tried.
31    Ignore,
32
33    /// Parse element but do not include it in the output.
34    Skip,
35
36    /// Parse the element and apply a custom function to it.
37    Map(ElementMapFn<ELT>),
38
39    /// Parse the element and apply a custom function to it which returns an array of elements.
40    FlatMap(ElementFlatMapFn<ELT>),
41}
42
43/// A configuration for the Markdown parser.
44#[derive(Clone)]
45pub struct MarkdownParserConfig {
46    /// If true, the parser will allow headings without a space after the hash marks.
47    pub(crate) allow_no_space_in_headings: bool,
48
49    /// A map of HTML entities to their corresponding `Entity` structs.
50    pub(crate) html_entities_map: HashMap<String, &'static entities::Entity>,
51
52    /// Maximum nesting depth of container blocks and inline elements.
53    /// See [`MarkdownParserConfig::with_max_nesting_depth`].
54    pub(crate) max_nesting_depth: usize,
55
56    /// The behavior of the parser when encountering blockquotes.
57    pub(crate) block_blockquote_behavior: ElementBehavior<crate::ast::Block>,
58
59    /// The behavior of the parser when encountering GitHub alerts.
60    pub(crate) block_github_alert_behavior: ElementBehavior<crate::ast::Block>,
61
62    /// The behavior of the parser when encountering headings in style 1 (e.g., `# Heading`).
63    pub(crate) block_heading_v1_behavior: ElementBehavior<crate::ast::Block>,
64
65    /// The behavior of the parser when encountering headings in style 2 (e.g., `Heading\n===`).
66    pub(crate) block_heading_v2_behavior: ElementBehavior<crate::ast::Block>,
67
68    /// The behavior of the parser when encountering thematic breaks (e.g., `---`).
69    pub(crate) block_thematic_break_behavior: ElementBehavior<crate::ast::Block>,
70
71    /// The behavior of the parser when encountering lists.
72    pub(crate) block_list_behavior: ElementBehavior<crate::ast::Block>,
73
74    /// The behavior of the parser when encountering code blocks.
75    pub(crate) block_code_block_behavior: ElementBehavior<crate::ast::Block>,
76
77    /// The behavior of the parser when encountering HTML blocks.
78    pub(crate) block_html_block_behavior: ElementBehavior<crate::ast::Block>,
79
80    /// The behavior of the parser when encountering footnote definitions.
81    pub(crate) block_footnote_definition_behavior: ElementBehavior<crate::ast::Block>,
82
83    /// The behavior of the parser when encountering link definitions.
84    pub(crate) block_link_definition_behavior: ElementBehavior<crate::ast::Block>,
85
86    /// The behavior of the parser when encountering tables.
87    pub(crate) block_table_behavior: ElementBehavior<crate::ast::Block>,
88
89    /// The behavior of the parser when encountering block paragraphs.
90    pub(crate) block_paragraph_behavior: ElementBehavior<crate::ast::Block>,
91
92    /// The behavior of the parser when encountering inline autolinks.
93    pub(crate) inline_autolink_behavior: ElementBehavior<crate::ast::Inline>,
94
95    /// The behavior of the parser when encountering inline links.
96    pub(crate) inline_link_behavior: ElementBehavior<crate::ast::Inline>,
97
98    /// The behavior of the parser when encountering inline footnote references.
99    pub(crate) inline_footnote_reference_behavior: ElementBehavior<crate::ast::Inline>,
100
101    /// The behavior of the parser when encountering inline reference links.
102    pub(crate) inline_reference_link_behavior: ElementBehavior<crate::ast::Inline>,
103
104    /// The behavior of the parser when encountering inline hard newlines.
105    pub(crate) inline_hard_newline_behavior: ElementBehavior<crate::ast::Inline>,
106
107    /// The behavior of the parser when encountering inline images.
108    pub(crate) inline_image_behavior: ElementBehavior<crate::ast::Inline>,
109
110    /// The behavior of the parser when encountering inline code spans.
111    pub(crate) inline_code_span_behavior: ElementBehavior<crate::ast::Inline>,
112
113    /// The behavior of the parser when encountering inline emphasis.
114    pub(crate) inline_emphasis_behavior: ElementBehavior<crate::ast::Inline>,
115
116    /// The behavior of the parser when encountering inline strikethrough.
117    pub(crate) inline_strikethrough_behavior: ElementBehavior<crate::ast::Inline>,
118
119    /// The behavior of the parser when encountering inline text.
120    pub(crate) inline_text_behavior: ElementBehavior<crate::ast::Inline>,
121
122    /// A custom parser for blocks. This is a function that takes a string and returns a `Block`.
123    pub(crate) custom_block_parser: Option<CustomBlockParserFn>,
124
125    /// A custom parser for inlines. This is a function that takes a string and returns a `Inline`.
126    pub(crate) custom_inline_parser: Option<CustomInlineParserFn>,
127}
128
129impl Default for MarkdownParserConfig {
130    fn default() -> Self {
131        Self {
132            allow_no_space_in_headings: false,
133            html_entities_map: Self::make_html_entities_map(),
134            max_nesting_depth: DEFAULT_MAX_NESTING_DEPTH,
135            block_blockquote_behavior: ElementBehavior::Parse,
136            block_github_alert_behavior: ElementBehavior::Parse,
137            block_heading_v1_behavior: ElementBehavior::Parse,
138            block_heading_v2_behavior: ElementBehavior::Parse,
139            block_thematic_break_behavior: ElementBehavior::Parse,
140            block_list_behavior: ElementBehavior::Parse,
141            block_code_block_behavior: ElementBehavior::Parse,
142            block_html_block_behavior: ElementBehavior::Parse,
143            block_footnote_definition_behavior: ElementBehavior::Parse,
144            block_link_definition_behavior: ElementBehavior::Parse,
145            block_table_behavior: ElementBehavior::Parse,
146            block_paragraph_behavior: ElementBehavior::Parse,
147            inline_autolink_behavior: ElementBehavior::Parse,
148            inline_link_behavior: ElementBehavior::Parse,
149            inline_footnote_reference_behavior: ElementBehavior::Parse,
150            inline_reference_link_behavior: ElementBehavior::Parse,
151            inline_hard_newline_behavior: ElementBehavior::Parse,
152            inline_image_behavior: ElementBehavior::Parse,
153            inline_code_span_behavior: ElementBehavior::Parse,
154            inline_emphasis_behavior: ElementBehavior::Parse,
155            inline_strikethrough_behavior: ElementBehavior::Parse,
156            inline_text_behavior: ElementBehavior::Parse,
157            custom_block_parser: None,
158            custom_inline_parser: None,
159        }
160    }
161}
162
163impl MarkdownParserConfig {
164    fn make_html_entities_map() -> HashMap<String, &'static entities::Entity> {
165        let mut map = HashMap::new();
166        for entity in entities::ENTITIES.iter() {
167            map.insert(entity.entity.to_string(), entity);
168        }
169        map
170    }
171
172    /// Enable the parser to allow headings without a space after the hash marks.
173    pub fn with_allow_no_space_in_headings(self) -> Self {
174        Self {
175            allow_no_space_in_headings: true,
176            ..self
177        }
178    }
179
180    /// Set the maximum nesting depth of the document.
181    ///
182    /// Every container block (blockquote, list item, footnote definition, GitHub alert)
183    /// and every inline element with nested content (emphasis, strikethrough, link label)
184    /// adds one level of nesting. When the depth exceeds `depth`,
185    /// [`parse_markdown`](crate::parser::parse_markdown) returns an error with
186    /// [`nom::error::ErrorKind::TooLarge`].
187    ///
188    /// The limit bounds both the parse time and the stack usage on adversarial input
189    /// (e.g. thousands of `>` markers). Defaults to [`DEFAULT_MAX_NESTING_DEPTH`].
190    pub fn with_max_nesting_depth(self, depth: usize) -> Self {
191        Self {
192            max_nesting_depth: depth,
193            ..self
194        }
195    }
196
197    /// Set a custom map of HTML entities.
198    pub fn with_html_entities_map(
199        self,
200        html_entities_map: HashMap<String, &'static entities::Entity>,
201    ) -> Self {
202        Self {
203            html_entities_map,
204            ..self
205        }
206    }
207
208    /// Set the behavior of the parser when encountering blockquotes.
209    pub fn with_block_blockquote_behavior(
210        self,
211        behavior: ElementBehavior<crate::ast::Block>,
212    ) -> Self {
213        Self {
214            block_blockquote_behavior: behavior,
215            ..self
216        }
217    }
218
219    /// Set the behavior of the parser when encountering GitHub alerts.
220    pub fn with_block_github_alert_behavior(
221        self,
222        behavior: ElementBehavior<crate::ast::Block>,
223    ) -> Self {
224        Self {
225            block_github_alert_behavior: behavior,
226            ..self
227        }
228    }
229
230    /// Set the behavior of the parser when encountering headings in style 1 (e.g., `# Heading`).
231    pub fn with_block_heading_v1_behavior(
232        self,
233        behavior: ElementBehavior<crate::ast::Block>,
234    ) -> Self {
235        Self {
236            block_heading_v1_behavior: behavior,
237            ..self
238        }
239    }
240
241    /// Set the behavior of the parser when encountering headings in style 2 (e.g., `Heading\n===`).
242    pub fn with_block_heading_v2_behavior(
243        self,
244        behavior: ElementBehavior<crate::ast::Block>,
245    ) -> Self {
246        Self {
247            block_heading_v2_behavior: behavior,
248            ..self
249        }
250    }
251
252    /// Set the behavior of the parser when encountering thematic breaks (e.g., `---`).
253    pub fn with_block_thematic_break_behavior(
254        self,
255        behavior: ElementBehavior<crate::ast::Block>,
256    ) -> Self {
257        Self {
258            block_thematic_break_behavior: behavior,
259            ..self
260        }
261    }
262
263    /// Set the behavior of the parser when encountering lists.
264    pub fn with_block_list_behavior(self, behavior: ElementBehavior<crate::ast::Block>) -> Self {
265        Self {
266            block_list_behavior: behavior,
267            ..self
268        }
269    }
270
271    /// Set the behavior of the parser when encountering code blocks.
272    pub fn with_block_code_block_behavior(
273        self,
274        behavior: ElementBehavior<crate::ast::Block>,
275    ) -> Self {
276        Self {
277            block_code_block_behavior: behavior,
278            ..self
279        }
280    }
281
282    /// Set the behavior of the parser when encountering HTML blocks.
283    pub fn with_block_html_block_behavior(
284        self,
285        behavior: ElementBehavior<crate::ast::Block>,
286    ) -> Self {
287        Self {
288            block_html_block_behavior: behavior,
289            ..self
290        }
291    }
292
293    /// Set the behavior of the parser when encountering footnote definitions.
294    pub fn with_block_footnote_definition_behavior(
295        self,
296        behavior: ElementBehavior<crate::ast::Block>,
297    ) -> Self {
298        Self {
299            block_footnote_definition_behavior: behavior,
300            ..self
301        }
302    }
303
304    /// Set the behavior of the parser when encountering link definitions.
305    pub fn with_block_link_definition_behavior(
306        self,
307        behavior: ElementBehavior<crate::ast::Block>,
308    ) -> Self {
309        Self {
310            block_link_definition_behavior: behavior,
311            ..self
312        }
313    }
314
315    /// Set the behavior of the parser when encountering tables.
316    pub fn with_block_table_behavior(self, behavior: ElementBehavior<crate::ast::Block>) -> Self {
317        Self {
318            block_table_behavior: behavior,
319            ..self
320        }
321    }
322
323    /// Set the behavior of the parser when encountering block paragraphs.
324    pub fn with_block_paragraph_behavior(
325        self,
326        behavior: ElementBehavior<crate::ast::Block>,
327    ) -> Self {
328        Self {
329            block_paragraph_behavior: behavior,
330            ..self
331        }
332    }
333
334    /// Set the behavior of the parser when encountering inline autolinks.
335    pub fn with_inline_autolink_behavior(
336        self,
337        behavior: ElementBehavior<crate::ast::Inline>,
338    ) -> Self {
339        Self {
340            inline_autolink_behavior: behavior,
341            ..self
342        }
343    }
344
345    /// Set the behavior of the parser when encountering inline links.
346    pub fn with_inline_link_behavior(self, behavior: ElementBehavior<crate::ast::Inline>) -> Self {
347        Self {
348            inline_link_behavior: behavior,
349            ..self
350        }
351    }
352
353    /// Set the behavior of the parser when encountering inline footnote references.
354    pub fn with_inline_footnote_reference_behavior(
355        self,
356        behavior: ElementBehavior<crate::ast::Inline>,
357    ) -> Self {
358        Self {
359            inline_footnote_reference_behavior: behavior,
360            ..self
361        }
362    }
363
364    /// Set the behavior of the parser when encountering inline reference links.
365    pub fn with_inline_reference_link_behavior(
366        self,
367        behavior: ElementBehavior<crate::ast::Inline>,
368    ) -> Self {
369        Self {
370            inline_reference_link_behavior: behavior,
371            ..self
372        }
373    }
374
375    /// Set the behavior of the parser when encountering inline hard newlines.
376    pub fn with_inline_hard_newline_behavior(
377        self,
378        behavior: ElementBehavior<crate::ast::Inline>,
379    ) -> Self {
380        Self {
381            inline_hard_newline_behavior: behavior,
382            ..self
383        }
384    }
385
386    /// Set the behavior of the parser when encountering inline images.
387    pub fn with_inline_image_behavior(self, behavior: ElementBehavior<crate::ast::Inline>) -> Self {
388        Self {
389            inline_image_behavior: behavior,
390            ..self
391        }
392    }
393
394    /// Set the behavior of the parser when encountering inline code spans.
395    pub fn with_inline_code_span_behavior(
396        self,
397        behavior: ElementBehavior<crate::ast::Inline>,
398    ) -> Self {
399        Self {
400            inline_code_span_behavior: behavior,
401            ..self
402        }
403    }
404
405    /// Set the behavior of the parser when encountering inline emphasis.
406    pub fn with_inline_emphasis_behavior(
407        self,
408        behavior: ElementBehavior<crate::ast::Inline>,
409    ) -> Self {
410        Self {
411            inline_emphasis_behavior: behavior,
412            ..self
413        }
414    }
415
416    /// Set the behavior of the parser when encountering inline strikethrough.
417    pub fn with_inline_strikethrough_behavior(
418        self,
419        behavior: ElementBehavior<crate::ast::Inline>,
420    ) -> Self {
421        Self {
422            inline_strikethrough_behavior: behavior,
423            ..self
424        }
425    }
426
427    /// Set the behavior of the parser when encountering inline text.
428    pub fn with_inline_text_behavior(self, behavior: ElementBehavior<crate::ast::Inline>) -> Self {
429        Self {
430            inline_text_behavior: behavior,
431            ..self
432        }
433    }
434
435    /// Set a custom parser for blocks.
436    pub fn with_custom_block_parser(self, parser: CustomBlockParserFn) -> Self {
437        Self {
438            custom_block_parser: Some(parser),
439            ..self
440        }
441    }
442
443    /// Set a custom parser for inlines.
444    pub fn with_custom_inline_parser(self, parser: CustomInlineParserFn) -> Self {
445        Self {
446            custom_inline_parser: Some(parser),
447            ..self
448        }
449    }
450}