1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
9pub enum Flavor {
10 #[default]
12 Pandoc,
13 Quarto,
15 #[cfg_attr(feature = "serde", serde(rename = "rmarkdown"))]
17 RMarkdown,
18 Gfm,
20 #[cfg_attr(feature = "serde", serde(alias = "commonmark"))]
22 CommonMark,
23 #[cfg_attr(feature = "serde", serde(rename = "multimarkdown"))]
25 MultiMarkdown,
26}
27
28#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(feature = "serde", serde(default))]
34#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
35#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
36pub struct Extensions {
37 #[cfg_attr(feature = "serde", serde(alias = "blank_before_header"))]
42 pub blank_before_header: bool,
43 #[cfg_attr(feature = "serde", serde(alias = "header_attributes"))]
45 pub header_attributes: bool,
46 pub auto_identifiers: bool,
48 pub gfm_auto_identifiers: bool,
50 pub implicit_header_references: bool,
52
53 #[cfg_attr(feature = "serde", serde(alias = "blank_before_blockquote"))]
56 pub blank_before_blockquote: bool,
57
58 #[cfg_attr(feature = "serde", serde(alias = "fancy_lists"))]
61 pub fancy_lists: bool,
62 pub startnum: bool,
64 #[cfg_attr(feature = "serde", serde(alias = "example_lists"))]
66 pub example_lists: bool,
67 #[cfg_attr(feature = "serde", serde(alias = "task_lists"))]
69 pub task_lists: bool,
70 #[cfg_attr(feature = "serde", serde(alias = "definition_lists"))]
72 pub definition_lists: bool,
73 #[cfg_attr(feature = "serde", serde(alias = "lists_without_preceding_blankline"))]
75 pub lists_without_preceding_blankline: bool,
76 #[cfg_attr(feature = "serde", serde(alias = "four_space_rule"))]
79 pub four_space_rule: bool,
80
81 #[cfg_attr(feature = "serde", serde(alias = "backtick_code_blocks"))]
84 pub backtick_code_blocks: bool,
85 #[cfg_attr(feature = "serde", serde(alias = "fenced_code_blocks"))]
87 pub fenced_code_blocks: bool,
88 #[cfg_attr(feature = "serde", serde(alias = "fenced_code_attributes"))]
90 pub fenced_code_attributes: bool,
91 pub executable_code: bool,
93 pub rmarkdown_inline_code: bool,
95 pub quarto_inline_code: bool,
97 #[cfg_attr(feature = "serde", serde(alias = "inline_code_attributes"))]
99 pub inline_code_attributes: bool,
100
101 #[cfg_attr(feature = "serde", serde(alias = "simple_tables"))]
104 pub simple_tables: bool,
105 #[cfg_attr(feature = "serde", serde(alias = "multiline_tables"))]
107 pub multiline_tables: bool,
108 #[cfg_attr(feature = "serde", serde(alias = "grid_tables"))]
110 pub grid_tables: bool,
111 #[cfg_attr(feature = "serde", serde(alias = "pipe_tables"))]
113 pub pipe_tables: bool,
114 #[cfg_attr(feature = "serde", serde(alias = "table_captions"))]
116 pub table_captions: bool,
117
118 #[cfg_attr(feature = "serde", serde(alias = "fenced_divs"))]
121 pub fenced_divs: bool,
122 #[cfg_attr(feature = "serde", serde(alias = "native_divs"))]
124 pub native_divs: bool,
125
126 #[cfg_attr(feature = "serde", serde(alias = "line_blocks"))]
129 pub line_blocks: bool,
130
131 #[cfg_attr(feature = "serde", serde(alias = "intraword_underscores"))]
136 pub intraword_underscores: bool,
137 pub strikeout: bool,
139 pub superscript: bool,
141 pub subscript: bool,
142
143 #[cfg_attr(feature = "serde", serde(alias = "inline_links"))]
146 pub inline_links: bool,
147 #[cfg_attr(feature = "serde", serde(alias = "reference_links"))]
149 pub reference_links: bool,
150 #[cfg_attr(feature = "serde", serde(alias = "shortcut_reference_links"))]
152 pub shortcut_reference_links: bool,
153 #[cfg_attr(feature = "serde", serde(alias = "link_attributes"))]
155 pub link_attributes: bool,
156 pub autolinks: bool,
158
159 #[cfg_attr(feature = "serde", serde(alias = "inline_images"))]
162 pub inline_images: bool,
163 #[cfg_attr(feature = "serde", serde(alias = "implicit_figures"))]
165 pub implicit_figures: bool,
166
167 #[cfg_attr(feature = "serde", serde(alias = "tex_math_dollars"))]
170 pub tex_math_dollars: bool,
171 #[cfg_attr(feature = "serde", serde(alias = "tex_math_gfm"))]
173 pub tex_math_gfm: bool,
174 #[cfg_attr(feature = "serde", serde(alias = "tex_math_single_backslash"))]
176 pub tex_math_single_backslash: bool,
177 #[cfg_attr(feature = "serde", serde(alias = "tex_math_double_backslash"))]
179 pub tex_math_double_backslash: bool,
180
181 #[cfg_attr(feature = "serde", serde(alias = "inline_footnotes"))]
184 pub inline_footnotes: bool,
185 pub footnotes: bool,
187
188 pub citations: bool,
191
192 #[cfg_attr(feature = "serde", serde(alias = "bracketed_spans"))]
195 pub bracketed_spans: bool,
196 #[cfg_attr(feature = "serde", serde(alias = "native_spans"))]
198 pub native_spans: bool,
199
200 #[cfg_attr(feature = "serde", serde(alias = "yaml_metadata_block"))]
203 pub yaml_metadata_block: bool,
204 #[cfg_attr(feature = "serde", serde(alias = "pandoc_title_block"))]
206 pub pandoc_title_block: bool,
207 pub mmd_title_block: bool,
209
210 #[cfg_attr(feature = "serde", serde(alias = "raw_html"))]
213 pub raw_html: bool,
214 #[cfg_attr(feature = "serde", serde(alias = "markdown_in_html_blocks"))]
216 pub markdown_in_html_blocks: bool,
217 #[cfg_attr(feature = "serde", serde(alias = "raw_tex"))]
219 pub raw_tex: bool,
220 #[cfg_attr(feature = "serde", serde(alias = "raw_attribute"))]
222 pub raw_attribute: bool,
223
224 #[cfg_attr(feature = "serde", serde(alias = "all_symbols_escapable"))]
227 pub all_symbols_escapable: bool,
228 #[cfg_attr(feature = "serde", serde(alias = "escaped_line_breaks"))]
230 pub escaped_line_breaks: bool,
231
232 #[cfg_attr(feature = "serde", serde(alias = "autolink_bare_uris"))]
236 pub autolink_bare_uris: bool,
237 #[cfg_attr(feature = "serde", serde(alias = "hard_line_breaks"))]
239 pub hard_line_breaks: bool,
240 pub mmd_header_identifiers: bool,
242 pub mmd_link_attributes: bool,
244 pub alerts: bool,
246 pub emoji: bool,
248 pub mark: bool,
250
251 #[cfg_attr(feature = "serde", serde(alias = "quarto_callouts"))]
254 pub quarto_callouts: bool,
255 #[cfg_attr(feature = "serde", serde(alias = "quarto_crossrefs"))]
257 pub quarto_crossrefs: bool,
258 #[cfg_attr(feature = "serde", serde(alias = "quarto_shortcodes"))]
260 pub quarto_shortcodes: bool,
261 pub bookdown_references: bool,
263 pub bookdown_equation_references: bool,
265}
266
267impl Default for Extensions {
268 fn default() -> Self {
269 Self::for_flavor(Flavor::default())
270 }
271}
272
273impl Extensions {
274 fn none_defaults() -> Self {
275 Self {
276 alerts: false,
277 all_symbols_escapable: false,
278 auto_identifiers: false,
279 autolink_bare_uris: false,
280 autolinks: false,
281 backtick_code_blocks: false,
282 blank_before_blockquote: false,
283 blank_before_header: false,
284 bookdown_references: false,
285 bookdown_equation_references: false,
286 bracketed_spans: false,
287 citations: false,
288 definition_lists: false,
289 lists_without_preceding_blankline: false,
290 emoji: false,
291 escaped_line_breaks: false,
292 example_lists: false,
293 executable_code: false,
294 rmarkdown_inline_code: false,
295 quarto_inline_code: false,
296 fancy_lists: false,
297 fenced_code_attributes: false,
298 fenced_code_blocks: false,
299 fenced_divs: false,
300 footnotes: false,
301 four_space_rule: false,
302 gfm_auto_identifiers: false,
303 grid_tables: false,
304 hard_line_breaks: false,
305 header_attributes: false,
306 implicit_figures: false,
307 implicit_header_references: false,
308 inline_code_attributes: false,
309 inline_footnotes: false,
310 inline_images: false,
311 inline_links: false,
312 intraword_underscores: false,
313 line_blocks: false,
314 link_attributes: false,
315 mark: false,
316 markdown_in_html_blocks: false,
317 mmd_header_identifiers: false,
318 mmd_link_attributes: false,
319 mmd_title_block: false,
320 multiline_tables: false,
321 native_divs: false,
322 native_spans: false,
323 pandoc_title_block: false,
324 pipe_tables: false,
325 quarto_callouts: false,
326 quarto_crossrefs: false,
327 quarto_shortcodes: false,
328 raw_attribute: false,
329 raw_html: false,
330 raw_tex: false,
331 reference_links: false,
332 shortcut_reference_links: false,
333 simple_tables: false,
334 startnum: false,
335 strikeout: false,
336 subscript: false,
337 superscript: false,
338 table_captions: false,
339 task_lists: false,
340 tex_math_dollars: false,
341 tex_math_double_backslash: false,
342 tex_math_gfm: false,
343 tex_math_single_backslash: false,
344 yaml_metadata_block: false,
345 }
346 }
347
348 pub fn for_flavor(flavor: Flavor) -> Self {
350 match flavor {
351 Flavor::Pandoc => Self::pandoc_defaults(),
352 Flavor::Quarto => Self::quarto_defaults(),
353 Flavor::RMarkdown => Self::rmarkdown_defaults(),
354 Flavor::Gfm => Self::gfm_defaults(),
355 Flavor::CommonMark => Self::commonmark_defaults(),
356 Flavor::MultiMarkdown => Self::multimarkdown_defaults(),
357 }
358 }
359
360 fn pandoc_defaults() -> Self {
361 Self {
362 auto_identifiers: true,
364 blank_before_blockquote: true,
365 blank_before_header: true,
366 gfm_auto_identifiers: false,
367 header_attributes: true,
368 implicit_header_references: true,
369
370 definition_lists: true,
372 example_lists: true,
373 fancy_lists: true,
374 lists_without_preceding_blankline: false,
375 startnum: true,
376 task_lists: true,
377
378 backtick_code_blocks: true,
380 executable_code: false,
381 rmarkdown_inline_code: false,
382 quarto_inline_code: false,
383 fenced_code_attributes: true,
384 fenced_code_blocks: true,
385 inline_code_attributes: true,
386
387 grid_tables: true,
389 multiline_tables: true,
390 pipe_tables: true,
391 simple_tables: true,
392 table_captions: true,
393
394 fenced_divs: true,
396 native_divs: true,
397
398 line_blocks: true,
400
401 intraword_underscores: true,
403 strikeout: true,
404 subscript: true,
405 superscript: true,
406
407 autolinks: true,
409 inline_links: true,
410 link_attributes: true,
411 reference_links: true,
412 shortcut_reference_links: true,
413
414 implicit_figures: true,
416 inline_images: true,
417
418 tex_math_dollars: true,
420 tex_math_double_backslash: false,
421 tex_math_gfm: false,
422 tex_math_single_backslash: false,
423
424 footnotes: true,
426 inline_footnotes: true,
427
428 citations: true,
430
431 bracketed_spans: true,
433 native_spans: true,
434
435 mmd_title_block: false,
437 pandoc_title_block: true,
438 yaml_metadata_block: true,
439
440 markdown_in_html_blocks: false,
442 raw_attribute: true,
443 raw_html: true,
444 raw_tex: true,
445
446 all_symbols_escapable: true,
448 escaped_line_breaks: true,
449
450 alerts: false,
452 autolink_bare_uris: false,
453 emoji: false,
454 four_space_rule: false,
455 hard_line_breaks: false,
456 mark: false,
457 mmd_header_identifiers: false,
458 mmd_link_attributes: false,
459
460 bookdown_references: false,
462 bookdown_equation_references: false,
463 quarto_callouts: false,
464 quarto_crossrefs: false,
465 quarto_shortcodes: false,
466 }
467 }
468
469 fn quarto_defaults() -> Self {
470 let mut ext = Self::pandoc_defaults();
471
472 ext.executable_code = true;
473 ext.rmarkdown_inline_code = true;
474 ext.quarto_inline_code = true;
475 ext.quarto_callouts = true;
476 ext.quarto_crossrefs = true;
477 ext.quarto_shortcodes = true;
478
479 ext
480 }
481
482 fn rmarkdown_defaults() -> Self {
483 let mut ext = Self::pandoc_defaults();
484
485 ext.bookdown_references = true;
486 ext.bookdown_equation_references = true;
487 ext.executable_code = true;
488 ext.rmarkdown_inline_code = true;
489 ext.quarto_inline_code = false;
490 ext.tex_math_dollars = true;
491 ext.tex_math_single_backslash = true;
492
493 ext
494 }
495
496 fn gfm_defaults() -> Self {
497 let mut ext = Self::none_defaults();
498
499 ext.alerts = true;
500 ext.auto_identifiers = true;
501 ext.autolink_bare_uris = true;
502 ext.autolinks = true;
503 ext.backtick_code_blocks = true;
504 ext.emoji = true;
505 ext.fenced_code_blocks = true;
506 ext.footnotes = true;
507 ext.gfm_auto_identifiers = true;
508 ext.inline_images = true;
509 ext.inline_links = true;
510 ext.pipe_tables = true;
511 ext.raw_html = true;
512 ext.reference_links = true;
513 ext.shortcut_reference_links = true;
514 ext.strikeout = true;
515 ext.task_lists = true;
516 ext.tex_math_dollars = true;
517 ext.tex_math_gfm = true;
518 ext.yaml_metadata_block = true;
519
520 ext
521 }
522
523 fn commonmark_defaults() -> Self {
524 let mut ext = Self::none_defaults();
525 ext.autolinks = true;
535 ext.backtick_code_blocks = true;
536 ext.escaped_line_breaks = true;
537 ext.fenced_code_blocks = true;
538 ext.inline_images = true;
539 ext.inline_links = true;
540 ext.intraword_underscores = true;
541 ext.raw_html = true;
542 ext.reference_links = true;
543 ext.shortcut_reference_links = true;
544 ext
545 }
546
547 fn multimarkdown_defaults() -> Self {
548 let mut ext = Self::none_defaults();
549
550 ext.all_symbols_escapable = true;
551 ext.auto_identifiers = true;
552 ext.backtick_code_blocks = true;
553 ext.definition_lists = true;
554 ext.footnotes = true;
555 ext.implicit_figures = true;
556 ext.implicit_header_references = true;
557 ext.intraword_underscores = true;
558 ext.mmd_header_identifiers = true;
559 ext.mmd_link_attributes = true;
560 ext.mmd_title_block = true;
561 ext.pipe_tables = true;
562 ext.raw_attribute = true;
563 ext.raw_html = true;
564 ext.reference_links = true;
565 ext.shortcut_reference_links = true;
566 ext.subscript = true;
567 ext.superscript = true;
568 ext.tex_math_dollars = true;
569 ext.tex_math_double_backslash = true;
570
571 ext
572 }
573
574 pub fn merge_with_flavor(user_overrides: HashMap<String, bool>, flavor: Flavor) -> Self {
588 let defaults = Self::for_flavor(flavor);
589 Self::merge_overrides(defaults, user_overrides)
590 }
591
592 fn merge_overrides(mut base: Extensions, user_overrides: HashMap<String, bool>) -> Self {
593 for (key, value) in user_overrides {
594 let normalized_key = key.replace('_', "-");
595 match normalized_key.as_str() {
596 "blank-before-header" => base.blank_before_header = value,
597 "header-attributes" => base.header_attributes = value,
598 "auto-identifiers" => base.auto_identifiers = value,
599 "gfm-auto-identifiers" => base.gfm_auto_identifiers = value,
600 "implicit-header-references" => base.implicit_header_references = value,
601 "blank-before-blockquote" => base.blank_before_blockquote = value,
602 "fancy-lists" => base.fancy_lists = value,
603 "startnum" => base.startnum = value,
604 "example-lists" => base.example_lists = value,
605 "task-lists" => base.task_lists = value,
606 "definition-lists" => base.definition_lists = value,
607 "lists-without-preceding-blankline" => {
608 base.lists_without_preceding_blankline = value
609 }
610 "four-space-rule" => base.four_space_rule = value,
611 "backtick-code-blocks" => base.backtick_code_blocks = value,
612 "fenced-code-blocks" => base.fenced_code_blocks = value,
613 "fenced-code-attributes" => base.fenced_code_attributes = value,
614 "executable-code" => base.executable_code = value,
615 "rmarkdown-inline-code" => base.rmarkdown_inline_code = value,
616 "quarto-inline-code" => base.quarto_inline_code = value,
617 "inline-code-attributes" => base.inline_code_attributes = value,
618 "simple-tables" => base.simple_tables = value,
619 "multiline-tables" => base.multiline_tables = value,
620 "grid-tables" => base.grid_tables = value,
621 "pipe-tables" => base.pipe_tables = value,
622 "table-captions" => base.table_captions = value,
623 "fenced-divs" => base.fenced_divs = value,
624 "native-divs" => base.native_divs = value,
625 "line-blocks" => base.line_blocks = value,
626 "intraword-underscores" => base.intraword_underscores = value,
627 "strikeout" => base.strikeout = value,
628 "superscript" => base.superscript = value,
629 "subscript" => base.subscript = value,
630 "inline-links" => base.inline_links = value,
631 "reference-links" => base.reference_links = value,
632 "shortcut-reference-links" => base.shortcut_reference_links = value,
633 "link-attributes" => base.link_attributes = value,
634 "autolinks" => base.autolinks = value,
635 "inline-images" => base.inline_images = value,
636 "implicit-figures" => base.implicit_figures = value,
637 "tex-math-dollars" => base.tex_math_dollars = value,
638 "tex-math-gfm" => base.tex_math_gfm = value,
639 "tex-math-single-backslash" => base.tex_math_single_backslash = value,
640 "tex-math-double-backslash" => base.tex_math_double_backslash = value,
641 "inline-footnotes" => base.inline_footnotes = value,
642 "footnotes" => base.footnotes = value,
643 "citations" => base.citations = value,
644 "bracketed-spans" => base.bracketed_spans = value,
645 "native-spans" => base.native_spans = value,
646 "yaml-metadata-block" => base.yaml_metadata_block = value,
647 "pandoc-title-block" => base.pandoc_title_block = value,
648 "mmd-title-block" => base.mmd_title_block = value,
649 "raw-html" => base.raw_html = value,
650 "markdown-in-html-blocks" => base.markdown_in_html_blocks = value,
651 "raw-tex" => base.raw_tex = value,
652 "raw-attribute" => base.raw_attribute = value,
653 "all-symbols-escapable" => base.all_symbols_escapable = value,
654 "escaped-line-breaks" => base.escaped_line_breaks = value,
655 "autolink-bare-uris" => base.autolink_bare_uris = value,
656 "hard-line-breaks" => base.hard_line_breaks = value,
657 "mmd-header-identifiers" => base.mmd_header_identifiers = value,
658 "mmd-link-attributes" => base.mmd_link_attributes = value,
659 "alerts" => base.alerts = value,
660 "emoji" => base.emoji = value,
661 "mark" => base.mark = value,
662 "quarto-callouts" => base.quarto_callouts = value,
663 "quarto-crossrefs" => base.quarto_crossrefs = value,
664 "quarto-shortcodes" => base.quarto_shortcodes = value,
665 "bookdown-references" => base.bookdown_references = value,
666 "bookdown-equation-references" => base.bookdown_equation_references = value,
667 _ => {}
668 }
669 }
670 base
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::{Extensions, Flavor};
677 use std::collections::HashMap;
678
679 #[test]
680 fn merge_with_flavor_keeps_known_extension_overrides() {
681 let mut overrides = HashMap::new();
682 overrides.insert("intraword-underscores".to_string(), false);
683 let ext = Extensions::merge_with_flavor(overrides, Flavor::Pandoc);
684 assert!(!ext.intraword_underscores);
685 }
686
687 #[test]
688 fn merge_with_flavor_ignores_unknown_extension_overrides() {
689 let mut overrides = HashMap::new();
690 overrides.insert("smart".to_string(), true);
691 overrides.insert("smart-quotes".to_string(), true);
692 let ext = Extensions::merge_with_flavor(overrides, Flavor::Gfm);
693 assert!(ext.strikeout, "known defaults should remain intact");
694 }
695
696 #[test]
697 fn lists_without_preceding_blankline_defaults_false_for_pandoc_and_gfm() {
698 assert!(!Extensions::for_flavor(Flavor::Pandoc).lists_without_preceding_blankline);
699 assert!(!Extensions::for_flavor(Flavor::Gfm).lists_without_preceding_blankline);
700 }
701
702 #[test]
703 fn merge_with_flavor_accepts_lists_without_preceding_blankline_override() {
704 let mut overrides = HashMap::new();
705 overrides.insert("lists-without-preceding-blankline".to_string(), true);
706 let ext = Extensions::merge_with_flavor(overrides, Flavor::Pandoc);
707 assert!(ext.lists_without_preceding_blankline);
708 }
709
710 #[test]
711 fn four_space_rule_defaults_off_for_every_flavor() {
712 for flavor in [
713 Flavor::Pandoc,
714 Flavor::Quarto,
715 Flavor::RMarkdown,
716 Flavor::Gfm,
717 Flavor::CommonMark,
718 Flavor::MultiMarkdown,
719 ] {
720 assert!(
721 !Extensions::for_flavor(flavor).four_space_rule,
722 "four_space_rule should be off by default for {flavor:?}"
723 );
724 }
725 }
726
727 #[test]
728 fn merge_with_flavor_accepts_four_space_rule_override() {
729 let mut overrides = HashMap::new();
730 overrides.insert("four-space-rule".to_string(), true);
731 let ext = Extensions::merge_with_flavor(overrides, Flavor::Pandoc);
732 assert!(ext.four_space_rule);
733 }
734}
735
736#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
737#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
738pub enum PandocCompat {
739 #[cfg_attr(feature = "serde", serde(rename = "latest"))]
744 Latest,
745 #[cfg_attr(
747 feature = "serde",
748 serde(rename = "3.7", alias = "3-7", alias = "v3.7", alias = "v3-7")
749 )]
750 V3_7,
751 #[default]
753 #[cfg_attr(
754 feature = "serde",
755 serde(rename = "3.9", alias = "3-9", alias = "v3.9", alias = "v3-9")
756 )]
757 V3_9,
758}
759
760impl PandocCompat {
761 pub const PINNED_LATEST: Self = Self::V3_9;
763
764 pub fn effective(self) -> Self {
765 match self {
766 Self::Latest => Self::PINNED_LATEST,
767 other => other,
768 }
769 }
770}
771
772#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
783#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
784#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
785#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
786pub enum Dialect {
787 #[default]
790 Pandoc,
791 CommonMark,
793}
794
795impl Dialect {
796 pub fn for_flavor(flavor: Flavor) -> Self {
798 match flavor {
799 Flavor::CommonMark | Flavor::Gfm => Dialect::CommonMark,
800 Flavor::Pandoc | Flavor::Quarto | Flavor::RMarkdown | Flavor::MultiMarkdown => {
801 Dialect::Pandoc
802 }
803 }
804 }
805}
806
807#[derive(Debug, Clone)]
808#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
809#[cfg_attr(feature = "serde", serde(default, rename_all = "kebab-case"))]
810#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
811pub struct ParserOptions {
812 pub flavor: Flavor,
813 pub dialect: Dialect,
814 pub extensions: Extensions,
815 pub pandoc_compat: PandocCompat,
817 #[cfg_attr(feature = "serde", serde(skip))]
828 pub refdef_labels: Option<Arc<HashSet<String>>>,
829}
830
831impl Default for ParserOptions {
832 fn default() -> Self {
833 let flavor = Flavor::default();
834 Self {
835 flavor,
836 dialect: Dialect::for_flavor(flavor),
837 extensions: Extensions::for_flavor(flavor),
838 pandoc_compat: PandocCompat::default(),
839 refdef_labels: None,
840 }
841 }
842}
843
844impl ParserOptions {
845 pub fn effective_pandoc_compat(&self) -> PandocCompat {
846 self.pandoc_compat.effective()
847 }
848}
849
850#[cfg(feature = "schema")]
851impl schemars::JsonSchema for Flavor {
852 fn schema_name() -> std::borrow::Cow<'static, str> {
853 "Flavor".into()
854 }
855
856 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
857 schemars::json_schema!({
861 "type": "string",
862 "description": "Markdown flavor to parse and format against.",
863 "enum": [
864 "pandoc",
865 "quarto",
866 "rmarkdown",
867 "gfm",
868 "common-mark",
869 "commonmark",
870 "multimarkdown"
871 ]
872 })
873 }
874}
875
876#[cfg(feature = "schema")]
877impl schemars::JsonSchema for PandocCompat {
878 fn schema_name() -> std::borrow::Cow<'static, str> {
879 "PandocCompat".into()
880 }
881
882 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
883 schemars::json_schema!({
884 "type": "string",
885 "description": "Compatibility target for ambiguous Pandoc behavior.",
886 "enum": [
887 "latest",
888 "3.7", "3-7", "v3.7", "v3-7",
889 "3.9", "3-9", "v3.9", "v3-9"
890 ]
891 })
892 }
893}