1use std::collections::HashMap;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
8pub enum Flavor {
9 #[default]
11 Pandoc,
12 Quarto,
14 #[cfg_attr(feature = "serde", serde(rename = "rmarkdown"))]
16 RMarkdown,
17 Gfm,
19 CommonMark,
21 #[cfg_attr(feature = "serde", serde(rename = "multimarkdown"))]
23 MultiMarkdown,
24}
25
26#[derive(Debug, Clone, PartialEq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[cfg_attr(feature = "serde", serde(default))]
32#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
33pub struct Extensions {
34 #[cfg_attr(feature = "serde", serde(alias = "blank_before_header"))]
39 pub blank_before_header: bool,
40 #[cfg_attr(feature = "serde", serde(alias = "header_attributes"))]
42 pub header_attributes: bool,
43 pub auto_identifiers: bool,
45 pub gfm_auto_identifiers: bool,
47 pub implicit_header_references: bool,
49
50 #[cfg_attr(feature = "serde", serde(alias = "blank_before_blockquote"))]
53 pub blank_before_blockquote: bool,
54
55 #[cfg_attr(feature = "serde", serde(alias = "fancy_lists"))]
58 pub fancy_lists: bool,
59 pub startnum: bool,
61 #[cfg_attr(feature = "serde", serde(alias = "example_lists"))]
63 pub example_lists: bool,
64 #[cfg_attr(feature = "serde", serde(alias = "task_lists"))]
66 pub task_lists: bool,
67 #[cfg_attr(feature = "serde", serde(alias = "definition_lists"))]
69 pub definition_lists: bool,
70
71 #[cfg_attr(feature = "serde", serde(alias = "backtick_code_blocks"))]
74 pub backtick_code_blocks: bool,
75 #[cfg_attr(feature = "serde", serde(alias = "fenced_code_blocks"))]
77 pub fenced_code_blocks: bool,
78 #[cfg_attr(feature = "serde", serde(alias = "fenced_code_attributes"))]
80 pub fenced_code_attributes: bool,
81 pub executable_code: bool,
83 pub rmarkdown_inline_code: bool,
85 pub quarto_inline_code: bool,
87 #[cfg_attr(feature = "serde", serde(alias = "inline_code_attributes"))]
89 pub inline_code_attributes: bool,
90
91 #[cfg_attr(feature = "serde", serde(alias = "simple_tables"))]
94 pub simple_tables: bool,
95 #[cfg_attr(feature = "serde", serde(alias = "multiline_tables"))]
97 pub multiline_tables: bool,
98 #[cfg_attr(feature = "serde", serde(alias = "grid_tables"))]
100 pub grid_tables: bool,
101 #[cfg_attr(feature = "serde", serde(alias = "pipe_tables"))]
103 pub pipe_tables: bool,
104 #[cfg_attr(feature = "serde", serde(alias = "table_captions"))]
106 pub table_captions: bool,
107
108 #[cfg_attr(feature = "serde", serde(alias = "fenced_divs"))]
111 pub fenced_divs: bool,
112 #[cfg_attr(feature = "serde", serde(alias = "native_divs"))]
114 pub native_divs: bool,
115
116 #[cfg_attr(feature = "serde", serde(alias = "line_blocks"))]
119 pub line_blocks: bool,
120
121 #[cfg_attr(feature = "serde", serde(alias = "intraword_underscores"))]
126 pub intraword_underscores: bool,
127 pub strikeout: bool,
129 pub superscript: bool,
131 pub subscript: bool,
132
133 #[cfg_attr(feature = "serde", serde(alias = "inline_links"))]
136 pub inline_links: bool,
137 #[cfg_attr(feature = "serde", serde(alias = "reference_links"))]
139 pub reference_links: bool,
140 #[cfg_attr(feature = "serde", serde(alias = "shortcut_reference_links"))]
142 pub shortcut_reference_links: bool,
143 #[cfg_attr(feature = "serde", serde(alias = "link_attributes"))]
145 pub link_attributes: bool,
146 pub autolinks: bool,
148
149 #[cfg_attr(feature = "serde", serde(alias = "inline_images"))]
152 pub inline_images: bool,
153 #[cfg_attr(feature = "serde", serde(alias = "implicit_figures"))]
155 pub implicit_figures: bool,
156
157 #[cfg_attr(feature = "serde", serde(alias = "tex_math_dollars"))]
160 pub tex_math_dollars: bool,
161 #[cfg_attr(feature = "serde", serde(alias = "tex_math_gfm"))]
163 pub tex_math_gfm: bool,
164 #[cfg_attr(feature = "serde", serde(alias = "tex_math_single_backslash"))]
166 pub tex_math_single_backslash: bool,
167 #[cfg_attr(feature = "serde", serde(alias = "tex_math_double_backslash"))]
169 pub tex_math_double_backslash: bool,
170
171 #[cfg_attr(feature = "serde", serde(alias = "inline_footnotes"))]
174 pub inline_footnotes: bool,
175 pub footnotes: bool,
177
178 pub citations: bool,
181
182 #[cfg_attr(feature = "serde", serde(alias = "bracketed_spans"))]
185 pub bracketed_spans: bool,
186 #[cfg_attr(feature = "serde", serde(alias = "native_spans"))]
188 pub native_spans: bool,
189
190 #[cfg_attr(feature = "serde", serde(alias = "yaml_metadata_block"))]
193 pub yaml_metadata_block: bool,
194 #[cfg_attr(feature = "serde", serde(alias = "pandoc_title_block"))]
196 pub pandoc_title_block: bool,
197 pub mmd_title_block: bool,
199
200 #[cfg_attr(feature = "serde", serde(alias = "raw_html"))]
203 pub raw_html: bool,
204 #[cfg_attr(feature = "serde", serde(alias = "markdown_in_html_blocks"))]
206 pub markdown_in_html_blocks: bool,
207 #[cfg_attr(feature = "serde", serde(alias = "raw_tex"))]
209 pub raw_tex: bool,
210 #[cfg_attr(feature = "serde", serde(alias = "raw_attribute"))]
212 pub raw_attribute: bool,
213
214 #[cfg_attr(feature = "serde", serde(alias = "all_symbols_escapable"))]
217 pub all_symbols_escapable: bool,
218 #[cfg_attr(feature = "serde", serde(alias = "escaped_line_breaks"))]
220 pub escaped_line_breaks: bool,
221
222 #[cfg_attr(feature = "serde", serde(alias = "autolink_bare_uris"))]
226 pub autolink_bare_uris: bool,
227 #[cfg_attr(feature = "serde", serde(alias = "hard_line_breaks"))]
229 pub hard_line_breaks: bool,
230 pub mmd_header_identifiers: bool,
232 pub mmd_link_attributes: bool,
234 pub alerts: bool,
236 pub emoji: bool,
238 pub mark: bool,
240
241 #[cfg_attr(feature = "serde", serde(alias = "quarto_callouts"))]
244 pub quarto_callouts: bool,
245 #[cfg_attr(feature = "serde", serde(alias = "quarto_crossrefs"))]
247 pub quarto_crossrefs: bool,
248 #[cfg_attr(feature = "serde", serde(alias = "quarto_shortcodes"))]
250 pub quarto_shortcodes: bool,
251 pub bookdown_references: bool,
253 pub bookdown_equation_references: bool,
255}
256
257impl Default for Extensions {
258 fn default() -> Self {
259 Self::for_flavor(Flavor::default())
260 }
261}
262
263impl Extensions {
264 fn none_defaults() -> Self {
265 Self {
266 alerts: false,
267 all_symbols_escapable: false,
268 auto_identifiers: false,
269 autolink_bare_uris: false,
270 autolinks: false,
271 backtick_code_blocks: false,
272 blank_before_blockquote: false,
273 blank_before_header: false,
274 bookdown_references: false,
275 bookdown_equation_references: false,
276 bracketed_spans: false,
277 citations: false,
278 definition_lists: false,
279 emoji: false,
280 escaped_line_breaks: false,
281 example_lists: false,
282 executable_code: false,
283 rmarkdown_inline_code: false,
284 quarto_inline_code: false,
285 fancy_lists: false,
286 fenced_code_attributes: false,
287 fenced_code_blocks: false,
288 fenced_divs: false,
289 footnotes: false,
290 gfm_auto_identifiers: false,
291 grid_tables: false,
292 hard_line_breaks: false,
293 header_attributes: false,
294 implicit_figures: false,
295 implicit_header_references: false,
296 inline_code_attributes: false,
297 inline_footnotes: false,
298 inline_images: false,
299 inline_links: false,
300 intraword_underscores: false,
301 line_blocks: false,
302 link_attributes: false,
303 mark: false,
304 markdown_in_html_blocks: false,
305 mmd_header_identifiers: false,
306 mmd_link_attributes: false,
307 mmd_title_block: false,
308 multiline_tables: false,
309 native_divs: false,
310 native_spans: false,
311 pandoc_title_block: false,
312 pipe_tables: false,
313 quarto_callouts: false,
314 quarto_crossrefs: false,
315 quarto_shortcodes: false,
316 raw_attribute: false,
317 raw_html: false,
318 raw_tex: false,
319 reference_links: false,
320 shortcut_reference_links: false,
321 simple_tables: false,
322 startnum: false,
323 strikeout: false,
324 subscript: false,
325 superscript: false,
326 table_captions: false,
327 task_lists: false,
328 tex_math_dollars: false,
329 tex_math_double_backslash: false,
330 tex_math_gfm: false,
331 tex_math_single_backslash: false,
332 yaml_metadata_block: false,
333 }
334 }
335
336 pub fn for_flavor(flavor: Flavor) -> Self {
338 match flavor {
339 Flavor::Pandoc => Self::pandoc_defaults(),
340 Flavor::Quarto => Self::quarto_defaults(),
341 Flavor::RMarkdown => Self::rmarkdown_defaults(),
342 Flavor::Gfm => Self::gfm_defaults(),
343 Flavor::CommonMark => Self::commonmark_defaults(),
344 Flavor::MultiMarkdown => Self::multimarkdown_defaults(),
345 }
346 }
347
348 fn pandoc_defaults() -> Self {
349 Self {
350 auto_identifiers: true,
352 blank_before_blockquote: true,
353 blank_before_header: true,
354 gfm_auto_identifiers: false,
355 header_attributes: true,
356 implicit_header_references: true,
357
358 definition_lists: true,
360 example_lists: true,
361 fancy_lists: true,
362 startnum: true,
363 task_lists: true,
364
365 backtick_code_blocks: true,
367 executable_code: false,
368 rmarkdown_inline_code: false,
369 quarto_inline_code: false,
370 fenced_code_attributes: true,
371 fenced_code_blocks: true,
372 inline_code_attributes: true,
373
374 grid_tables: true,
376 multiline_tables: true,
377 pipe_tables: true,
378 simple_tables: true,
379 table_captions: true,
380
381 fenced_divs: true,
383 native_divs: true,
384
385 line_blocks: true,
387
388 intraword_underscores: true,
390 strikeout: true,
391 subscript: true,
392 superscript: true,
393
394 autolinks: true,
396 inline_links: true,
397 link_attributes: true,
398 reference_links: true,
399 shortcut_reference_links: true,
400
401 implicit_figures: true,
403 inline_images: true,
404
405 tex_math_dollars: true,
407 tex_math_double_backslash: false,
408 tex_math_gfm: false,
409 tex_math_single_backslash: false,
410
411 footnotes: true,
413 inline_footnotes: true,
414
415 citations: true,
417
418 bracketed_spans: true,
420 native_spans: true,
421
422 mmd_title_block: false,
424 pandoc_title_block: true,
425 yaml_metadata_block: true,
426
427 markdown_in_html_blocks: false,
429 raw_attribute: true,
430 raw_html: true,
431 raw_tex: true,
432
433 all_symbols_escapable: true,
435 escaped_line_breaks: true,
436
437 alerts: false,
439 autolink_bare_uris: false,
440 emoji: false,
441 hard_line_breaks: false,
442 mark: false,
443 mmd_header_identifiers: false,
444 mmd_link_attributes: false,
445
446 bookdown_references: false,
448 bookdown_equation_references: false,
449 quarto_callouts: false,
450 quarto_crossrefs: false,
451 quarto_shortcodes: false,
452 }
453 }
454
455 fn quarto_defaults() -> Self {
456 let mut ext = Self::pandoc_defaults();
457
458 ext.executable_code = true;
459 ext.rmarkdown_inline_code = true;
460 ext.quarto_inline_code = true;
461 ext.quarto_callouts = true;
462 ext.quarto_crossrefs = true;
463 ext.quarto_shortcodes = true;
464
465 ext
466 }
467
468 fn rmarkdown_defaults() -> Self {
469 let mut ext = Self::pandoc_defaults();
470
471 ext.bookdown_references = true;
472 ext.bookdown_equation_references = true;
473 ext.executable_code = true;
474 ext.rmarkdown_inline_code = true;
475 ext.quarto_inline_code = false;
476 ext.tex_math_dollars = true;
477 ext.tex_math_single_backslash = true;
478
479 ext
480 }
481
482 fn gfm_defaults() -> Self {
483 let mut ext = Self::none_defaults();
484
485 ext.alerts = true;
486 ext.auto_identifiers = true;
487 ext.autolink_bare_uris = true;
488 ext.backtick_code_blocks = true;
489 ext.emoji = true;
490 ext.fenced_code_blocks = true;
491 ext.footnotes = true;
492 ext.gfm_auto_identifiers = true;
493 ext.pipe_tables = true;
494 ext.raw_html = true;
495 ext.strikeout = true;
496 ext.task_lists = true;
497 ext.tex_math_dollars = true;
498 ext.tex_math_gfm = true;
499 ext.yaml_metadata_block = true;
500
501 ext
502 }
503
504 fn commonmark_defaults() -> Self {
505 let mut ext = Self::none_defaults();
506 ext.raw_html = true;
507 ext
508 }
509
510 fn multimarkdown_defaults() -> Self {
511 let mut ext = Self::none_defaults();
512
513 ext.all_symbols_escapable = true;
514 ext.auto_identifiers = true;
515 ext.backtick_code_blocks = true;
516 ext.definition_lists = true;
517 ext.footnotes = true;
518 ext.implicit_figures = true;
519 ext.implicit_header_references = true;
520 ext.intraword_underscores = true;
521 ext.mmd_header_identifiers = true;
522 ext.mmd_link_attributes = true;
523 ext.mmd_title_block = true;
524 ext.pipe_tables = true;
525 ext.raw_attribute = true;
526 ext.raw_html = true;
527 ext.reference_links = true;
528 ext.shortcut_reference_links = true;
529 ext.subscript = true;
530 ext.superscript = true;
531 ext.tex_math_dollars = true;
532 ext.tex_math_double_backslash = true;
533
534 ext
535 }
536
537 pub fn merge_with_flavor(user_overrides: HashMap<String, bool>, flavor: Flavor) -> Self {
551 let defaults = Self::for_flavor(flavor);
552 Self::merge_overrides(defaults, user_overrides)
553 }
554
555 fn merge_overrides(mut base: Extensions, user_overrides: HashMap<String, bool>) -> Self {
556 for (key, value) in user_overrides {
557 let normalized_key = key.replace('_', "-");
558 match normalized_key.as_str() {
559 "blank-before-header" => base.blank_before_header = value,
560 "header-attributes" => base.header_attributes = value,
561 "auto-identifiers" => base.auto_identifiers = value,
562 "gfm-auto-identifiers" => base.gfm_auto_identifiers = value,
563 "implicit-header-references" => base.implicit_header_references = value,
564 "blank-before-blockquote" => base.blank_before_blockquote = value,
565 "fancy-lists" => base.fancy_lists = value,
566 "startnum" => base.startnum = value,
567 "example-lists" => base.example_lists = value,
568 "task-lists" => base.task_lists = value,
569 "definition-lists" => base.definition_lists = value,
570 "backtick-code-blocks" => base.backtick_code_blocks = value,
571 "fenced-code-blocks" => base.fenced_code_blocks = value,
572 "fenced-code-attributes" => base.fenced_code_attributes = value,
573 "executable-code" => base.executable_code = value,
574 "rmarkdown-inline-code" => base.rmarkdown_inline_code = value,
575 "quarto-inline-code" => base.quarto_inline_code = value,
576 "inline-code-attributes" => base.inline_code_attributes = value,
577 "simple-tables" => base.simple_tables = value,
578 "multiline-tables" => base.multiline_tables = value,
579 "grid-tables" => base.grid_tables = value,
580 "pipe-tables" => base.pipe_tables = value,
581 "table-captions" => base.table_captions = value,
582 "fenced-divs" => base.fenced_divs = value,
583 "native-divs" => base.native_divs = value,
584 "line-blocks" => base.line_blocks = value,
585 "intraword-underscores" => base.intraword_underscores = value,
586 "strikeout" => base.strikeout = value,
587 "superscript" => base.superscript = value,
588 "subscript" => base.subscript = value,
589 "inline-links" => base.inline_links = value,
590 "reference-links" => base.reference_links = value,
591 "shortcut-reference-links" => base.shortcut_reference_links = value,
592 "link-attributes" => base.link_attributes = value,
593 "autolinks" => base.autolinks = value,
594 "inline-images" => base.inline_images = value,
595 "implicit-figures" => base.implicit_figures = value,
596 "tex-math-dollars" => base.tex_math_dollars = value,
597 "tex-math-gfm" => base.tex_math_gfm = value,
598 "tex-math-single-backslash" => base.tex_math_single_backslash = value,
599 "tex-math-double-backslash" => base.tex_math_double_backslash = value,
600 "inline-footnotes" => base.inline_footnotes = value,
601 "footnotes" => base.footnotes = value,
602 "citations" => base.citations = value,
603 "bracketed-spans" => base.bracketed_spans = value,
604 "native-spans" => base.native_spans = value,
605 "yaml-metadata-block" => base.yaml_metadata_block = value,
606 "pandoc-title-block" => base.pandoc_title_block = value,
607 "mmd-title-block" => base.mmd_title_block = value,
608 "raw-html" => base.raw_html = value,
609 "markdown-in-html-blocks" => base.markdown_in_html_blocks = value,
610 "raw-tex" => base.raw_tex = value,
611 "raw-attribute" => base.raw_attribute = value,
612 "all-symbols-escapable" => base.all_symbols_escapable = value,
613 "escaped-line-breaks" => base.escaped_line_breaks = value,
614 "autolink-bare-uris" => base.autolink_bare_uris = value,
615 "hard-line-breaks" => base.hard_line_breaks = value,
616 "mmd-header-identifiers" => base.mmd_header_identifiers = value,
617 "mmd-link-attributes" => base.mmd_link_attributes = value,
618 "alerts" => base.alerts = value,
619 "emoji" => base.emoji = value,
620 "mark" => base.mark = value,
621 "quarto-callouts" => base.quarto_callouts = value,
622 "quarto-crossrefs" => base.quarto_crossrefs = value,
623 "quarto-shortcodes" => base.quarto_shortcodes = value,
624 "bookdown-references" => base.bookdown_references = value,
625 "bookdown-equation-references" => base.bookdown_equation_references = value,
626 _ => {}
627 }
628 }
629 base
630 }
631}
632
633#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
634#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
635pub enum PandocCompat {
636 #[cfg_attr(feature = "serde", serde(rename = "latest"))]
641 Latest,
642 #[cfg_attr(
644 feature = "serde",
645 serde(rename = "3.7", alias = "3-7", alias = "v3.7", alias = "v3-7")
646 )]
647 V3_7,
648 #[default]
650 #[cfg_attr(
651 feature = "serde",
652 serde(rename = "3.9", alias = "3-9", alias = "v3.9", alias = "v3-9")
653 )]
654 V3_9,
655}
656
657impl PandocCompat {
658 pub const PINNED_LATEST: Self = Self::V3_9;
660
661 pub fn effective(self) -> Self {
662 match self {
663 Self::Latest => Self::PINNED_LATEST,
664 other => other,
665 }
666 }
667}
668
669#[derive(Debug, Clone)]
670#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
671#[cfg_attr(feature = "serde", serde(default, rename_all = "kebab-case"))]
672pub struct ParserOptions {
673 pub flavor: Flavor,
674 pub extensions: Extensions,
675 pub pandoc_compat: PandocCompat,
677}
678
679impl Default for ParserOptions {
680 fn default() -> Self {
681 let flavor = Flavor::default();
682 Self {
683 flavor,
684 extensions: Extensions::for_flavor(flavor),
685 pandoc_compat: PandocCompat::default(),
686 }
687 }
688}
689
690impl ParserOptions {
691 pub fn effective_pandoc_compat(&self) -> PandocCompat {
692 self.pandoc_compat.effective()
693 }
694}