markdown_syntax/ast.rs
1//! The owned Markdown AST that [`parse()`](crate::parse()) produces and
2//! `Document::to_markdown`/`to_html` consume. Every node carries a [`NodeMeta`]
3//! with an optional source [`Span`]. [`Block`] and [`Inline`] are the two node
4//! enums; everything else is a concrete node struct or a small enum describing
5//! a node's variant.
6
7use alloc::{string::String, sync::Arc, vec::Vec};
8
9use crate::diagnostic::Diagnostic;
10use crate::parse::Definitions;
11use crate::span::Span;
12
13/// Metadata attached to every AST node; currently just the source span.
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
15pub struct NodeMeta {
16 /// The node's source location, or `None` for hand-built nodes.
17 pub span: Option<Span>,
18}
19
20impl NodeMeta {
21 /// Wrap an optional [`Span`] into a [`NodeMeta`].
22 pub const fn new(span: Option<Span>) -> Self {
23 Self { span }
24 }
25}
26
27/// The root of a document: a sequence of top-level [`Block`]s, and for a
28/// parsed document the source it was parsed from.
29///
30/// The source is the only truth: writing a parsed document back with
31/// [`Document::to_markdown`] reuses the source of every top-level block that
32/// did not change. Only [`parse()`](crate::parse()) gives a document a
33/// source; [`Document::without_source`] drops it, and a document without one
34/// is written as canonical Markdown.
35#[derive(Clone, Debug, Default, Eq, PartialEq)]
36pub struct Document {
37 /// Node metadata (source span).
38 pub meta: NodeMeta,
39 /// The document's top-level blocks, in source order.
40 pub children: Vec<Block>,
41 /// The input `parse` read, shared by clones.
42 source: Option<Arc<str>>,
43}
44
45impl Document {
46 /// A document holding `children` and no source.
47 pub fn new(children: Vec<Block>) -> Self {
48 Self {
49 meta: NodeMeta::default(),
50 children,
51 source: None,
52 }
53 }
54
55 /// The document `parse` returns: `children` read from `source`.
56 pub(crate) fn parsed(meta: NodeMeta, children: Vec<Block>, source: &str) -> Self {
57 Self {
58 meta,
59 children,
60 source: Some(Arc::from(source)),
61 }
62 }
63
64 /// The input this document was parsed from, or `None` for a document
65 /// built by hand or returned by [`Document::without_source`].
66 pub fn source(&self) -> Option<&str> {
67 self.source.as_deref()
68 }
69
70 /// This document without its source; writing it gives canonical
71 /// Markdown.
72 #[must_use]
73 pub fn without_source(mut self) -> Self {
74 self.source = None;
75 self
76 }
77}
78
79impl From<Vec<Block>> for Document {
80 fn from(children: Vec<Block>) -> Self {
81 Self::new(children)
82 }
83}
84
85/// Child indices from a document down to one node. A list's children are its
86/// items, a table's are its rows, and a row's are its cells; every other
87/// container's are its blocks.
88#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
89pub struct NodePath(pub Vec<usize>);
90
91impl From<Vec<usize>> for NodePath {
92 fn from(indices: Vec<usize>) -> Self {
93 Self(indices)
94 }
95}
96
97impl From<&[usize]> for NodePath {
98 fn from(indices: &[usize]) -> Self {
99 Self(indices.into())
100 }
101}
102
103impl<const N: usize> From<[usize; N]> for NodePath {
104 fn from(indices: [usize; N]) -> Self {
105 Self(indices.into())
106 }
107}
108
109/// A block-level node: the building blocks of a document's vertical structure.
110#[derive(Clone, Debug, Eq, PartialEq)]
111pub enum Block {
112 /// A paragraph of inline content.
113 Paragraph(Paragraph),
114 /// An ATX heading: `# h`.
115 Heading(Heading),
116 /// A thematic break / horizontal rule: `---`, `***`, or `___`.
117 ThematicBreak(ThematicBreak),
118 /// A block quote: lines prefixed with `> `.
119 BlockQuote(BlockQuote),
120 /// A GFM alert / admonition: `> [!NOTE]` etc.
121 Alert(Alert),
122 /// A bullet or ordered list.
123 List(List),
124 /// A fenced (```` ``` ````) or indented code block.
125 CodeBlock(CodeBlock),
126 /// A raw HTML block.
127 HtmlBlock(HtmlBlock),
128 /// A structured raw HTML container whose interior is parsed as Markdown.
129 HtmlContainer(HtmlContainer),
130 /// A link reference definition: `[label]: url "title"`.
131 Definition(Definition),
132 /// A footnote definition: `[^id]: text`.
133 FootnoteDefinition(FootnoteDefinition),
134 /// A GFM pipe table.
135 Table(Table),
136 /// A display math block: `$$ … $$`.
137 MathBlock(MathBlock),
138 /// A leading frontmatter block (`---` YAML or `+++` TOML).
139 Frontmatter(Frontmatter),
140 /// A leaf directive: `::name[label]{attrs}`.
141 LeafDirective(LeafDirective),
142 /// A container directive: `:::name … :::`.
143 ContainerDirective(ContainerDirective),
144 /// A blank line beyond the one that separates two blocks.
145 BlankLine(BlankLine),
146}
147
148/// A paragraph: a run of inline content. Source: any plain text line(s).
149#[derive(Clone, Debug, Eq, PartialEq)]
150pub struct Paragraph {
151 /// Node metadata (source span).
152 pub meta: NodeMeta,
153 /// The paragraph's inline content.
154 pub children: Vec<Inline>,
155 /// The text the content was built from, by [`Paragraph::with_source`].
156 pub(crate) source: Option<String>,
157}
158
159/// A heading. Source: `# Title` … `###### Title` (ATX).
160#[derive(Clone, Debug, Eq, PartialEq)]
161pub struct Heading {
162 /// Node metadata (source span).
163 pub meta: NodeMeta,
164 /// Heading level, 1..=6.
165 pub depth: u8,
166 /// The heading's inline content.
167 pub children: Vec<Inline>,
168 /// The text the content was built from, by [`Heading::with_source`].
169 pub(crate) source: Option<String>,
170}
171
172/// A blank line the source holds beyond the one that separates two blocks:
173/// K blank lines between two blocks are one separator and K − 1 of these.
174/// Before a document's first block, at the start or end of a block quote,
175/// alert, HTML container, or container directive, and after a document's
176/// last line, each blank line is one. Source: a line holding only
177/// whitespace and its containers' `>` markers.
178#[derive(Clone, Debug, Default, Eq, PartialEq)]
179pub struct BlankLine {
180 /// Node metadata (source span): the line with its line ending.
181 pub meta: NodeMeta,
182}
183
184/// A thematic break / horizontal rule. Source: `---`, `***`, or `___`.
185#[derive(Clone, Debug, Eq, PartialEq)]
186pub struct ThematicBreak {
187 /// Node metadata (source span).
188 pub meta: NodeMeta,
189 /// Which character formed the break.
190 pub marker: ThematicBreakMarker,
191}
192
193/// The character used to draw a [`ThematicBreak`].
194#[derive(Clone, Copy, Debug, Eq, PartialEq)]
195pub enum ThematicBreakMarker {
196 /// Dashes: `---`.
197 Dash,
198 /// Asterisks: `***`.
199 Asterisk,
200 /// Underscores: `___`.
201 Underscore,
202}
203
204/// A block quote: content prefixed with `> `.
205#[derive(Clone, Debug, Eq, PartialEq)]
206pub struct BlockQuote {
207 /// Node metadata (source span).
208 pub meta: NodeMeta,
209 /// The quoted block content.
210 pub children: Vec<Block>,
211}
212
213/// A GFM alert / admonition. Source: `> [!NOTE]` followed by quoted content.
214#[derive(Clone, Debug, Eq, PartialEq)]
215pub struct Alert {
216 /// Node metadata (source span).
217 pub meta: NodeMeta,
218 /// The alert severity / type.
219 pub kind: AlertKind,
220 /// An optional custom title following the `[!KIND]` marker.
221 pub title: Option<String>,
222 /// The alert's block content.
223 pub children: Vec<Block>,
224}
225
226/// The kind of a GFM [`Alert`] (the `[!KIND]` marker).
227#[derive(Clone, Copy, Debug, Eq, PartialEq)]
228pub enum AlertKind {
229 /// `> [!NOTE]`.
230 Note,
231 /// `> [!TIP]`.
232 Tip,
233 /// `> [!IMPORTANT]`.
234 Important,
235 /// `> [!WARNING]`.
236 Warning,
237 /// `> [!CAUTION]`.
238 Caution,
239}
240
241/// A bullet or ordered list. Source: `- a` / `1. a` lines.
242#[derive(Clone, Debug, Eq, PartialEq)]
243pub struct List {
244 /// Node metadata (source span).
245 pub meta: NodeMeta,
246 /// `true` for an ordered list, `false` for a bullet list.
247 pub ordered: bool,
248 /// The marker delimiter used by the list items.
249 pub delimiter: ListDelimiter,
250 /// `true` if the list is tight (no blank lines between items / no `<p>`).
251 pub tight: bool,
252 /// The list's items.
253 pub children: Vec<ListItem>,
254}
255
256/// The marker character that delimits a list's items.
257#[derive(Clone, Copy, Debug, Eq, PartialEq)]
258pub enum ListDelimiter {
259 /// Bullet `-`.
260 Dash,
261 /// Bullet `*`.
262 Asterisk,
263 /// Bullet `+`.
264 Plus,
265 /// Ordered `1.`.
266 Period,
267 /// Ordered `1)`.
268 Paren,
269}
270
271/// A single list item, optionally a GFM task-list checkbox.
272#[derive(Clone, Debug, Eq, PartialEq)]
273pub struct ListItem {
274 /// Node metadata (source span).
275 pub meta: NodeMeta,
276 /// Task-list state: `Some(true)` for `[x]`, `Some(false)` for `[ ]`, `None` otherwise.
277 pub checked: Option<bool>,
278 /// The number an ordered item is written with (`3.` => `Some(3)`), which
279 /// for the first item is the list's start. `None` is written as the
280 /// previous item's number plus one, or 1 for the first item; a bullet
281 /// item holds `None`.
282 pub number: Option<u64>,
283 /// The item's block content.
284 pub children: Vec<Block>,
285}
286
287/// A code block. Source: ```` ```lang … ``` ```` (fenced) or 4-space-indented lines.
288#[derive(Clone, Debug, Eq, PartialEq)]
289pub struct CodeBlock {
290 /// Node metadata (source span).
291 pub meta: NodeMeta,
292 /// Whether the block is fenced (and with what fence) or indented.
293 pub kind: CodeBlockKind,
294 /// The info string after a fence (e.g. the `rust` in ```` ```rust ````).
295 pub info: Option<String>,
296 /// The literal code contents.
297 pub value: String,
298}
299
300/// Whether a [`CodeBlock`] is fenced or indented.
301#[derive(Clone, Copy, Debug, Eq, PartialEq)]
302pub enum CodeBlockKind {
303 /// A fenced code block; records the fence char and its run length.
304 Fenced {
305 /// Which character formed the fence (backtick or tilde).
306 marker: FenceMarker,
307 /// The number of fence characters in the opening fence (>=3).
308 length: usize,
309 },
310 /// A 4-space-indented code block.
311 Indented,
312}
313
314/// The character used to fence a [`CodeBlock`].
315#[derive(Clone, Copy, Debug, Eq, PartialEq)]
316pub enum FenceMarker {
317 /// Backtick fence: ```` ``` ````.
318 Backtick,
319 /// Tilde fence: `~~~`.
320 Tilde,
321}
322
323/// A raw HTML block: HTML emitted verbatim.
324#[derive(Clone, Debug, Eq, PartialEq)]
325pub struct HtmlBlock {
326 /// Node metadata (source span).
327 pub meta: NodeMeta,
328 /// The literal HTML source.
329 pub value: String,
330}
331
332/// A structured raw HTML container, such as `<details> ... </details>`.
333#[derive(Clone, Debug, Eq, PartialEq)]
334pub struct HtmlContainer {
335 /// Node metadata (source span).
336 pub meta: NodeMeta,
337 /// The opening HTML tag.
338 pub opening: HtmlTag,
339 /// The parsed container content.
340 pub content: HtmlContainerContent,
341 /// The closing HTML tag.
342 pub closing: HtmlTag,
343}
344
345/// A raw HTML tag that bounds an [`HtmlContainer`].
346#[derive(Clone, Debug, Eq, PartialEq)]
347pub struct HtmlTag {
348 /// Node metadata (source span).
349 pub meta: NodeMeta,
350 /// The normalized lowercase tag name.
351 pub name: String,
352 /// The literal tag source, including angle brackets and attributes.
353 pub raw: String,
354}
355
356/// The parsed content inside an [`HtmlContainer`].
357#[derive(Clone, Debug, Eq, PartialEq)]
358pub enum HtmlContainerContent {
359 /// Block-level Markdown content.
360 Blocks(Vec<Block>),
361 /// Inline-level Markdown content.
362 Inlines(Vec<Inline>),
363}
364
365/// A link reference definition. Source: `[label]: destination "title"`.
366#[derive(Clone, Debug, Eq, PartialEq)]
367pub struct Definition {
368 /// Node metadata (source span).
369 pub meta: NodeMeta,
370 /// The label as written in the source (e.g. `Foo Bar`).
371 pub label: String,
372 /// The normalized lookup key (case-folded, whitespace-collapsed) for matching references.
373 pub identifier: String,
374 /// The link target URL.
375 pub destination: String,
376 /// How the destination was delimited (bare or `<…>`).
377 pub destination_kind: LinkDestinationKind,
378 /// The optional link title, with the quotes it is written in.
379 pub title: Option<Title>,
380}
381
382/// A footnote definition. Source: `[^id]: footnote text`.
383#[derive(Clone, Debug, Eq, PartialEq)]
384pub struct FootnoteDefinition {
385 /// Node metadata (source span).
386 pub meta: NodeMeta,
387 /// The label as written in the source (the text after `^`).
388 pub label: String,
389 /// The normalized lookup key matching [`FootnoteReference`]s.
390 pub identifier: String,
391 /// The footnote's block content.
392 pub children: Vec<Block>,
393}
394
395/// A GFM pipe table: a header row, an alignment row, then body rows. Source:
396/// `| a | b |` / `|---|---|` / `| 1 | 2 |`.
397#[derive(Clone, Debug, Eq, PartialEq)]
398pub struct Table {
399 /// Node metadata (source span).
400 pub meta: NodeMeta,
401 /// Per-column alignment from the delimiter row.
402 pub alignments: Vec<TableAlignment>,
403 /// All rows; the first is the header row.
404 pub rows: Vec<TableRow>,
405}
406
407/// The alignment of a [`Table`] column, from the `:---:` delimiter row.
408#[derive(Clone, Copy, Debug, Eq, PartialEq)]
409pub enum TableAlignment {
410 /// No explicit alignment: `---`.
411 None,
412 /// Left-aligned: `:---`.
413 Left,
414 /// Center-aligned: `:---:`.
415 Center,
416 /// Right-aligned: `---:`.
417 Right,
418}
419
420/// A single row of a [`Table`].
421#[derive(Clone, Debug, Eq, PartialEq)]
422pub struct TableRow {
423 /// Node metadata (source span).
424 pub meta: NodeMeta,
425 /// The row's cells.
426 pub cells: Vec<TableCell>,
427}
428
429/// A single cell of a [`TableRow`].
430#[derive(Clone, Debug, Eq, PartialEq)]
431pub struct TableCell {
432 /// Node metadata (source span).
433 pub meta: NodeMeta,
434 /// The cell's inline content.
435 pub children: Vec<Inline>,
436 /// The text the content was built from, by [`TableCell::with_source`].
437 pub(crate) source: Option<String>,
438}
439
440/// A display math block. Source: `$$ … $$`.
441#[derive(Clone, Debug, Eq, PartialEq)]
442pub struct MathBlock {
443 /// Node metadata (source span).
444 pub meta: NodeMeta,
445 /// The literal math contents (between the `$$` fences).
446 pub value: String,
447}
448
449/// A leading frontmatter block. Source: `---` YAML or `+++` TOML at the top of
450/// the document.
451#[derive(Clone, Debug, Eq, PartialEq)]
452pub struct Frontmatter {
453 /// Node metadata (source span).
454 pub meta: NodeMeta,
455 /// Whether the frontmatter is YAML or TOML.
456 pub kind: FrontmatterKind,
457 /// The literal frontmatter contents (between the fences).
458 pub value: String,
459}
460
461/// The format of a [`Frontmatter`] block.
462#[derive(Clone, Copy, Debug, Eq, PartialEq)]
463pub enum FrontmatterKind {
464 /// YAML frontmatter, fenced by `---`.
465 Yaml,
466 /// TOML frontmatter, fenced by `+++`.
467 Toml,
468}
469
470/// A leaf directive. Source: `::name[label]{attrs}`.
471#[derive(Clone, Debug, Eq, PartialEq)]
472pub struct LeafDirective {
473 /// Node metadata (source span).
474 pub meta: NodeMeta,
475 /// The directive name following the `::`.
476 pub name: String,
477 /// The optional `[label]` inline content.
478 pub label: Vec<Inline>,
479 /// The optional `{attrs}` attributes.
480 pub attributes: Vec<DirectiveAttribute>,
481 /// The text the label was built from, by [`LeafDirective::with_source`].
482 pub(crate) label_source: Option<String>,
483}
484
485/// A container directive. Source: `:::name[label]{attrs}` … `:::`.
486#[derive(Clone, Debug, Eq, PartialEq)]
487pub struct ContainerDirective {
488 /// Node metadata (source span).
489 pub meta: NodeMeta,
490 /// The directive name following the `:::`.
491 pub name: String,
492 /// The optional `[label]` inline content.
493 pub label: Vec<Inline>,
494 /// The optional `{attrs}` attributes.
495 pub attributes: Vec<DirectiveAttribute>,
496 /// The directive's enclosed block content.
497 pub children: Vec<Block>,
498 /// The text the label was built from, by
499 /// [`ContainerDirective::with_source`].
500 pub(crate) label_source: Option<String>,
501}
502
503/// An inline-level node: the leaf and span content inside blocks.
504#[derive(Clone, Debug, Eq, PartialEq)]
505pub enum Inline {
506 /// Literal text.
507 Text(Text),
508 /// A backslash escape such as `\*`.
509 Escape(Escape),
510 /// A character reference such as `&` or `÷`.
511 CharacterReference(CharacterReference),
512 /// Emphasis: `*text*` or `_text_`.
513 Emphasis(Emphasis),
514 /// Strong emphasis: `**text**` or `__text__`.
515 Strong(Strong),
516 /// Strikethrough: `~~text~~`.
517 Delete(Delete),
518 /// A highlight / "mark" span: `==text==`.
519 Mark(Mark),
520 /// An emoji-style shortcode: `:name:`.
521 Shortcode(Shortcode),
522 /// An inline code span: `` `code` ``.
523 Code(CodeInline),
524 /// An inline link: `[text](url)`.
525 Link(Link),
526 /// An autolink: `<https://example.com>` or a bare literal URL or email.
527 Autolink(Autolink),
528 /// An inline image: ``.
529 Image(Image),
530 /// A reference link: `[text][label]`.
531 LinkReference(LinkReference),
532 /// A reference image: `![alt][label]`.
533 ImageReference(ImageReference),
534 /// Raw inline HTML such as `<span>`.
535 Html(HtmlInline),
536 /// A soft line break (a plain newline within a paragraph).
537 SoftBreak(SoftBreak),
538 /// A hard line break (`\` or two trailing spaces).
539 LineBreak(LineBreak),
540 /// Inline math: `$x$`.
541 Math(MathInline),
542 /// A footnote reference: `[^id]`.
543 FootnoteReference(FootnoteReference),
544 /// An inline footnote: `^[inline note]`.
545 InlineFootnote(InlineFootnote),
546 /// A wiki link: `[[target|label]]`.
547 WikiLink(WikiLink),
548 /// A text directive: `:name[label]{attrs}`.
549 TextDirective(TextDirective),
550 /// A tag: `#name`.
551 Tag(Tag),
552}
553
554/// Literal text content.
555#[derive(Clone, Debug, Eq, PartialEq)]
556pub struct Text {
557 /// Node metadata (source span).
558 pub meta: NodeMeta,
559 /// The text value.
560 pub value: String,
561}
562
563/// A backslash escape such as `\*` or `\\`.
564#[derive(Clone, Debug, Eq, PartialEq)]
565pub struct Escape {
566 /// Node metadata (source span).
567 pub meta: NodeMeta,
568 /// The escaped (literal) character.
569 pub value: char,
570}
571
572/// A character reference such as `&` or `÷`. The reference as
573/// written is the node's one fact; [`CharacterReference::value`] decodes it.
574#[derive(Clone, Debug, Eq, PartialEq)]
575pub struct CharacterReference {
576 /// Node metadata (source span).
577 pub meta: NodeMeta,
578 /// The reference as written, including `&` and `;` (e.g. `&`).
579 pub reference: String,
580}
581
582/// Emphasis (typically italic): `*text*` or `_text_`.
583#[derive(Clone, Debug, Eq, PartialEq)]
584pub struct Emphasis {
585 /// Node metadata (source span).
586 pub meta: NodeMeta,
587 /// The delimiter the span was written with.
588 pub delimiter: EmphasisDelimiter,
589 /// The emphasized inline content.
590 pub children: Vec<Inline>,
591}
592
593/// Strong emphasis (typically bold): `**text**` or `__text__`.
594#[derive(Clone, Debug, Eq, PartialEq)]
595pub struct Strong {
596 /// Node metadata (source span).
597 pub meta: NodeMeta,
598 /// The delimiter the span was written with.
599 pub delimiter: EmphasisDelimiter,
600 /// The strongly-emphasized inline content.
601 pub children: Vec<Inline>,
602}
603
604/// The delimiter character of an [`Emphasis`] or [`Strong`] span.
605#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
606pub enum EmphasisDelimiter {
607 /// `*text*` / `**text**`.
608 #[default]
609 Asterisk,
610 /// `_text_` / `__text__`.
611 Underscore,
612}
613
614/// Strikethrough: `~~text~~`.
615#[derive(Clone, Debug, Eq, PartialEq)]
616pub struct Delete {
617 /// Node metadata (source span).
618 pub meta: NodeMeta,
619 /// The struck-through inline content.
620 pub children: Vec<Inline>,
621}
622
623/// A highlight / "mark" span: `==text==`.
624#[derive(Clone, Debug, Eq, PartialEq)]
625pub struct Mark {
626 /// Node metadata (source span).
627 pub meta: NodeMeta,
628 /// The highlighted inline content.
629 pub children: Vec<Inline>,
630}
631
632/// An emoji shortcode: `:name:`, where `name` is a gemoji name or alias.
633#[derive(Clone, Debug, Eq, PartialEq)]
634pub struct Shortcode {
635 /// Node metadata (source span).
636 pub meta: NodeMeta,
637 /// The shortcode name between the colons (e.g. `smile` for `:smile:`).
638 pub name: String,
639}
640
641/// A tag: `#` and a name, such as `#rust` or `#ai/agents`.
642#[derive(Clone, Debug, Eq, PartialEq)]
643pub struct Tag {
644 /// Node metadata (source span).
645 pub meta: NodeMeta,
646 /// The name as written, without the `#`. A `/` separates levels, which
647 /// the caller splits.
648 pub name: String,
649}
650
651/// An inline code span: `` `code` ``. The fence and the padding spaces are
652/// not recorded: the serializer chooses them from the value.
653#[derive(Clone, Debug, Eq, PartialEq)]
654pub struct CodeInline {
655 /// Node metadata (source span).
656 pub meta: NodeMeta,
657 /// The code text, normalized as CommonMark reads it: line endings as
658 /// spaces, and one space stripped from each end when both ends hold one
659 /// and the text is not all spaces.
660 pub value: String,
661}
662
663/// An inline link: `[text](destination "title")`.
664#[derive(Clone, Debug, Eq, PartialEq)]
665pub struct Link {
666 /// Node metadata (source span).
667 pub meta: NodeMeta,
668 /// The link target URL.
669 pub destination: String,
670 /// How the destination was delimited (bare or `<…>`).
671 pub destination_kind: LinkDestinationKind,
672 /// The optional link title, with the quotes it is written in.
673 pub title: Option<Title>,
674 /// The link's inline content (the visible text).
675 pub children: Vec<Inline>,
676}
677
678/// An autolink: `<https://example.com>`, `<a@b.c>`, or a GFM literal
679/// autolink, a bare `https://…`, `www.…`, or email address. The text as
680/// written is the node's one fact; [`Autolink::destination`] derives the link
681/// target from it.
682#[derive(Clone, Debug, Eq, PartialEq)]
683pub struct Autolink {
684 /// Node metadata (source span). An angle-bracket autolink's span covers
685 /// its brackets.
686 pub meta: NodeMeta,
687 /// Which autolink syntax the text is written in.
688 pub form: AutolinkForm,
689 /// The URL or address as written, without an angle-bracket autolink's
690 /// brackets.
691 pub text: String,
692}
693
694/// How an [`Autolink`] is written.
695#[derive(Clone, Copy, Debug, Eq, PartialEq)]
696pub enum AutolinkForm {
697 /// An angle-bracket autolink: `<https://example.com>` or `<a@b.c>`.
698 Angle,
699 /// A GFM literal autolink: a bare `https://…`, `www.…`, or email address.
700 Literal,
701}
702
703/// An inline image: ``.
704#[derive(Clone, Debug, Eq, PartialEq)]
705pub struct Image {
706 /// Node metadata (source span).
707 pub meta: NodeMeta,
708 /// The image source URL.
709 pub destination: String,
710 /// How the destination was delimited (bare or `<…>`).
711 pub destination_kind: LinkDestinationKind,
712 /// The optional image title, with the quotes it is written in.
713 pub title: Option<Title>,
714 /// The image's alt-text inline content.
715 pub alt: Vec<Inline>,
716}
717
718/// How a link/image destination was delimited in the source.
719#[derive(Clone, Copy, Debug, Eq, PartialEq)]
720pub enum LinkDestinationKind {
721 /// A bare destination: `(url)`.
722 Bare,
723 /// An angle-bracketed destination: `(<url>)`.
724 Angle,
725 /// No destination present: `()`.
726 Omitted,
727}
728
729/// How a link/image title was quoted in the source.
730#[derive(Clone, Copy, Debug, Eq, PartialEq)]
731pub enum LinkTitleKind {
732 /// Double-quoted: `"title"`.
733 DoubleQuote,
734 /// Single-quoted: `'title'`.
735 SingleQuote,
736 /// Parenthesized: `(title)`.
737 Paren,
738}
739
740/// The title of a [`Link`], [`Image`], or [`Definition`]: its text and the
741/// quotes it is written in.
742#[derive(Clone, Debug, Eq, PartialEq)]
743pub struct Title {
744 /// The title text, with its escapes and character references decoded.
745 pub value: String,
746 /// How the title is quoted.
747 pub kind: LinkTitleKind,
748}
749
750/// A reference link: `[text][label]`, `[text][]`, or `[text]`.
751#[derive(Clone, Debug, Eq, PartialEq)]
752pub struct LinkReference {
753 /// Node metadata (source span).
754 pub meta: NodeMeta,
755 /// The normalized lookup key matching a [`Definition`].
756 pub identifier: String,
757 /// The label as written in the source.
758 pub label: String,
759 /// Whether the reference is full, collapsed, or shortcut form.
760 pub kind: ReferenceKind,
761 /// The link's inline content (the visible text).
762 pub children: Vec<Inline>,
763}
764
765/// A reference image: `![alt][label]`, `![alt][]`, or `![alt]`.
766#[derive(Clone, Debug, Eq, PartialEq)]
767pub struct ImageReference {
768 /// Node metadata (source span).
769 pub meta: NodeMeta,
770 /// The normalized lookup key matching a [`Definition`].
771 pub identifier: String,
772 /// The label as written in the source.
773 pub label: String,
774 /// Whether the reference is full, collapsed, or shortcut form.
775 pub kind: ReferenceKind,
776 /// The image's alt-text inline content.
777 pub alt: Vec<Inline>,
778}
779
780/// The form of a reference link/image.
781#[derive(Clone, Copy, Debug, Eq, PartialEq)]
782pub enum ReferenceKind {
783 /// Full reference: `[text][label]`.
784 Full,
785 /// Collapsed reference: `[label][]`.
786 Collapsed,
787 /// Shortcut reference: `[label]`.
788 Shortcut,
789}
790
791/// Raw inline HTML such as `<span>` or `</em>`.
792#[derive(Clone, Debug, Eq, PartialEq)]
793pub struct HtmlInline {
794 /// Node metadata (source span).
795 pub meta: NodeMeta,
796 /// The literal HTML source.
797 pub value: String,
798}
799
800/// A soft line break: a plain newline within a paragraph.
801#[derive(Clone, Debug, Eq, PartialEq)]
802pub struct SoftBreak {
803 /// Node metadata (source span).
804 pub meta: NodeMeta,
805}
806
807/// A hard line break: a trailing `\` or two trailing spaces.
808#[derive(Clone, Debug, Eq, PartialEq)]
809pub struct LineBreak {
810 /// Node metadata (source span).
811 pub meta: NodeMeta,
812 /// Which syntax produced the break.
813 pub kind: LineBreakKind,
814}
815
816/// Which syntax produced a hard [`LineBreak`].
817#[derive(Clone, Copy, Debug, Eq, PartialEq)]
818pub enum LineBreakKind {
819 /// A trailing backslash: `\`.
820 Backslash,
821 /// Two or more trailing spaces.
822 Spaces,
823}
824
825/// Which syntax delimited an inline [`MathInline`] span.
826#[derive(Clone, Copy, Debug, Eq, PartialEq)]
827pub enum MathInlineKind {
828 /// Dollar-fenced inline math (`$…$`, `$$…$$`, …); `dollars` is the fence
829 /// length (>=1). Dollar math always renders inline, while a 2-dollar fence
830 /// is conventionally treated as display elsewhere in the ecosystem.
831 Dollar {
832 /// The number of `$` characters in the fence (>=1).
833 dollars: u8,
834 },
835 /// Math-code span: `$`…`$`.
836 Code,
837}
838
839/// Inline math: `$x$`.
840#[derive(Clone, Debug, Eq, PartialEq)]
841pub struct MathInline {
842 /// Node metadata (source span).
843 pub meta: NodeMeta,
844 /// The literal math contents (between the fences).
845 pub value: String,
846 /// Which delimiter syntax was used.
847 pub kind: MathInlineKind,
848}
849
850/// A footnote reference: `[^id]`.
851#[derive(Clone, Debug, Eq, PartialEq)]
852pub struct FootnoteReference {
853 /// Node metadata (source span).
854 pub meta: NodeMeta,
855 /// The label as written in the source (the text after `^`).
856 pub label: String,
857 /// The normalized lookup key matching a [`FootnoteDefinition`].
858 pub identifier: String,
859}
860
861/// An inline footnote: `^[inline note]`.
862#[derive(Clone, Debug, Eq, PartialEq)]
863pub struct InlineFootnote {
864 /// Node metadata (source span).
865 pub meta: NodeMeta,
866 /// The footnote's inline content.
867 pub children: Vec<Inline>,
868}
869
870/// A wiki link: `[[target|label]]`, or an embed: `![[target|label]]`.
871#[derive(Clone, Debug, Eq, PartialEq)]
872pub struct WikiLink {
873 /// Node metadata (source span). An embed's span starts at its `!`.
874 pub meta: NodeMeta,
875 /// The link target (page name) as written in the source, its backslash
876 /// escapes and character references kept; [`WikiLink::decoded_target`]
877 /// gives the text they stand for.
878 pub target: String,
879 /// The visible label as written in the source, its backslash escapes and
880 /// character references kept; [`WikiLink::decoded_label`] gives the text
881 /// they stand for. A label equal to `target` means the link is written
882 /// without a `|label` part.
883 pub label: String,
884 /// Whether a `!` directly before the `[[` marks the link as an embed.
885 pub embed: bool,
886}
887
888/// A text directive. Source: `:name[label]{attrs}`.
889#[derive(Clone, Debug, Eq, PartialEq)]
890pub struct TextDirective {
891 /// Node metadata (source span).
892 pub meta: NodeMeta,
893 /// The directive name following the `:`.
894 pub name: String,
895 /// The optional `[label]` inline content.
896 pub label: Vec<Inline>,
897 /// The optional `{attrs}` attributes.
898 pub attributes: Vec<DirectiveAttribute>,
899}
900
901/// One attribute of a directive's `{name=value}` block.
902#[derive(Clone, Debug, Eq, PartialEq)]
903pub struct DirectiveAttribute {
904 /// The attribute name.
905 pub name: String,
906 /// The attribute value, or `None` for a valueless attribute.
907 pub value: Option<String>,
908}
909
910// ---------------------------------------------------------------------------
911// Ergonomic accessors and a minimal construction layer.
912//
913// Every node carries a `meta: NodeMeta`, so `meta()`/`span()` are uniform across
914// the enums and free callers from writing an exhaustive match just to read a
915// span. `From`/`new` collapse the `Variant(Struct { meta, .. })` boilerplate for
916// hand-built ASTs; the raw struct literals remain available for full control.
917// ---------------------------------------------------------------------------
918
919macro_rules! impl_meta_accessors {
920 ($enum:ident { $($variant:ident),+ $(,)? }) => {
921 impl $enum {
922 /// Borrow this node's [`NodeMeta`].
923 pub fn meta(&self) -> &NodeMeta {
924 match self { $( $enum::$variant(node) => &node.meta, )+ }
925 }
926
927 /// This node's source span, if it carries one.
928 pub fn span(&self) -> Option<Span> {
929 self.meta().span
930 }
931 }
932 };
933}
934
935macro_rules! impl_from_variants {
936 ($enum:ident { $($variant:ident($ty:ty)),+ $(,)? }) => {
937 $(
938 impl From<$ty> for $enum {
939 fn from(node: $ty) -> Self {
940 $enum::$variant(node)
941 }
942 }
943 )+
944 };
945}
946
947impl_meta_accessors!(Block {
948 Paragraph,
949 Heading,
950 ThematicBreak,
951 BlockQuote,
952 Alert,
953 List,
954 CodeBlock,
955 HtmlBlock,
956 HtmlContainer,
957 Definition,
958 FootnoteDefinition,
959 Table,
960 MathBlock,
961 Frontmatter,
962 LeafDirective,
963 ContainerDirective,
964 BlankLine,
965});
966
967impl_from_variants!(Block {
968 Paragraph(Paragraph), Heading(Heading), ThematicBreak(ThematicBreak),
969 BlockQuote(BlockQuote), Alert(Alert), List(List),
970 CodeBlock(CodeBlock), HtmlBlock(HtmlBlock), Definition(Definition),
971 HtmlContainer(HtmlContainer),
972 FootnoteDefinition(FootnoteDefinition), Table(Table), MathBlock(MathBlock),
973 Frontmatter(Frontmatter), LeafDirective(LeafDirective), ContainerDirective(ContainerDirective),
974 BlankLine(BlankLine),
975});
976
977impl_meta_accessors!(Inline {
978 Text,
979 Escape,
980 CharacterReference,
981 Emphasis,
982 Strong,
983 Delete,
984 Mark,
985 Shortcode,
986 Code,
987 Link,
988 Autolink,
989 Image,
990 LinkReference,
991 ImageReference,
992 Html,
993 SoftBreak,
994 LineBreak,
995 Math,
996 FootnoteReference,
997 InlineFootnote,
998 WikiLink,
999 TextDirective,
1000 Tag,
1001});
1002
1003impl_from_variants!(Inline {
1004 Text(Text), Escape(Escape), CharacterReference(CharacterReference),
1005 Emphasis(Emphasis), Strong(Strong), Delete(Delete), Mark(Mark),
1006 Shortcode(Shortcode), Code(CodeInline), Link(Link), Autolink(Autolink), Image(Image),
1007 LinkReference(LinkReference), ImageReference(ImageReference), Html(HtmlInline),
1008 SoftBreak(SoftBreak), LineBreak(LineBreak), Math(MathInline),
1009 FootnoteReference(FootnoteReference), InlineFootnote(InlineFootnote), WikiLink(WikiLink),
1010 TextDirective(TextDirective), Tag(Tag),
1011});
1012
1013impl Block {
1014 /// The blocks directly inside this block: a block quote's, an alert's, a
1015 /// footnote definition's, a container directive's, or an HTML container's
1016 /// that holds blocks; an empty slice for every other block. A list's
1017 /// blocks are in its items ([`ListItem::children`]), as [`NodePath`]
1018 /// counts them, so a walker over blocks steps into a list's items itself.
1019 pub fn children(&self) -> &[Block] {
1020 match self {
1021 Block::BlockQuote(n) => &n.children,
1022 Block::Alert(n) => &n.children,
1023 Block::FootnoteDefinition(n) => &n.children,
1024 Block::ContainerDirective(n) => &n.children,
1025 Block::HtmlContainer(n) => match &n.content {
1026 HtmlContainerContent::Blocks(blocks) => blocks,
1027 HtmlContainerContent::Inlines(_) => &[],
1028 },
1029 Block::Paragraph(_)
1030 | Block::Heading(_)
1031 | Block::ThematicBreak(_)
1032 | Block::List(_)
1033 | Block::CodeBlock(_)
1034 | Block::HtmlBlock(_)
1035 | Block::Definition(_)
1036 | Block::Table(_)
1037 | Block::MathBlock(_)
1038 | Block::Frontmatter(_)
1039 | Block::LeafDirective(_)
1040 | Block::BlankLine(_) => &[],
1041 }
1042 }
1043}
1044
1045impl Inline {
1046 /// The inline subtree of this node, or an empty slice for a leaf. Covers the
1047 /// `alt`/`label` fields uniformly, so a generic walker never silently skips
1048 /// an image's alt text or a directive's label.
1049 pub fn children(&self) -> &[Inline] {
1050 match self {
1051 Inline::Emphasis(n) => &n.children,
1052 Inline::Strong(n) => &n.children,
1053 Inline::Delete(n) => &n.children,
1054 Inline::Mark(n) => &n.children,
1055 Inline::Link(n) => &n.children,
1056 Inline::Image(n) => &n.alt,
1057 Inline::LinkReference(n) => &n.children,
1058 Inline::ImageReference(n) => &n.alt,
1059 Inline::InlineFootnote(n) => &n.children,
1060 Inline::TextDirective(n) => &n.label,
1061 Inline::Text(_)
1062 | Inline::Escape(_)
1063 | Inline::CharacterReference(_)
1064 | Inline::Shortcode(_)
1065 | Inline::Code(_)
1066 | Inline::Autolink(_)
1067 | Inline::Html(_)
1068 | Inline::SoftBreak(_)
1069 | Inline::LineBreak(_)
1070 | Inline::Math(_)
1071 | Inline::FootnoteReference(_)
1072 | Inline::WikiLink(_)
1073 | Inline::Tag(_) => &[],
1074 }
1075 }
1076}
1077
1078impl CharacterReference {
1079 /// A character reference node for `reference`, written with its `&` and
1080 /// `;` (e.g. `&`).
1081 pub fn new(reference: impl Into<String>) -> Self {
1082 Self {
1083 meta: NodeMeta::default(),
1084 reference: reference.into(),
1085 }
1086 }
1087
1088 /// The character the reference decodes to, or `None` when
1089 /// [`reference`](Self::reference) is not exactly one character reference.
1090 /// A valid document holds only references that decode.
1091 pub fn value(&self) -> Option<String> {
1092 crate::decode::decode_character_reference(&self.reference)
1093 }
1094}
1095
1096impl Autolink {
1097 /// An autolink of `form` whose text is `text`.
1098 pub fn new(form: AutolinkForm, text: impl Into<String>) -> Self {
1099 Self {
1100 meta: NodeMeta::default(),
1101 form,
1102 text: text.into(),
1103 }
1104 }
1105
1106 /// The link target the text writes, or `None` when the text is not
1107 /// exactly one autolink of its form. An angle-bracket URI links to itself
1108 /// and an email address to it after `mailto:`; a literal `www.` domain
1109 /// links to it after `http://`, a literal email address to it after
1110 /// `mailto:`, and any other literal autolink to itself. A valid document
1111 /// holds only autolinks with a destination.
1112 pub fn destination(&self) -> Option<String> {
1113 match self.form {
1114 AutolinkForm::Angle => crate::parse::angle_autolink_destination(&self.text),
1115 AutolinkForm::Literal => crate::parse::literal_autolink_destination(&self.text),
1116 }
1117 }
1118}
1119
1120impl Title {
1121 /// A title holding `value`, quoted as `kind`.
1122 pub fn new(value: impl Into<String>, kind: LinkTitleKind) -> Self {
1123 Self {
1124 value: value.into(),
1125 kind,
1126 }
1127 }
1128}
1129
1130impl Shortcode {
1131 /// The emoji the crate's pinned gemoji table gives this shortcode's
1132 /// name, or `None` for a name the table does not hold.
1133 pub fn glyph(&self) -> Option<&'static str> {
1134 crate::gemoji::glyph(&self.name)
1135 }
1136}
1137
1138impl Tag {
1139 /// A tag named `name`, written without its `#`.
1140 pub fn new(name: impl Into<String>) -> Self {
1141 Self {
1142 meta: NodeMeta::default(),
1143 name: name.into(),
1144 }
1145 }
1146}
1147
1148impl WikiLink {
1149 /// The target with its backslash escapes and character references
1150 /// decoded, as CommonMark decodes a link destination: `a \| b & c`
1151 /// decodes to `a | b & c`.
1152 pub fn decoded_target(&self) -> String {
1153 crate::decode::decode_escapes_and_references(&self.target)
1154 }
1155
1156 /// The label with its backslash escapes and character references
1157 /// decoded, as [`WikiLink::decoded_target`] decodes the target.
1158 pub fn decoded_label(&self) -> String {
1159 crate::decode::decode_escapes_and_references(&self.label)
1160 }
1161}
1162
1163impl Text {
1164 /// A text node with the given string value.
1165 pub fn new(value: impl Into<String>) -> Self {
1166 Self {
1167 meta: NodeMeta::default(),
1168 value: value.into(),
1169 }
1170 }
1171}
1172
1173impl From<&str> for Text {
1174 fn from(value: &str) -> Self {
1175 Text::new(value)
1176 }
1177}
1178
1179impl From<String> for Text {
1180 fn from(value: String) -> Self {
1181 Text::new(value)
1182 }
1183}
1184
1185impl Paragraph {
1186 /// A paragraph from any iterator of inline-convertible children.
1187 pub fn new<I, T>(children: I) -> Self
1188 where
1189 I: IntoIterator<Item = T>,
1190 T: Into<Inline>,
1191 {
1192 Self {
1193 meta: NodeMeta::default(),
1194 children: children.into_iter().map(Into::into).collect(),
1195 source: None,
1196 }
1197 }
1198
1199 /// A paragraph whose content is `text`, read as
1200 /// [`parse_inline`](crate::parse_inline) reads it with `definitions`, and
1201 /// the diagnostics of that read. The paragraph keeps `text`, which the
1202 /// writer writes in place of its inlines while they are the ones `text`
1203 /// reads as; the spans of its inlines are offsets in `text`. Pass the
1204 /// definitions of the document the paragraph will be written in
1205 /// ([`Definitions::of`]); after they change, or for a leaf built with
1206 /// others, [`Document::reread_kept_sources`] reads its text again.
1207 pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1208 let (children, diagnostics) = read_source(text, definitions);
1209 let paragraph = Self {
1210 source: Some(text.into()),
1211 ..Self::new(children)
1212 };
1213 (paragraph, diagnostics)
1214 }
1215
1216 /// The text [`Paragraph::with_source`] built the paragraph from.
1217 pub fn source(&self) -> Option<&str> {
1218 self.source.as_deref()
1219 }
1220}
1221
1222impl Heading {
1223 /// A heading of the given depth.
1224 pub fn new<I, T>(depth: u8, children: I) -> Self
1225 where
1226 I: IntoIterator<Item = T>,
1227 T: Into<Inline>,
1228 {
1229 Self {
1230 meta: NodeMeta::default(),
1231 depth,
1232 children: children.into_iter().map(Into::into).collect(),
1233 source: None,
1234 }
1235 }
1236
1237 /// A heading of the given depth whose content is `text`, built as
1238 /// [`Paragraph::with_source`] builds a paragraph.
1239 pub fn with_source(
1240 depth: u8,
1241 text: &str,
1242 definitions: &Definitions,
1243 ) -> (Self, Vec<Diagnostic>) {
1244 let (children, diagnostics) = read_source(text, definitions);
1245 let heading = Self {
1246 source: Some(text.into()),
1247 ..Self::new(depth, children)
1248 };
1249 (heading, diagnostics)
1250 }
1251
1252 /// The text [`Heading::with_source`] built the heading from.
1253 pub fn source(&self) -> Option<&str> {
1254 self.source.as_deref()
1255 }
1256}
1257
1258impl TableCell {
1259 /// A cell from any iterator of inline-convertible children.
1260 pub fn new<I, T>(children: I) -> Self
1261 where
1262 I: IntoIterator<Item = T>,
1263 T: Into<Inline>,
1264 {
1265 Self {
1266 meta: NodeMeta::default(),
1267 children: children.into_iter().map(Into::into).collect(),
1268 source: None,
1269 }
1270 }
1271
1272 /// A cell whose content is `text`, built as [`Paragraph::with_source`]
1273 /// builds a paragraph. A `|` in `text` is content: the writer escapes it.
1274 pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1275 let (children, diagnostics) = read_source(text, definitions);
1276 let cell = Self {
1277 source: Some(text.into()),
1278 ..Self::new(children)
1279 };
1280 (cell, diagnostics)
1281 }
1282
1283 /// The text [`TableCell::with_source`] built the cell from.
1284 pub fn source(&self) -> Option<&str> {
1285 self.source.as_deref()
1286 }
1287}
1288
1289impl LeafDirective {
1290 /// A leaf directive named `name`, with no label and no attributes.
1291 pub fn new(name: impl Into<String>) -> Self {
1292 Self {
1293 meta: NodeMeta::default(),
1294 name: name.into(),
1295 label: Vec::new(),
1296 attributes: Vec::new(),
1297 label_source: None,
1298 }
1299 }
1300
1301 /// A leaf directive named `name` whose label is `text`, built as
1302 /// [`Paragraph::with_source`] builds a paragraph's content.
1303 pub fn with_source(
1304 name: impl Into<String>,
1305 text: &str,
1306 definitions: &Definitions,
1307 ) -> (Self, Vec<Diagnostic>) {
1308 let (label, diagnostics) = read_source(text, definitions);
1309 let directive = Self {
1310 label,
1311 label_source: Some(text.into()),
1312 ..Self::new(name)
1313 };
1314 (directive, diagnostics)
1315 }
1316
1317 /// The text [`LeafDirective::with_source`] built the label from.
1318 pub fn label_source(&self) -> Option<&str> {
1319 self.label_source.as_deref()
1320 }
1321}
1322
1323impl ContainerDirective {
1324 /// A container directive named `name` holding `children`, with no label
1325 /// and no attributes.
1326 pub fn new(name: impl Into<String>, children: Vec<Block>) -> Self {
1327 Self {
1328 meta: NodeMeta::default(),
1329 name: name.into(),
1330 label: Vec::new(),
1331 attributes: Vec::new(),
1332 children,
1333 label_source: None,
1334 }
1335 }
1336
1337 /// A container directive named `name` holding `children`, whose label
1338 /// is `text`, built as [`Paragraph::with_source`] builds a paragraph's
1339 /// content.
1340 pub fn with_source(
1341 name: impl Into<String>,
1342 text: &str,
1343 children: Vec<Block>,
1344 definitions: &Definitions,
1345 ) -> (Self, Vec<Diagnostic>) {
1346 let (label, diagnostics) = read_source(text, definitions);
1347 let directive = Self {
1348 label,
1349 label_source: Some(text.into()),
1350 ..Self::new(name, children)
1351 };
1352 (directive, diagnostics)
1353 }
1354
1355 /// The text [`ContainerDirective::with_source`] built the label from.
1356 pub fn label_source(&self) -> Option<&str> {
1357 self.label_source.as_deref()
1358 }
1359}
1360
1361/// The inlines `text` reads as, with the diagnostics of the read.
1362fn read_source(text: &str, definitions: &Definitions) -> (Vec<Inline>, Vec<Diagnostic>) {
1363 let output = crate::parse::parse_inline_tree(text, definitions);
1364 (output.inlines, output.diagnostics)
1365}
1366
1367impl Link {
1368 /// An inline, bare-destination link with no title.
1369 pub fn new<I, T>(destination: impl Into<String>, children: I) -> Self
1370 where
1371 I: IntoIterator<Item = T>,
1372 T: Into<Inline>,
1373 {
1374 Self {
1375 meta: NodeMeta::default(),
1376 destination: destination.into(),
1377 destination_kind: LinkDestinationKind::Bare,
1378 title: None,
1379 children: children.into_iter().map(Into::into).collect(),
1380 }
1381 }
1382}
1383
1384impl CodeInline {
1385 /// An inline code span holding `value`.
1386 pub fn new(value: impl Into<String>) -> Self {
1387 Self {
1388 meta: NodeMeta::default(),
1389 value: value.into(),
1390 }
1391 }
1392}
1393
1394impl List {
1395 /// A tight, dash-delimited bullet list.
1396 pub fn new<I>(children: I) -> Self
1397 where
1398 I: IntoIterator<Item = ListItem>,
1399 {
1400 Self {
1401 meta: NodeMeta::default(),
1402 ordered: false,
1403 delimiter: ListDelimiter::Dash,
1404 tight: true,
1405 children: children.into_iter().collect(),
1406 }
1407 }
1408}
1409
1410impl ListItem {
1411 /// An item holding `children`, with no task marker and no number.
1412 pub fn new<I, T>(children: I) -> Self
1413 where
1414 I: IntoIterator<Item = T>,
1415 T: Into<Block>,
1416 {
1417 Self {
1418 meta: NodeMeta::default(),
1419 checked: None,
1420 number: None,
1421 children: children.into_iter().map(Into::into).collect(),
1422 }
1423 }
1424}
1425
1426impl BlankLine {
1427 /// A blank line.
1428 pub fn new() -> Self {
1429 Self::default()
1430 }
1431}