Skip to main content

base_markdown/
configuration.rs

1// Released under the MIT License.
2// Copyright, 2022, by Bernhard Berger.
3// Copyright, 2022-2025, by Titus Wormer.
4// Copyright, 2023, by cel.
5// Copyright, 2025, by Ophir Lojkine.
6// Copyright, 2026, by Samuel Williams.
7
8use crate::util::line_ending::LineEnding;
9use alloc::string::String;
10
11/// Control which constructs are enabled.
12///
13/// Not all constructs can be configured.
14/// Notably, blank lines and paragraphs cannot be turned off.
15///
16/// ## Examples
17///
18/// ```
19/// use base_markdown::Constructs;
20/// # fn main() {
21///
22/// // Use the default trait to get `CommonMark` constructs:
23/// let commonmark = Constructs::default();
24///
25/// // To turn on all of GFM, use the `gfm` method:
26/// let gfm = Constructs::gfm();
27///
28/// // Or, mix and match:
29/// let custom = Constructs {
30///   math_flow: true,
31///   math_text: true,
32///   ..Constructs::gfm()
33/// };
34/// # }
35/// ```
36#[allow(clippy::struct_excessive_bools)]
37#[derive(Clone, Debug, Eq, PartialEq)]
38#[cfg_attr(
39    feature = "serde",
40    derive(serde::Serialize, serde::Deserialize),
41    serde(rename_all = "camelCase")
42)]
43pub struct Constructs {
44    /// Attention.
45    ///
46    /// ```markdown
47    /// > | a *b* c **d**.
48    ///       ^^^   ^^^^^
49    /// ```
50    pub attention: bool,
51    /// Autolink.
52    ///
53    /// ```markdown
54    /// > | a <https://example.com> b <user@example.org>.
55    ///       ^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^
56    /// ```
57    pub autolink: bool,
58    /// Block quote.
59    ///
60    /// ```markdown
61    /// > | > a
62    ///     ^^^
63    /// ```
64    pub block_quote: bool,
65    /// Character escape.
66    ///
67    /// ```markdown
68    /// > | a \* b
69    ///       ^^
70    /// ```
71    pub character_escape: bool,
72    /// Character reference.
73    ///
74    /// ```markdown
75    /// > | a &amp; b
76    ///       ^^^^^
77    /// ```
78    pub character_reference: bool,
79    /// Code (indented).
80    ///
81    /// ```markdown
82    /// > |     a
83    ///     ^^^^^
84    /// ```
85    pub code_indented: bool,
86    /// Code (fenced).
87    ///
88    /// ```markdown
89    /// > | ~~~js
90    ///     ^^^^^
91    /// > | console.log(1)
92    ///     ^^^^^^^^^^^^^^
93    /// > | ~~~
94    ///     ^^^
95    /// ```
96    pub code_fenced: bool,
97    /// Code (text).
98    ///
99    /// ```markdown
100    /// > | a `b` c
101    ///       ^^^
102    /// ```
103    pub code_text: bool,
104    /// Definition.
105    ///
106    /// ```markdown
107    /// > | [a]: b "c"
108    ///     ^^^^^^^^^^
109    /// ```
110    pub definition: bool,
111    /// Frontmatter with an unindented opening fence on the first document line,
112    /// outside list items and blockquotes, and a matching closing fence.
113    ///
114    /// All forms produce a source-backed opaque node with [`NodeSyntax::Frontmatter`][crate::NodeSyntax::Frontmatter] metadata.
115    /// Untagged `---` and `+++` imply YAML and TOML. A format hint after either
116    /// delimiter, or a language-tagged backtick/tilde code fence, supplies an
117    /// explicit language. The parser preserves the raw body and authored info
118    /// string; HTML omits the frontmatter.
119    ///
120    /// ````markdown
121    /// > | ---
122    ///     ^^^
123    /// > | title: Neptune
124    ///     ^^^^^^^^^^^^^^
125    /// > | ---
126    ///     ^^^
127    /// ````
128    pub frontmatter: bool,
129    /// Autolink literal.
130    ///
131    /// ```markdown
132    /// > | https://example.com
133    ///     ^^^^^^^^^^^^^^^^^^^
134    /// ```
135    pub autolink_literal: bool,
136    /// Footnote definition.
137    ///
138    /// ```markdown
139    /// > | [^a]: b
140    ///     ^^^^^^^
141    /// ```
142    pub footnote_definition: bool,
143    /// Footnote label start.
144    ///
145    /// ```markdown
146    /// > | a[^b]
147    ///      ^^
148    /// ```
149    pub label_start_footnote: bool,
150    /// Strikethrough.
151    ///
152    /// ```markdown
153    /// > | a ~b~ c.
154    ///       ^^^
155    /// ```
156    pub strikethrough: bool,
157    /// Table.
158    ///
159    /// ```markdown
160    /// > | | a |
161    ///     ^^^^^
162    /// > | | - |
163    ///     ^^^^^
164    /// > | | b |
165    ///     ^^^^^
166    /// ```
167    pub table: bool,
168    /// Task list item.
169    ///
170    /// ```markdown
171    /// > | * [x] y.
172    ///       ^^^
173    /// ```
174    pub task_list_item: bool,
175    /// Hard break (escape).
176    ///
177    /// ```markdown
178    /// > | a\
179    ///      ^
180    ///   | b
181    /// ```
182    pub hard_break_escape: bool,
183    /// Hard break (trailing).
184    ///
185    /// ```markdown
186    /// > | a␠␠
187    ///      ^^
188    ///   | b
189    /// ```
190    pub hard_break_trailing: bool,
191    /// Heading (atx).
192    ///
193    /// ```markdown
194    /// > | # a
195    ///     ^^^
196    /// ```
197    pub heading_atx: bool,
198    /// Heading (setext).
199    ///
200    /// ```markdown
201    /// > | a
202    ///     ^^
203    /// > | ==
204    ///     ^^
205    /// ```
206    pub heading_setext: bool,
207    /// HTML (flow).
208    ///
209    /// ```markdown
210    /// > | <div>
211    ///     ^^^^^
212    /// ```
213    pub html_flow: bool,
214    /// HTML (text).
215    ///
216    /// ```markdown
217    /// > | a <b> c
218    ///       ^^^
219    /// ```
220    pub html_text: bool,
221    /// Label start (image).
222    ///
223    /// ```markdown
224    /// > | a ![b](c) d
225    ///       ^^
226    /// ```
227    pub label_start_image: bool,
228    /// Label start (link).
229    ///
230    /// ```markdown
231    /// > | a [b](c) d
232    ///       ^
233    /// ```
234    pub label_start_link: bool,
235    /// Label end.
236    ///
237    /// ```markdown
238    /// > | a [b](c) d
239    ///         ^^^^
240    /// ```
241    pub label_end: bool,
242    /// List items.
243    ///
244    /// ```markdown
245    /// > | * a
246    ///     ^^^
247    /// ```
248    pub list_item: bool,
249    /// Math (flow).
250    ///
251    /// ```markdown
252    /// > | $$
253    ///     ^^
254    /// > | \frac{1}{2}
255    ///     ^^^^^^^^^^^
256    /// > | $$
257    ///     ^^
258    /// ```
259    pub math_flow: bool,
260    /// Math (text).
261    ///
262    /// ```markdown
263    /// > | a $b$ c
264    ///       ^^^
265    /// ```
266    pub math_text: bool,
267
268    /// Thematic break.
269    ///
270    /// ```markdown
271    /// > | ***
272    ///     ^^^
273    /// ```
274    pub thematic_break: bool,
275}
276
277impl Default for Constructs {
278    /// `CommonMark`.
279    ///
280    /// `CommonMark` is a relatively strong specification of how markdown
281    /// works.
282    /// Most markdown parsers try to follow it.
283    ///
284    /// For more information, see the `CommonMark` specification:
285    /// <https://spec.commonmark.org>.
286    fn default() -> Self {
287        Self {
288            attention: true,
289            autolink: true,
290            block_quote: true,
291            character_escape: true,
292            character_reference: true,
293            code_indented: true,
294            code_fenced: true,
295            code_text: true,
296            definition: true,
297            frontmatter: false,
298            autolink_literal: false,
299            label_start_footnote: false,
300            footnote_definition: false,
301            strikethrough: false,
302            table: false,
303            task_list_item: false,
304            hard_break_escape: true,
305            hard_break_trailing: true,
306            heading_atx: true,
307            heading_setext: true,
308            html_flow: true,
309            html_text: true,
310            label_start_image: true,
311            label_start_link: true,
312            label_end: true,
313            list_item: true,
314            math_flow: false,
315            math_text: false,
316
317            thematic_break: true,
318        }
319    }
320}
321
322impl Constructs {
323    /// GFM.
324    ///
325    /// GFM stands for **GitHub flavored markdown**.
326    /// GFM extends `CommonMark` and adds support for autolink literals,
327    /// footnotes, strikethrough, tables, and tasklists.
328    ///
329    /// For more information, see the GFM specification:
330    /// <https://github.github.com/gfm/>.
331    pub fn gfm() -> Self {
332        Self {
333            autolink_literal: true,
334            footnote_definition: true,
335            label_start_footnote: true,
336            strikethrough: true,
337            table: true,
338            task_list_item: true,
339            ..Self::default()
340        }
341    }
342}
343
344/// Configuration that describes how to compile to HTML.
345///
346/// You likely either want to turn on the dangerous options
347/// (`allow_dangerous_html`, `allow_dangerous_protocol`) when dealing with
348/// input you trust, or want to customize how footnotes are compiled
349/// (typically because the input markdown is not in English).
350///
351/// ## Examples
352///
353/// ```
354/// use base_markdown::CompileOptions;
355/// # fn main() {
356///
357/// // Use the default trait to get safe defaults:
358/// let safe = CompileOptions::default();
359///
360/// // Live dangerously / trust the author:
361/// let danger = CompileOptions {
362///   allow_dangerous_html: true,
363///   allow_dangerous_protocol: true,
364///   ..CompileOptions::default()
365/// };
366///
367/// // In French:
368/// let enFrançais = CompileOptions {
369///   footnote_back_label: Some("Arrière".into()),
370///   footnote_label: Some("Notes de bas de page".into()),
371///   ..CompileOptions::default()
372/// };
373/// # }
374/// ```
375#[allow(clippy::struct_excessive_bools)]
376#[derive(Clone, Debug, Default)]
377#[cfg_attr(
378    feature = "serde",
379    derive(serde::Serialize, serde::Deserialize),
380    serde(default, rename_all = "camelCase")
381)]
382pub struct CompileOptions {
383    /// Whether to allow all values in images.
384    ///
385    /// The default is `false`,
386    /// which lets `allow_dangerous_protocol` control protocol safety for
387    /// both links and images.
388    ///
389    /// Pass `true` to allow all values as `src` on images,
390    /// regardless of `allow_dangerous_protocol`.
391    /// This is safe because the
392    /// [HTML specification][whatwg-html-image-processing]
393    /// does not allow executable code in images.
394    ///
395    /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
396    ///
397    /// ## Examples
398    ///
399    /// ```
400    /// use base_markdown::{to_html_with_options, CompileOptions, Options};
401    ///
402    /// // By default, some protocols in image sources are dropped:
403    /// assert_eq!(
404    ///     to_html_with_options(
405    ///         "![](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)",
406    ///         &Options::default()
407    ///     ),
408    ///     "<p><img src=\"\" alt=\"\" /></p>"
409    /// );
410    ///
411    /// // Turn `allow_any_img_src` on to allow all values as `src` on images.
412    /// // This is safe because browsers do not execute code in images.
413    /// assert_eq!(
414    ///     to_html_with_options(
415    ///         "![](javascript:alert(1))",
416    ///         &Options {
417    ///             compile: CompileOptions {
418    ///               allow_any_img_src: true,
419    ///               ..CompileOptions::default()
420    ///             },
421    ///             ..Options::default()
422    ///         }
423    ///     ),
424    ///     "<p><img src=\"javascript:alert(1)\" alt=\"\" /></p>"
425    /// );
426    /// ```
427    pub allow_any_img_src: bool,
428
429    /// Whether to allow (dangerous) HTML.
430    ///
431    /// The default is `false`, which still parses the HTML according to
432    /// `CommonMark` but shows the HTML as text instead of as elements.
433    ///
434    /// Pass `true` for trusted content to get actual HTML elements.
435    ///
436    /// When using GFM, make sure to also turn off `tagfilter`.
437    /// Otherwise, some dangerous HTML is still ignored.
438    ///
439    /// ## Examples
440    ///
441    /// ```
442    /// use base_markdown::{to_html, to_html_with_options, CompileOptions, Options};
443    ///
444    /// // `markdown-rs` is safe by default:
445    /// assert_eq!(
446    ///     to_html("Hi, <i>venus</i>!"),
447    ///     "<p>Hi, &lt;i&gt;venus&lt;/i&gt;!</p>"
448    /// );
449    ///
450    /// // Turn `allow_dangerous_html` on to allow potentially dangerous HTML:
451    /// assert_eq!(
452    ///     to_html_with_options(
453    ///         "Hi, <i>venus</i>!",
454    ///         &Options {
455    ///             compile: CompileOptions {
456    ///               allow_dangerous_html: true,
457    ///               ..CompileOptions::default()
458    ///             },
459    ///             ..Options::default()
460    ///         }
461    ///     ),
462    ///     "<p>Hi, <i>venus</i>!</p>"
463    /// );
464    /// ```
465    pub allow_dangerous_html: bool,
466
467    /// Whether to allow dangerous protocols in links and images.
468    ///
469    /// The default is `false`, which drops URLs in links and images that use
470    /// dangerous protocols.
471    ///
472    /// Pass `true` for trusted content to support all protocols.
473    ///
474    /// URLs that have no protocol (which means it’s relative to the current
475    /// page, such as `./some/page.html`) and URLs that have a safe protocol
476    /// (for images: `http`, `https`; for links: `http`, `https`, `irc`,
477    /// `ircs`, `mailto`, `xmpp`), are safe.
478    /// All other URLs are dangerous and dropped.
479    ///
480    /// When the option `allow_all_protocols_in_img` is enabled,
481    /// `allow_dangerous_protocol` only applies to links.
482    ///
483    /// This is safe because the
484    /// [HTML specification][whatwg-html-image-processing]
485    /// does not allow executable code in images.
486    /// All modern browsers respect this.
487    ///
488    /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
489    ///
490    /// ## Examples
491    ///
492    /// ```
493    /// use base_markdown::{to_html, to_html_with_options, CompileOptions, Options};
494    ///
495    /// // `markdown-rs` is safe by default:
496    /// assert_eq!(
497    ///     to_html("<javascript:alert(1)>"),
498    ///     "<p><a href=\"\">javascript:alert(1)</a></p>"
499    /// );
500    ///
501    /// // Turn `allow_dangerous_protocol` on to allow potentially dangerous protocols:
502    /// assert_eq!(
503    ///     to_html_with_options(
504    ///         "<javascript:alert(1)>",
505    ///         &Options {
506    ///             compile: CompileOptions {
507    ///               allow_dangerous_protocol: true,
508    ///               ..CompileOptions::default()
509    ///             },
510    ///             ..Options::default()
511    ///         }
512    ///     ),
513    ///     "<p><a href=\"javascript:alert(1)\">javascript:alert(1)</a></p>"
514    /// );
515    /// ```
516    pub allow_dangerous_protocol: bool,
517
518    // To do: `doc_markdown` is broken.
519    #[allow(clippy::doc_markdown)]
520    /// Default line ending to use when compiling to HTML, for line endings not
521    /// in `value`.
522    ///
523    /// Generally, `markdown-rs` copies line endings (`\r`, `\n`, `\r\n`) in
524    /// the markdown document over to the compiled HTML.
525    /// In some cases, such as `> a`, CommonMark requires that extra line
526    /// endings are added: `<blockquote>\n<p>a</p>\n</blockquote>`.
527    ///
528    /// To create that line ending, the document is checked for the first line
529    /// ending that is used.
530    /// If there is no line ending, `default_line_ending` is used.
531    /// If that isn’t configured, `\n` is used.
532    ///
533    /// ## Examples
534    ///
535    /// ```
536    /// use base_markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options};
537    ///
538    /// // `markdown-rs` uses `\n` by default:
539    /// assert_eq!(
540    ///     to_html("> a"),
541    ///     "<blockquote>\n<p>a</p>\n</blockquote>"
542    /// );
543    ///
544    /// // Define `default_line_ending` to configure the default:
545    /// assert_eq!(
546    ///     to_html_with_options(
547    ///         "> a",
548    ///         &Options {
549    ///             compile: CompileOptions {
550    ///               default_line_ending: LineEnding::CarriageReturnLineFeed,
551    ///               ..CompileOptions::default()
552    ///             },
553    ///             ..Options::default()
554    ///         }
555    ///     ),
556    ///     "<blockquote>\r\n<p>a</p>\r\n</blockquote>"
557    /// );
558    /// ```
559    pub default_line_ending: LineEnding,
560
561    /// Textual label to describe the backreference back to footnote calls.
562    ///
563    /// The default value is `"Back to content"`.
564    /// Change it when the markdown is not in English.
565    ///
566    /// This label is used in the `aria-label` attribute on each backreference
567    /// (the `↩` links).
568    /// It affects users of assistive technology.
569    ///
570    /// ## Examples
571    ///
572    /// ```
573    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
574    ///
575    /// // `"Back to content"` is used by default:
576    /// assert_eq!(
577    ///     to_html_with_options(
578    ///         "[^a]\n\n[^a]: b",
579    ///         &Options::gfm()
580    ///     ),
581    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
582    /// );
583    ///
584    /// // Pass `footnote_back_label` to use something else:
585    /// assert_eq!(
586    ///     to_html_with_options(
587    ///         "[^a]\n\n[^a]: b",
588    ///         &Options {
589    ///             parse: ParseOptions::gfm(),
590    ///             compile: CompileOptions {
591    ///               footnote_back_label: Some("Arrière".into()),
592    ///               ..CompileOptions::gfm()
593    ///             }
594    ///         }
595    ///     ),
596    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Arrière\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
597    /// );
598    /// ```
599    pub footnote_back_label: Option<String>,
600
601    /// Prefix to use before the `id` attribute on footnotes to prevent them
602    /// from *clobbering*.
603    ///
604    /// The default is `"user-content-"`.
605    /// Pass `Some("".into())` for trusted markdown and when you are careful
606    /// with polyfilling.
607    /// You could pass a different prefix.
608    ///
609    /// DOM clobbering is this:
610    ///
611    /// ```html
612    /// <p id="x"></p>
613    /// <script>alert(x) // `x` now refers to the `p#x` DOM element</script>
614    /// ```
615    ///
616    /// The above example shows that elements are made available by browsers,
617    /// by their ID, on the `window` object.
618    /// This is a security risk because you might be expecting some other
619    /// variable at that place.
620    /// It can also break polyfills.
621    /// Using a prefix solves these problems.
622    ///
623    /// ## Examples
624    ///
625    /// ```
626    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
627    ///
628    /// // `"user-content-"` is used by default:
629    /// assert_eq!(
630    ///     to_html_with_options(
631    ///         "[^a]\n\n[^a]: b",
632    ///         &Options::gfm()
633    ///     ),
634    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
635    /// );
636    ///
637    /// // Pass `footnote_clobber_prefix` to use something else:
638    /// assert_eq!(
639    ///     to_html_with_options(
640    ///         "[^a]\n\n[^a]: b",
641    ///         &Options {
642    ///             parse: ParseOptions::gfm(),
643    ///             compile: CompileOptions {
644    ///               footnote_clobber_prefix: Some("".into()),
645    ///               ..CompileOptions::gfm()
646    ///             }
647    ///         }
648    ///     ),
649    ///     "<p><sup><a href=\"#fn-a\" id=\"fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"fn-a\">\n<p>b <a href=\"#fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
650    /// );
651    /// ```
652    pub footnote_clobber_prefix: Option<String>,
653
654    /// Attributes to use on the footnote label.
655    ///
656    /// The default value is `"class=\"sr-only\""`.
657    /// Change it to show the label and add other attributes.
658    ///
659    /// This label is typically hidden visually (assuming a `sr-only` CSS class
660    /// is defined that does that), and thus affects screen readers only.
661    /// If you do have such a class, but want to show this section to everyone,
662    /// pass an empty string.
663    /// You can also add different attributes.
664    ///
665    /// > 👉 **Note**: `id="footnote-label"` is always added, because footnote
666    /// > calls use it with `aria-describedby` to provide an accessible label.
667    ///
668    /// ## Examples
669    ///
670    /// ```
671    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
672    ///
673    /// // `"class=\"sr-only\""` is used by default:
674    /// assert_eq!(
675    ///     to_html_with_options(
676    ///         "[^a]\n\n[^a]: b",
677    ///         &Options::gfm()
678    ///     ),
679    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
680    /// );
681    ///
682    /// // Pass `footnote_label_attributes` to use something else:
683    /// assert_eq!(
684    ///     to_html_with_options(
685    ///         "[^a]\n\n[^a]: b",
686    ///         &Options {
687    ///             parse: ParseOptions::gfm(),
688    ///             compile: CompileOptions {
689    ///               footnote_label_attributes: Some("class=\"footnote-heading\"".into()),
690    ///               ..CompileOptions::gfm()
691    ///             }
692    ///         }
693    ///     ),
694    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"footnote-heading\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
695    /// );
696    /// ```
697    pub footnote_label_attributes: Option<String>,
698
699    /// HTML tag name to use for the footnote label element.
700    ///
701    /// The default value is `"h2"`.
702    /// Change it to match your document structure.
703    ///
704    /// This label is typically hidden visually (assuming a `sr-only` CSS class
705    /// is defined that does that), and thus affects screen readers only.
706    /// If you do have such a class, but want to show this section to everyone,
707    /// pass different attributes with the `footnote_label_attributes`
708    /// option.
709    ///
710    /// ## Examples
711    ///
712    /// ```
713    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
714    ///
715    /// // `"h2"` is used by default:
716    /// assert_eq!(
717    ///     to_html_with_options(
718    ///         "[^a]\n\n[^a]: b",
719    ///         &Options::gfm()
720    ///     ),
721    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
722    /// );
723    ///
724    /// // Pass `footnote_label_tag_name` to use something else:
725    /// assert_eq!(
726    ///     to_html_with_options(
727    ///         "[^a]\n\n[^a]: b",
728    ///         &Options {
729    ///             parse: ParseOptions::gfm(),
730    ///             compile: CompileOptions {
731    ///               footnote_label_tag_name: Some("h1".into()),
732    ///               ..CompileOptions::gfm()
733    ///             }
734    ///         }
735    ///     ),
736    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h1 id=\"footnote-label\" class=\"sr-only\">Footnotes</h1>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
737    /// );
738    /// ```
739    pub footnote_label_tag_name: Option<String>,
740
741    /// Textual label to use for the footnotes section.
742    ///
743    /// The default value is `"Footnotes"`.
744    /// Change it when the markdown is not in English.
745    ///
746    /// This label is typically hidden visually (assuming a `sr-only` CSS class
747    /// is defined that does that), and thus affects screen readers only.
748    /// If you do have such a class, but want to show this section to everyone,
749    /// pass different attributes with the `footnote_label_attributes`
750    /// option.
751    ///
752    /// ## Examples
753    ///
754    /// ```
755    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
756    ///
757    /// // `"Footnotes"` is used by default:
758    /// assert_eq!(
759    ///     to_html_with_options(
760    ///         "[^a]\n\n[^a]: b",
761    ///         &Options::gfm()
762    ///     ),
763    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
764    /// );
765    ///
766    /// // Pass `footnote_label` to use something else:
767    /// assert_eq!(
768    ///     to_html_with_options(
769    ///         "[^a]\n\n[^a]: b",
770    ///         &Options {
771    ///             parse: ParseOptions::gfm(),
772    ///             compile: CompileOptions {
773    ///               footnote_label: Some("Notes de bas de page".into()),
774    ///               ..CompileOptions::gfm()
775    ///             }
776    ///         }
777    ///     ),
778    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Notes de bas de page</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
779    /// );
780    /// ```
781    pub footnote_label: Option<String>,
782
783    /// Whether or not task list HTML `<input>` items are enabled.
784    ///
785    /// This determines whether or not the user of the browser is able
786    /// to click and toggle generated checkbox items. The default is false.
787    ///
788    /// ## Examples
789    ///
790    /// ```
791    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
792    ///
793    /// // With `task_list_item_checkable`, generated `<input type="checkbox" />`
794    /// // tags do not contain the attribute `disabled=""` and are thus toggleable by
795    /// // browser users.
796    /// assert_eq!(
797    ///     to_html_with_options(
798    ///         "* [x] y.",
799    ///         &Options {
800    ///             parse: ParseOptions::gfm(),
801    ///             compile: CompileOptions {
802    ///                 task_list_item_checkable: true,
803    ///                 ..CompileOptions::gfm()
804    ///             }
805    ///         }
806    ///     ),
807    ///     "<ul>\n<li><input type=\"checkbox\" checked=\"\" /> y.</li>\n</ul>"
808    /// );
809    /// ```
810    pub task_list_item_checkable: bool,
811
812    /// Whether to filter disallowed raw HTML tags.
813    ///
814    /// This option does nothing if `allow_dangerous_html` is not turned on.
815    /// The default is `false`, which does not filter raw HTML tags.
816    /// Pass `true` for output that is a bit closer to GitHub’s actual output.
817    ///
818    /// The tagfilter is kinda weird and kinda useless.
819    /// The tag filter is a naïve attempt at XSS protection.
820    /// You should use a proper HTML sanitizing algorithm instead.
821    ///
822    /// ## Examples
823    ///
824    /// ```
825    /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
826    ///
827    /// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched:
828    /// assert_eq!(
829    ///     to_html_with_options(
830    ///         "<iframe>",
831    ///         &Options {
832    ///             parse: ParseOptions::gfm(),
833    ///             compile: CompileOptions {
834    ///               allow_dangerous_html: true,
835    ///               ..CompileOptions::default()
836    ///             }
837    ///         }
838    ///     ),
839    ///     "<iframe>"
840    /// );
841    ///
842    /// // Pass `tagfilter: true` to make some of that safe:
843    /// assert_eq!(
844    ///     to_html_with_options(
845    ///         "<iframe>",
846    ///         &Options {
847    ///             parse: ParseOptions::gfm(),
848    ///             compile: CompileOptions {
849    ///               allow_dangerous_html: true,
850    ///               tagfilter: true,
851    ///               ..CompileOptions::default()
852    ///             }
853    ///         }
854    ///     ),
855    ///     "&lt;iframe>"
856    /// );
857    /// ```
858    ///
859    /// ## References
860    ///
861    /// * [*§ 6.1 Disallowed Raw HTML (extension)* in GFM](https://github.github.com/gfm/#disallowed-raw-html-extension-)
862    /// * [`cmark-gfm#extensions/tagfilter.c`](https://github.com/github/cmark-gfm/blob/master/extensions/tagfilter.c)
863    pub tagfilter: bool,
864
865    /// Whether to add unique `id` attributes to headings for in-page links
866    /// and tables of contents.
867    ///
868    /// IDs use the same text normalization and duplicate suffixes as
869    /// [`Document::headings`][crate::Document::headings].
870    ///
871    /// The default is `false`.
872    pub heading_ids: bool,
873}
874
875impl CompileOptions {
876    /// GFM.
877    ///
878    /// GFM stands for **GitHub flavored markdown**.
879    /// On the compilation side, the GFM preset turns on the tagfilter.
880    /// The tagfilter is useless, but it’s included here for consistency, and
881    /// this method exists for parity to parse options.
882    ///
883    /// For more information, see the GFM specification:
884    /// <https://github.github.com/gfm/>.
885    pub fn gfm() -> Self {
886        Self {
887            tagfilter: true,
888            ..Self::default()
889        }
890    }
891}
892
893/// Configuration that describes how to parse from markdown.
894///
895/// You can use this:
896///
897/// * To control what markdown constructs are turned on and off
898/// * To control some of those constructs
899///
900/// In most cases, you will want to use the default trait or `gfm` method.
901///
902/// ## Examples
903///
904/// ```
905/// use base_markdown::ParseOptions;
906/// # fn main() {
907///
908/// // Use the default trait to parse markdown according to `CommonMark`:
909/// let commonmark = ParseOptions::default();
910///
911/// // Use the `gfm` method to parse markdown according to GFM:
912/// let gfm = ParseOptions::gfm();
913/// # }
914/// ```
915#[allow(clippy::struct_excessive_bools)]
916#[cfg_attr(
917    feature = "serde",
918    derive(serde::Serialize, serde::Deserialize),
919    serde(default, rename_all = "camelCase")
920)]
921#[derive(Debug)]
922pub struct ParseOptions {
923    /// Which constructs to enable and disable.
924    ///
925    /// The default is to follow `CommonMark`.
926    ///
927    /// ## Examples
928    ///
929    /// ```
930    /// use base_markdown::{to_html, to_html_with_options, Constructs, Options, ParseOptions};
931    ///
932    /// // `markdown-rs` follows CommonMark by default:
933    /// assert_eq!(
934    ///     to_html("    indented code?"),
935    ///     "<pre><code>indented code?\n</code></pre>"
936    /// );
937    ///
938    /// // Pass `constructs` to choose what to enable and disable:
939    /// assert_eq!(
940    ///     to_html_with_options(
941    ///         "    indented code?",
942    ///         &Options {
943    ///             parse: ParseOptions {
944    ///               constructs: Constructs {
945    ///                 code_indented: false,
946    ///                 ..Constructs::default()
947    ///               },
948    ///               ..ParseOptions::default()
949    ///             },
950    ///             ..Options::default()
951    ///         }
952    ///     ),
953    ///     "<p>indented code?</p>"
954    /// );
955    /// ```
956    #[cfg_attr(feature = "serde", serde(default))]
957    pub constructs: Constructs,
958
959    /// Whether to parse a language prefix, such as `ruby:`, before inline code.
960    ///
961    /// When enabled, the prefix is removed from the surrounding text and
962    /// stored as the inline code node's `lang` value. The default is `false`.
963    #[cfg_attr(feature = "serde", serde(default))]
964    pub inline_code_info: bool,
965
966    /// Whether indented type 6 and type 7 HTML blocks continue across blank
967    /// lines when later content keeps a consistent indentation.
968    ///
969    /// The default is `false`, which follows `CommonMark`'s blank-line rule.
970    #[cfg_attr(feature = "serde", serde(default))]
971    pub html_block_blank_lines: bool,
972
973    /// Whether `:` is allowed in HTML tag names for namespace prefixes such
974    /// as `svg:circle`.
975    ///
976    /// The default is `false` to preserve `CommonMark` parsing of text such as
977    /// `<m:abc>`.
978    #[cfg_attr(feature = "serde", serde(default))]
979    pub html_tag_namespaces: bool,
980
981    /// Whether to support strikethrough with a single tilde
982    ///
983    /// This option does nothing if `strikethrough` is not turned on in
984    /// `constructs`.
985    /// This option does not affect strikethrough with double tildes.
986    ///
987    /// The default is `true`, which follows how markdown on `github.com`
988    /// works, as strikethrough with single tildes is supported.
989    /// Pass `false`, to follow the GFM spec more strictly, by not allowing
990    /// strikethrough with single tildes.
991    ///
992    /// ## Examples
993    ///
994    /// ```
995    /// use base_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
996    ///
997    /// // `markdown-rs` supports single tildes by default:
998    /// assert_eq!(
999    ///     to_html_with_options(
1000    ///         "~a~",
1001    ///         &Options {
1002    ///             parse: ParseOptions {
1003    ///               constructs: Constructs::gfm(),
1004    ///               ..ParseOptions::default()
1005    ///             },
1006    ///             ..Options::default()
1007    ///         }
1008    ///     ),
1009    ///     "<p><del>a</del></p>"
1010    /// );
1011    ///
1012    /// // Pass `strikethrough_single_tilde: false` to turn that off:
1013    /// assert_eq!(
1014    ///     to_html_with_options(
1015    ///         "~a~",
1016    ///         &Options {
1017    ///             parse: ParseOptions {
1018    ///               constructs: Constructs::gfm(),
1019    ///               strikethrough_single_tilde: false,
1020    ///               ..ParseOptions::default()
1021    ///             },
1022    ///             ..Options::default()
1023    ///         }
1024    ///     ),
1025    ///     "<p>~a~</p>"
1026    /// );
1027    /// ```
1028    #[cfg_attr(feature = "serde", serde(default))]
1029    pub strikethrough_single_tilde: bool,
1030
1031    /// Whether to support math (text) with a single dollar
1032    ///
1033    /// This option does nothing if `math_text` is not turned on in
1034    /// `constructs`.
1035    /// This option does not affect math (text) with two or more dollars.
1036    ///
1037    /// The default is `true`, which is more close to how code (text) and
1038    /// Pandoc work, as it allows math with a single dollar to form.
1039    /// However, single dollars can interfere with “normal” dollars in text.
1040    /// Pass `false`, to only allow math (text) to form when two or more
1041    /// dollars are used.
1042    /// If you pass `false`, you can still use two or more dollars for text
1043    /// math.
1044    ///
1045    /// ## Examples
1046    ///
1047    /// ```
1048    /// use base_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1049    ///
1050    /// // `markdown-rs` supports single dollars by default:
1051    /// assert_eq!(
1052    ///     to_html_with_options(
1053    ///         "$a$",
1054    ///         &Options {
1055    ///             parse: ParseOptions {
1056    ///               constructs: Constructs {
1057    ///                 math_text: true,
1058    ///                 ..Constructs::default()
1059    ///               },
1060    ///               ..ParseOptions::default()
1061    ///             },
1062    ///             ..Options::default()
1063    ///         }
1064    ///     ),
1065    ///     "<p><code class=\"language-math math-inline\">a</code></p>"
1066    /// );
1067    ///
1068    /// // Pass `math_text_single_dollar: false` to turn that off:
1069    /// assert_eq!(
1070    ///     to_html_with_options(
1071    ///         "$a$",
1072    ///         &Options {
1073    ///             parse: ParseOptions {
1074    ///               constructs: Constructs {
1075    ///                 math_text: true,
1076    ///                 ..Constructs::default()
1077    ///               },
1078    ///               math_text_single_dollar: false,
1079    ///               ..ParseOptions::default()
1080    ///             },
1081    ///             ..Options::default()
1082    ///         }
1083    ///     ),
1084    ///     "<p>$a$</p>"
1085    /// );
1086    /// ```
1087    #[cfg_attr(feature = "serde", serde(default))]
1088    pub math_text_single_dollar: bool,
1089}
1090
1091impl Default for ParseOptions {
1092    /// `CommonMark` defaults.
1093    fn default() -> Self {
1094        Self {
1095            constructs: Constructs::default(),
1096            inline_code_info: false,
1097            html_block_blank_lines: false,
1098            html_tag_namespaces: false,
1099            strikethrough_single_tilde: true,
1100            math_text_single_dollar: true,
1101        }
1102    }
1103}
1104
1105impl ParseOptions {
1106    /// GFM.
1107    ///
1108    /// GFM stands for GitHub flavored markdown.
1109    /// GFM extends `CommonMark` and adds support for autolink literals,
1110    /// footnotes, strikethrough, tables, and tasklists.
1111    ///
1112    /// For more information, see the GFM specification:
1113    /// <https://github.github.com/gfm/>
1114    pub fn gfm() -> Self {
1115        Self {
1116            constructs: Constructs::gfm(),
1117            ..Self::default()
1118        }
1119    }
1120}
1121
1122/// Configuration that describes how to parse from markdown and compile to
1123/// HTML.
1124///
1125/// In most cases, you will want to use the default trait or `gfm` method.
1126///
1127/// ## Examples
1128///
1129/// ```
1130/// use base_markdown::Options;
1131/// # fn main() {
1132///
1133/// // Use the default trait to compile markdown to HTML according to `CommonMark`:
1134/// let commonmark = Options::default();
1135///
1136/// // Use the `gfm` method to compile markdown to HTML according to GFM:
1137/// let gfm = Options::gfm();
1138/// # }
1139/// ```
1140#[allow(clippy::struct_excessive_bools)]
1141#[derive(Debug, Default)]
1142#[cfg_attr(
1143    feature = "serde",
1144    derive(serde::Serialize, serde::Deserialize),
1145    serde(default)
1146)]
1147pub struct Options {
1148    /// Configuration that describes how to parse from markdown.
1149    pub parse: ParseOptions,
1150    /// Configuration that describes how to compile to HTML.
1151    pub compile: CompileOptions,
1152}
1153
1154impl Options {
1155    /// GFM.
1156    ///
1157    /// GFM stands for GitHub flavored markdown.
1158    /// GFM extends `CommonMark` and adds support for autolink literals,
1159    /// footnotes, strikethrough, tables, and tasklists.
1160    /// On the compilation side, the GFM preset turns on the tagfilter.
1161    /// The tagfilter is useless, but it’s included here for consistency.
1162    ///
1163    /// For more information, see the GFM specification:
1164    /// <https://github.github.com/gfm/>
1165    pub fn gfm() -> Self {
1166        Self {
1167            parse: ParseOptions::gfm(),
1168            compile: CompileOptions::gfm(),
1169        }
1170    }
1171}
1172
1173#[cfg(test)]
1174#[path = "configuration/tests.rs"]
1175mod tests;