pulldown_cmark/
lib.rs

1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Pull parser for [CommonMark](https://commonmark.org). This crate provides a [Parser](struct.Parser.html) struct
22//! which is an iterator over [Event](enum.Event.html)s. This iterator can be used
23//! directly, or to output HTML using the [HTML module](html/index.html).
24//!
25//! By default, only CommonMark features are enabled. To use extensions like tables,
26//! footnotes or task lists, enable them by setting the corresponding flags in the
27//! [Options](struct.Options.html) struct.
28//!
29//! # Example
30//! ```rust
31//! use pulldown_cmark::{Parser, Options};
32//!
33//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
34//!
35//! // Set up options and parser. Strikethroughs are not part of the CommonMark standard
36//! // and we therefore must enable it explicitly.
37//! let mut options = Options::empty();
38//! options.insert(Options::ENABLE_STRIKETHROUGH);
39//! let parser = Parser::new_ext(markdown_input, options);
40//!
41//! # #[cfg(feature = "html")] {
42//! // Write to String buffer.
43//! let mut html_output = String::new();
44//! pulldown_cmark::html::push_html(&mut html_output, parser);
45//!
46//! // Check that the output is what we expected.
47//! let expected_html = "<p>Hello world, this is a <del>complicated</del> <em>very simple</em> example.</p>\n";
48//! assert_eq!(expected_html, &html_output);
49//! # }
50//! ```
51//!
52//! Note that consecutive text events can happen due to the manner in which the
53//! parser evaluates the source. A utility `TextMergeStream` exists to improve
54//! the comfort of iterating the events:
55//!
56//! ```rust
57//! use pulldown_cmark::{Event, Parser, TextMergeStream};
58//!
59//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
60//!
61//! let iterator = TextMergeStream::new(Parser::new(markdown_input));
62//!
63//! for event in iterator {
64//!     match event {
65//!         Event::Text(text) => println!("{}", text),
66//!         _ => {}
67//!     }
68//! }
69//! ```
70//!
71
72// When compiled for the rustc compiler itself we want to make sure that this is
73// an unstable crate.
74#![cfg_attr(rustbuild, feature(staged_api, rustc_private))]
75#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))]
76// Forbid unsafe code unless the SIMD feature is enabled.
77#![cfg_attr(not(feature = "simd"), forbid(unsafe_code))]
78#![warn(missing_debug_implementations)]
79
80#[cfg(feature = "serde")]
81use serde::{Deserialize, Serialize};
82
83#[cfg(feature = "html")]
84pub mod html;
85
86pub mod utils;
87
88mod entities;
89mod firstpass;
90mod linklabel;
91mod parse;
92mod puncttable;
93mod scanners;
94mod strings;
95mod tree;
96
97use std::fmt::Display;
98
99pub use crate::parse::{
100    BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback, OffsetIter, Parser, RefDefs,
101};
102pub use crate::strings::{CowStr, InlineStr};
103pub use crate::utils::*;
104
105/// Codeblock kind.
106#[derive(Clone, Debug, PartialEq)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108pub enum CodeBlockKind<'a> {
109    Indented,
110    /// The value contained in the tag describes the language of the code, which may be empty.
111    #[cfg_attr(feature = "serde", serde(borrow))]
112    Fenced(CowStr<'a>),
113}
114
115impl<'a> CodeBlockKind<'a> {
116    pub fn is_indented(&self) -> bool {
117        matches!(*self, CodeBlockKind::Indented)
118    }
119
120    pub fn is_fenced(&self) -> bool {
121        matches!(*self, CodeBlockKind::Fenced(_))
122    }
123
124    pub fn into_static(self) -> CodeBlockKind<'static> {
125        match self {
126            CodeBlockKind::Indented => CodeBlockKind::Indented,
127            CodeBlockKind::Fenced(s) => CodeBlockKind::Fenced(s.into_static()),
128        }
129    }
130}
131
132/// BlockQuote kind (Note, Tip, Important, Warning, Caution).
133#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub enum BlockQuoteKind {
136    Note,
137    Tip,
138    Important,
139    Warning,
140    Caution,
141}
142
143/// Metadata block kind.
144#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
145#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
146pub enum MetadataBlockKind {
147    YamlStyle,
148    PlusesStyle,
149}
150
151/// Tags for elements that can contain other elements.
152#[derive(Clone, Debug, PartialEq)]
153#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154pub enum Tag<'a> {
155    /// A paragraph of text and other inline elements.
156    Paragraph,
157
158    /// A heading, with optional identifier, classes and custom attributes.
159    /// The identifier is prefixed with `#` and the last one in the attributes
160    /// list is chosen, classes are prefixed with `.` and custom attributes
161    /// have no prefix and can optionally have a value (`myattr` or `myattr=myvalue`).
162    ///
163    /// `id`, `classes` and `attrs` are only parsed and populated with [`Options::ENABLE_HEADING_ATTRIBUTES`], `None` or empty otherwise.
164    Heading {
165        level: HeadingLevel,
166        id: Option<CowStr<'a>>,
167        classes: Vec<CowStr<'a>>,
168        /// The first item of the tuple is the attr and second one the value.
169        attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
170    },
171
172    /// A block quote.
173    ///
174    /// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_GFM`], `None` otherwise.
175    ///
176    /// ```markdown
177    /// > regular quote
178    ///
179    /// > [!NOTE]
180    /// > note quote
181    /// ```
182    BlockQuote(Option<BlockQuoteKind>),
183    /// A code block.
184    CodeBlock(CodeBlockKind<'a>),
185
186    /// An HTML block.
187    ///
188    /// A line that begins with some predefined tags (HTML block tags) (see [CommonMark Spec](https://spec.commonmark.org/0.31.2/#html-blocks) for more details) or any tag that is followed only by whitespace.
189    ///
190    /// Most HTML blocks end on an empty line, though some e.g. `<pre>` like `<script>` or `<!-- Comments -->` don't.
191    /// ```markdown
192    /// <body> Is HTML block even though here is non-whitespace.
193    /// Block ends on an empty line.
194    ///
195    /// <some-random-tag>
196    /// This is HTML block.
197    ///
198    /// <pre> Doesn't end on empty lines.
199    ///
200    /// This is still the same block.</pre>
201    /// ```
202    HtmlBlock,
203
204    /// A list. If the list is ordered the field indicates the number of the first item.
205    /// Contains only list items.
206    List(Option<u64>), // TODO: add delim and tight for ast (not needed for html)
207    /// A list item.
208    Item,
209    /// A footnote definition. The value contained is the footnote's label by which it can
210    /// be referred to.
211    ///
212    /// Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`] or [`Options::ENABLE_OLD_FOOTNOTES`].
213    #[cfg_attr(feature = "serde", serde(borrow))]
214    FootnoteDefinition(CowStr<'a>),
215
216    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
217    DefinitionList,
218    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
219    DefinitionListTitle,
220    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
221    DefinitionListDefinition,
222
223    /// A table. Contains a vector describing the text-alignment for each of its columns.
224    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
225    Table(Vec<Alignment>),
226    /// A table header. Contains only `TableCell`s. Note that the table body starts immediately
227    /// after the closure of the `TableHead` tag. There is no `TableBody` tag.
228    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
229    TableHead,
230    /// A table row. Is used both for header rows as body rows. Contains only `TableCell`s.
231    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
232    TableRow,
233    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
234    TableCell,
235
236    // span-level tags
237    /// [Emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
238    /// ```markdown
239    /// half*emph* _strong_ _multi _level__
240    /// ```
241    Emphasis,
242    /// [Strong emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
243    /// ```markdown
244    /// half**strong** __strong__ __multi __level____
245    /// ```
246    Strong,
247    /// Only parsed and emitted with [`Options::ENABLE_STRIKETHROUGH`].
248    ///
249    /// ```markdown
250    /// ~strike through~
251    /// ```
252    Strikethrough,
253    /// Only parsed and emitted with [`Options::ENABLE_SUPERSCRIPT`].
254    ///
255    /// ```markdown
256    /// ^superscript^
257    /// ```
258    Superscript,
259    /// Only parsed and emitted with [`Options::ENABLE_SUBSCRIPT`], if disabled `~something~` is parsed as [`Strikethrough`](Self::Strikethrough).
260    /// ```markdown
261    /// ~subscript~ ~~if also enabled this is strikethrough~~
262    /// ```
263    Subscript,
264
265    /// A link.
266    Link {
267        link_type: LinkType,
268        dest_url: CowStr<'a>,
269        title: CowStr<'a>,
270        /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
271        id: CowStr<'a>,
272    },
273
274    /// An image. The first field is the link type, the second the destination URL and the third is a title,
275    /// the fourth is the link identifier.
276    Image {
277        link_type: LinkType,
278        dest_url: CowStr<'a>,
279        title: CowStr<'a>,
280        /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
281        id: CowStr<'a>,
282    },
283
284    /// A metadata block.
285    /// Only parsed and emitted with [`Options::ENABLE_YAML_STYLE_METADATA_BLOCKS`]
286    /// or [`Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS`].
287    MetadataBlock(MetadataBlockKind),
288}
289
290impl<'a> Tag<'a> {
291    pub fn to_end(&self) -> TagEnd {
292        match self {
293            Tag::Paragraph => TagEnd::Paragraph,
294            Tag::Heading { level, .. } => TagEnd::Heading(*level),
295            Tag::BlockQuote(kind) => TagEnd::BlockQuote(*kind),
296            Tag::CodeBlock(_) => TagEnd::CodeBlock,
297            Tag::HtmlBlock => TagEnd::HtmlBlock,
298            Tag::List(number) => TagEnd::List(number.is_some()),
299            Tag::Item => TagEnd::Item,
300            Tag::FootnoteDefinition(_) => TagEnd::FootnoteDefinition,
301            Tag::Table(_) => TagEnd::Table,
302            Tag::TableHead => TagEnd::TableHead,
303            Tag::TableRow => TagEnd::TableRow,
304            Tag::TableCell => TagEnd::TableCell,
305            Tag::Subscript => TagEnd::Subscript,
306            Tag::Superscript => TagEnd::Superscript,
307            Tag::Emphasis => TagEnd::Emphasis,
308            Tag::Strong => TagEnd::Strong,
309            Tag::Strikethrough => TagEnd::Strikethrough,
310            Tag::Link { .. } => TagEnd::Link,
311            Tag::Image { .. } => TagEnd::Image,
312            Tag::MetadataBlock(kind) => TagEnd::MetadataBlock(*kind),
313            Tag::DefinitionList => TagEnd::DefinitionList,
314            Tag::DefinitionListTitle => TagEnd::DefinitionListTitle,
315            Tag::DefinitionListDefinition => TagEnd::DefinitionListDefinition,
316        }
317    }
318
319    pub fn into_static(self) -> Tag<'static> {
320        match self {
321            Tag::Paragraph => Tag::Paragraph,
322            Tag::Heading {
323                level,
324                id,
325                classes,
326                attrs,
327            } => Tag::Heading {
328                level,
329                id: id.map(|s| s.into_static()),
330                classes: classes.into_iter().map(|s| s.into_static()).collect(),
331                attrs: attrs
332                    .into_iter()
333                    .map(|(k, v)| (k.into_static(), v.map(|s| s.into_static())))
334                    .collect(),
335            },
336            Tag::BlockQuote(k) => Tag::BlockQuote(k),
337            Tag::CodeBlock(kb) => Tag::CodeBlock(kb.into_static()),
338            Tag::HtmlBlock => Tag::HtmlBlock,
339            Tag::List(v) => Tag::List(v),
340            Tag::Item => Tag::Item,
341            Tag::FootnoteDefinition(a) => Tag::FootnoteDefinition(a.into_static()),
342            Tag::Table(v) => Tag::Table(v),
343            Tag::TableHead => Tag::TableHead,
344            Tag::TableRow => Tag::TableRow,
345            Tag::TableCell => Tag::TableCell,
346            Tag::Emphasis => Tag::Emphasis,
347            Tag::Strong => Tag::Strong,
348            Tag::Strikethrough => Tag::Strikethrough,
349            Tag::Superscript => Tag::Superscript,
350            Tag::Subscript => Tag::Subscript,
351            Tag::Link {
352                link_type,
353                dest_url,
354                title,
355                id,
356            } => Tag::Link {
357                link_type,
358                dest_url: dest_url.into_static(),
359                title: title.into_static(),
360                id: id.into_static(),
361            },
362            Tag::Image {
363                link_type,
364                dest_url,
365                title,
366                id,
367            } => Tag::Image {
368                link_type,
369                dest_url: dest_url.into_static(),
370                title: title.into_static(),
371                id: id.into_static(),
372            },
373            Tag::MetadataBlock(v) => Tag::MetadataBlock(v),
374            Tag::DefinitionList => Tag::DefinitionList,
375            Tag::DefinitionListTitle => Tag::DefinitionListTitle,
376            Tag::DefinitionListDefinition => Tag::DefinitionListDefinition,
377        }
378    }
379}
380
381/// The end of a `Tag`.
382#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
383#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
384pub enum TagEnd {
385    Paragraph,
386    Heading(HeadingLevel),
387
388    BlockQuote(Option<BlockQuoteKind>),
389    CodeBlock,
390
391    HtmlBlock,
392
393    /// A list, `true` for ordered lists.
394    List(bool),
395    Item,
396    FootnoteDefinition,
397
398    DefinitionList,
399    DefinitionListTitle,
400    DefinitionListDefinition,
401
402    Table,
403    TableHead,
404    TableRow,
405    TableCell,
406
407    Emphasis,
408    Strong,
409    Strikethrough,
410    Superscript,
411    Subscript,
412
413    Link,
414    Image,
415
416    MetadataBlock(MetadataBlockKind),
417}
418
419/// Make sure `TagEnd` is no more than two bytes in size.
420/// This is why it's used instead of just using `Tag`.
421#[cfg(target_pointer_width = "64")]
422const _STATIC_ASSERT_TAG_END_SIZE: [(); 2] = [(); std::mem::size_of::<TagEnd>()];
423
424impl<'a> From<Tag<'a>> for TagEnd {
425    fn from(value: Tag) -> Self {
426        value.to_end()
427    }
428}
429
430#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
431#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
432pub enum HeadingLevel {
433    H1 = 1,
434    H2,
435    H3,
436    H4,
437    H5,
438    H6,
439}
440
441impl Display for HeadingLevel {
442    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443        match self {
444            Self::H1 => write!(f, "h1"),
445            Self::H2 => write!(f, "h2"),
446            Self::H3 => write!(f, "h3"),
447            Self::H4 => write!(f, "h4"),
448            Self::H5 => write!(f, "h5"),
449            Self::H6 => write!(f, "h6"),
450        }
451    }
452}
453
454/// Returned when trying to convert a `usize` into a `Heading` but it fails
455/// because the usize isn't a valid heading level
456#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
457pub struct InvalidHeadingLevel(usize);
458
459impl TryFrom<usize> for HeadingLevel {
460    type Error = InvalidHeadingLevel;
461
462    fn try_from(value: usize) -> Result<Self, Self::Error> {
463        match value {
464            1 => Ok(Self::H1),
465            2 => Ok(Self::H2),
466            3 => Ok(Self::H3),
467            4 => Ok(Self::H4),
468            5 => Ok(Self::H5),
469            6 => Ok(Self::H6),
470            _ => Err(InvalidHeadingLevel(value)),
471        }
472    }
473}
474
475/// Type specifier for inline links. See [the Tag::Link](enum.Tag.html#variant.Link) for more information.
476#[derive(Clone, Debug, PartialEq, Copy)]
477#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
478pub enum LinkType {
479    /// Inline link like `[foo](bar)`
480    Inline,
481    /// Reference link like `[foo][bar]`
482    Reference,
483    /// Reference without destination in the document, but resolved by the broken_link_callback
484    ReferenceUnknown,
485    /// Collapsed link like `[foo][]`
486    Collapsed,
487    /// Collapsed link without destination in the document, but resolved by the broken_link_callback
488    CollapsedUnknown,
489    /// Shortcut link like `[foo]`
490    Shortcut,
491    /// Shortcut without destination in the document, but resolved by the broken_link_callback
492    ShortcutUnknown,
493    /// Autolink like `<http://foo.bar/baz>`
494    Autolink,
495    /// Email address in autolink like `<john@example.org>`
496    Email,
497    /// Wikilink link like `[[foo]]` or `[[foo|bar]]`
498    WikiLink {
499        /// `true` if the wikilink was piped.
500        ///
501        /// * `true` - `[[foo|bar]]`
502        /// * `false` - `[[foo]]`
503        has_pothole: bool,
504    },
505}
506
507impl LinkType {
508    /// Map the link type to an equivalent _Unknown link type.
509    fn to_unknown(self) -> Self {
510        match self {
511            LinkType::Reference => LinkType::ReferenceUnknown,
512            LinkType::Collapsed => LinkType::CollapsedUnknown,
513            LinkType::Shortcut => LinkType::ShortcutUnknown,
514            _ => unreachable!(),
515        }
516    }
517}
518
519/// Markdown events that are generated in a preorder traversal of the document
520/// tree, with additional `End` events whenever all of an inner node's children
521/// have been visited.
522#[derive(Clone, Debug, PartialEq)]
523#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
524pub enum Event<'a> {
525    /// Start of a tagged element. Events that are yielded after this event
526    /// and before its corresponding `End` event are inside this element.
527    /// Start and end events are guaranteed to be balanced.
528    #[cfg_attr(feature = "serde", serde(borrow))]
529    Start(Tag<'a>),
530    /// End of a tagged element.
531    End(TagEnd),
532    /// A text node.
533    ///
534    /// All text, outside and inside [`Tag`]s.
535    #[cfg_attr(feature = "serde", serde(borrow))]
536    Text(CowStr<'a>),
537    /// An [inline code node](https://spec.commonmark.org/0.31.2/#code-spans).
538    ///
539    /// ```markdown
540    /// `code`
541    /// ```
542    #[cfg_attr(feature = "serde", serde(borrow))]
543    Code(CowStr<'a>),
544    /// An inline math environment node.
545    /// Requires [`Options::ENABLE_MATH`].
546    ///
547    /// ```markdown
548    /// $math$
549    /// ```
550    #[cfg_attr(feature = "serde", serde(borrow))]
551    InlineMath(CowStr<'a>),
552    /// A display math environment node.
553    /// Requires [`Options::ENABLE_MATH`].
554    ///
555    /// ```markdown
556    /// $$math$$
557    /// ```
558    #[cfg_attr(feature = "serde", serde(borrow))]
559    DisplayMath(CowStr<'a>),
560    /// An HTML node.
561    ///
562    /// A line of HTML inside [`Tag::HtmlBlock`] includes the line break.
563    #[cfg_attr(feature = "serde", serde(borrow))]
564    Html(CowStr<'a>),
565    /// An [inline HTML node](https://spec.commonmark.org/0.31.2/#raw-html).
566    ///
567    /// Contains only the tag itself, e.g. `<open-tag>`, `</close-tag>` or `<!-- comment -->`.
568    ///
569    /// **Note**: Under some conditions HTML can also be parsed as an HTML Block, see [`Tag::HtmlBlock`] for details.
570    #[cfg_attr(feature = "serde", serde(borrow))]
571    InlineHtml(CowStr<'a>),
572    /// A reference to a footnote with given label, which may or may not be defined
573    /// by an event with a [`Tag::FootnoteDefinition`] tag. Definitions and references to them may
574    /// occur in any order. Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`] or [`Options::ENABLE_OLD_FOOTNOTES`].
575    ///
576    /// ```markdown
577    /// [^1]
578    /// ```
579    #[cfg_attr(feature = "serde", serde(borrow))]
580    FootnoteReference(CowStr<'a>),
581    /// A [soft line break](https://spec.commonmark.org/0.31.2/#soft-line-breaks).
582    ///
583    /// Any line break that isn't a [`HardBreak`](Self::HardBreak), or the end of e.g. a paragraph.
584    SoftBreak,
585    /// A [hard line break](https://spec.commonmark.org/0.31.2/#hard-line-breaks).
586    ///
587    /// A line ending that is either preceded by at least two spaces or `\`.
588    ///
589    /// ```markdown
590    /// hard··
591    /// line\
592    /// breaks
593    /// ```
594    /// *`·` is a space*
595    HardBreak,
596    /// A horizontal ruler.
597    ///
598    /// ```markdown
599    /// ***
600    /// ···---
601    /// _·_··_····_··
602    /// ```
603    /// *`·` is any whitespace*
604    Rule,
605    /// A task list marker, rendered as a checkbox in HTML. Contains a true when it is checked.
606    /// Only parsed and emitted with [`Options::ENABLE_TASKLISTS`].
607    /// ```markdown
608    /// - [ ] unchecked
609    /// - [x] checked
610    /// ```
611    TaskListMarker(bool),
612}
613
614impl<'a> Event<'a> {
615    pub fn into_static(self) -> Event<'static> {
616        match self {
617            Event::Start(t) => Event::Start(t.into_static()),
618            Event::End(e) => Event::End(e),
619            Event::Text(s) => Event::Text(s.into_static()),
620            Event::Code(s) => Event::Code(s.into_static()),
621            Event::InlineMath(s) => Event::InlineMath(s.into_static()),
622            Event::DisplayMath(s) => Event::DisplayMath(s.into_static()),
623            Event::Html(s) => Event::Html(s.into_static()),
624            Event::InlineHtml(s) => Event::InlineHtml(s.into_static()),
625            Event::FootnoteReference(s) => Event::FootnoteReference(s.into_static()),
626            Event::SoftBreak => Event::SoftBreak,
627            Event::HardBreak => Event::HardBreak,
628            Event::Rule => Event::Rule,
629            Event::TaskListMarker(b) => Event::TaskListMarker(b),
630        }
631    }
632}
633
634/// Table column text alignment.
635#[derive(Copy, Clone, Debug, PartialEq)]
636#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
637
638pub enum Alignment {
639    /// Default text alignment.
640    None,
641    Left,
642    Center,
643    Right,
644}
645
646bitflags::bitflags! {
647    /// Option struct containing flags for enabling extra features
648    /// that are not part of the CommonMark spec.
649    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
650    pub struct Options: u32 {
651        const ENABLE_TABLES = 1 << 1;
652        /// GitHub-compatible footnote syntax.
653        ///
654        /// Footnotes are referenced with the syntax `[^IDENT]`,
655        /// and defined with an identifier followed by a colon at top level.
656        ///
657        /// ---
658        ///
659        /// ```markdown
660        /// Footnote referenced [^1].
661        ///
662        /// [^1]: footnote defined
663        /// ```
664        ///
665        /// Footnote referenced [^1].
666        ///
667        /// [^1]: footnote defined
668        const ENABLE_FOOTNOTES = 1 << 2;
669        const ENABLE_STRIKETHROUGH = 1 << 3;
670        const ENABLE_TASKLISTS = 1 << 4;
671        /// Enables replacement of ASCII punctuation characters with
672        /// Unicode ligatures and smart quotes.
673        ///
674        /// This includes replacing `--` with `—`, `---` with `—`, `...` with `…`,
675        /// `"quote"` with `“quote”`, and `'quote'` with `‘quote’`.
676        ///
677        /// The replacement takes place during the parsing of the document.
678        const ENABLE_SMART_PUNCTUATION = 1 << 5;
679        /// Extension to allow headings to have ID and classes.
680        ///
681        /// `# text { #id .class1 .class2 myattr other_attr=myvalue }`
682        /// is interpreted as a level 1 heading
683        /// with the content `text`, ID `id`, classes `class1` and `class2` and
684        /// custom attributes `myattr` (without value) and
685        /// `other_attr` with value `myvalue`.
686        /// Note that ID, classes, and custom attributes should be space-separated.
687        const ENABLE_HEADING_ATTRIBUTES = 1 << 6;
688        /// Metadata blocks in YAML style, i.e.:
689        /// - starting with a `---` line
690        /// - ending with a `---` or `...` line
691        const ENABLE_YAML_STYLE_METADATA_BLOCKS = 1 << 7;
692        /// Metadata blocks delimited by:
693        /// - `+++` line at start
694        /// - `+++` line at end
695        const ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS = 1 << 8;
696        /// Older footnote syntax. This flag implies `ENABLE_FOOTNOTES`, changing it to use an
697        /// older syntax instead of the new, default, GitHub-compatible syntax.
698        ///
699        /// New syntax is different from the old syntax regarding
700        /// indentation, nesting, and footnote references with no definition:
701        ///
702        /// ```markdown
703        /// [^1]: In new syntax, this is two footnote definitions.
704        /// [^2]: In old syntax, this is a single footnote definition with two lines.
705        ///
706        /// [^3]:
707        ///
708        ///     In new syntax, this is a footnote with two paragraphs.
709        ///
710        ///     In old syntax, this is a footnote followed by a code block.
711        ///
712        /// In new syntax, this undefined footnote definition renders as
713        /// literal text [^4]. In old syntax, it creates a dangling link.
714        /// ```
715        const ENABLE_OLD_FOOTNOTES = (1 << 9) | (1 << 2);
716        /// With this feature enabled, two events `Event::InlineMath` and `Event::DisplayMath`
717        /// are emitted that conventionally contain TeX formulas.
718        const ENABLE_MATH = 1 << 10;
719        /// Misc GitHub Flavored Markdown features not supported in CommonMark.
720        /// The following features are currently behind this tag:
721        /// - Blockquote tags ([!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]).
722        const ENABLE_GFM = 1 << 11;
723        /// Commonmark-HS-Extensions compatible definition lists.
724        ///
725        /// ```markdown
726        /// title 1
727        ///   : definition 1
728        /// title 2
729        ///   : definition 2
730        /// ```
731        const ENABLE_DEFINITION_LIST = 1 << 12;
732        const ENABLE_SUPERSCRIPT = 1 << 13;
733        const ENABLE_SUBSCRIPT = 1 << 14;
734        /// Obsidian-style Wikilinks.
735        const ENABLE_WIKILINKS = 1 << 15;
736    }
737}
738
739impl Options {
740    pub(crate) fn has_gfm_footnotes(&self) -> bool {
741        self.contains(Options::ENABLE_FOOTNOTES) && !self.contains(Options::ENABLE_OLD_FOOTNOTES)
742    }
743}