Skip to main content

asciidoc_parser/blocks/
block.rs

1use std::slice::Iter;
2
3use crate::{
4    HasSpan, Parser, Span,
5    attributes::Attrlist,
6    blocks::{
7        AdmonitionBlock, Break, CompoundDelimitedBlock, ContentModel, IsBlock, ListBlock, ListItem,
8        ListItemMarker, MediaBlock, Preamble, QuoteBlock, RawDelimitedBlock, SectionBlock,
9        SimpleBlock, TableBlock, media::TargetResolution, metadata::BlockMetadata,
10        starts_with_admonition_label,
11    },
12    content::{Content, SubstitutionGroup},
13    document::{Attribute, RefType},
14    parser::{InlineSubstitutionRenderer, ReferenceResolver, ReferenceWarning},
15    span::MatchedItem,
16    strings::CowStr,
17    warnings::{MatchAndWarnings, Warning, WarningType},
18};
19
20/// **Block elements** form the main structure of an AsciiDoc document, starting
21/// with the document itself.
22///
23/// A block element (aka **block**) is a discrete, line-oriented chunk of
24/// content in an AsciiDoc document. Once parsed, that chunk of content becomes
25/// a block element in the parsed document model. Certain blocks may contain
26/// other blocks, so we say that blocks can be nested. The converter visits each
27/// block in turn, in document order, converting it to a corresponding chunk of
28/// output.
29///
30/// This enum represents all of the block types that are understood directly by
31/// this parser and also implements the [`IsBlock`] trait.
32#[derive(Clone, Eq, PartialEq)]
33#[allow(clippy::large_enum_variant)] // TEMPORARY: review later
34#[non_exhaustive]
35pub enum Block<'src> {
36    /// A block that’s treated as contiguous lines of paragraph text (and
37    /// subject to normal substitutions) (e.g., a paragraph block).
38    Simple(SimpleBlock<'src>),
39
40    /// A media block is used to represent an image, video, or audio block
41    /// macro.
42    Media(MediaBlock<'src>),
43
44    /// A section helps to partition the document into a content hierarchy.
45    /// May also be a part, chapter, or special section.
46    Section(SectionBlock<'src>),
47
48    /// A list contains a sequence of items prefixed with symbol, such as a disc
49    /// (aka bullet). Each individual item in the list is represented by a
50    /// [`ListItem`].
51    List(ListBlock<'src>),
52
53    /// A list item is a special kind of block that is a member of a
54    /// [`ListBlock`] and contains one or more blocks attached to it.
55    ListItem(ListItem<'src>),
56
57    /// A delimited block that contains verbatim, raw, or comment text. The
58    /// content between the matching delimiters is not parsed for block
59    /// syntax.
60    RawDelimited(RawDelimitedBlock<'src>),
61
62    /// A delimited block that can contain other blocks.
63    CompoundDelimited(CompoundDelimitedBlock<'src>),
64
65    /// An admonition draws attention to a statement by taking it out of the
66    /// content's flow and labeling it with a priority (e.g., a note or a
67    /// warning).
68    Admonition(AdmonitionBlock<'src>),
69
70    /// A blockquote: a quote, prose excerpt, or verse, optionally attributed to
71    /// a person and a source citation.
72    Quote(QuoteBlock<'src>),
73
74    /// A table block arranges content into a grid of rows and columns.
75    Table(TableBlock<'src>),
76
77    /// Content between the end of the document header and the first section
78    /// title in the document body is called the preamble.
79    Preamble(Preamble<'src>),
80
81    /// A thematic or page break.
82    Break(Break<'src>),
83
84    /// When an attribute is defined in the document body using an attribute
85    /// entry, that’s simply referred to as a document attribute.
86    DocumentAttribute(Attribute<'src>),
87}
88
89impl<'src> std::fmt::Debug for Block<'src> {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Block::Simple(block) => f.debug_tuple("Block::Simple").field(block).finish(),
93            Block::Media(block) => f.debug_tuple("Block::Media").field(block).finish(),
94            Block::Section(block) => f.debug_tuple("Block::Section").field(block).finish(),
95            Block::List(block) => f.debug_tuple("Block::List").field(block).finish(),
96            Block::ListItem(block) => f.debug_tuple("Block::ListItem").field(block).finish(),
97
98            Block::RawDelimited(block) => {
99                f.debug_tuple("Block::RawDelimited").field(block).finish()
100            }
101
102            Block::CompoundDelimited(block) => f
103                .debug_tuple("Block::CompoundDelimited")
104                .field(block)
105                .finish(),
106
107            Block::Admonition(block) => f.debug_tuple("Block::Admonition").field(block).finish(),
108            Block::Quote(block) => f.debug_tuple("Block::Quote").field(block).finish(),
109            Block::Table(block) => f.debug_tuple("Block::Table").field(block).finish(),
110            Block::Preamble(block) => f.debug_tuple("Block::Preamble").field(block).finish(),
111            Block::Break(break_) => f.debug_tuple("Block::Break").field(break_).finish(),
112
113            Block::DocumentAttribute(block) => f
114                .debug_tuple("Block::DocumentAttribute")
115                .field(block)
116                .finish(),
117        }
118    }
119}
120
121/// Outcome of attempting to parse a single [`Block`].
122///
123/// Most blocks parse to [`Parsed`](Self::Parsed). [`Dropped`](Self::Dropped)
124/// supports `attribute-missing=drop-line`: when a block-macro target references
125/// a missing attribute, Asciidoctor discards the whole block, which the parser
126/// must distinguish both from a successful parse and from "no block matched"
127/// (so the block-collection loops advance past the dropped source rather than
128/// spinning or mis-parsing it).
129// `Parsed` embeds a `Block`, which is itself a large enum (see the matching
130// allow on `Block`). This outcome is short-lived and returned by value on the
131// hot parse path, so boxing it would just trade the size for an allocation.
132#[allow(clippy::large_enum_variant)]
133pub(crate) enum BlockParseOutcome<'src> {
134    /// A block was parsed.
135    Parsed(MatchedItem<'src, Block<'src>>),
136
137    /// The input was recognized as a block macro but dropped at parse time
138    /// because its target referenced a missing attribute under
139    /// `attribute-missing=drop-line`. The contained span is where parsing
140    /// should resume (the dropped block's `after`).
141    Dropped(Span<'src>),
142
143    /// No block matched. This happens only for empty or all-blank input.
144    NoMatch,
145}
146
147impl<'src> Block<'src> {
148    /// Parse a block of any type and return a `Block` that describes it.
149    ///
150    /// Consumes any blank lines before and after the block.
151    ///
152    /// This is a test-only convenience wrapper over
153    /// [`parse_with_outcome`](Self::parse_with_outcome) that flattens the
154    /// drop-line outcome to an `Option`; production code uses
155    /// `parse_with_outcome` so it can react to a dropped block.
156    #[cfg(test)]
157    pub(crate) fn parse(
158        source: Span<'src>,
159        parser: &mut Parser,
160    ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
161        let MatchAndWarnings { item, warnings } = Self::parse_internal(source, parser, None, false);
162
163        MatchAndWarnings {
164            item: match item {
165                BlockParseOutcome::Parsed(mi) => Some(mi),
166                BlockParseOutcome::Dropped(_) | BlockParseOutcome::NoMatch => None,
167            },
168            warnings,
169        }
170    }
171
172    /// Parse a block of any type, returning the full [`BlockParseOutcome`] so a
173    /// block-collection loop can advance past a block that was dropped at parse
174    /// time (`attribute-missing=drop-line`). Consumes any blank lines before
175    /// and after the block.
176    ///
177    /// This is the entry point used by production block-collection loops.
178    pub(crate) fn parse_with_outcome(
179        source: Span<'src>,
180        parser: &mut Parser,
181    ) -> MatchAndWarnings<'src, BlockParseOutcome<'src>> {
182        Self::parse_internal(source, parser, None, false)
183    }
184
185    /// Parse a block of any type and return a `Block` that describes it.
186    ///
187    /// Will terminate early when parsing certain block types within a list
188    /// context.
189    ///
190    /// Consumes any blank lines before and after the block.
191    ///
192    /// If `is_continuation` is true, this content was attached via a `+`
193    /// continuation marker and literal blocks should preserve their
194    /// indentation.
195    pub(crate) fn parse_for_list_item(
196        source: Span<'src>,
197        parser: &mut Parser,
198        parent_list_markers: &[ListItemMarker<'src>],
199        is_continuation: bool,
200    ) -> MatchAndWarnings<'src, BlockParseOutcome<'src>> {
201        Self::parse_internal(source, parser, Some(parent_list_markers), is_continuation)
202    }
203
204    /// Shared parser for [`parse_with_outcome`](Self::parse_with_outcome) and
205    /// [`parse_for_list_item`](Self::parse_for_list_item).
206    fn parse_internal(
207        source: Span<'src>,
208        parser: &mut Parser,
209        parent_list_markers: Option<&[ListItemMarker<'src>]>,
210        is_continuation: bool,
211    ) -> MatchAndWarnings<'src, BlockParseOutcome<'src>> {
212        // Optimization: If the first line doesn't match any of the early indications
213        // for delimited blocks, titles, or attrlists, we can skip directly to treating
214        // this as a simple block. That saves quite a bit of parsing time.
215        let first_line = source.take_line().item.discard_whitespace();
216
217        // If it does contain any of those markers, we fall through to the more costly
218        // tests below which can more accurately classify the upcoming block.
219        if let Some(first_char) = first_line.chars().next()
220            && !matches!(
221                first_char,
222                '.' | '#'
223                    | '='
224                    | '/'
225                    | '-'
226                    | '+'
227                    | '*'
228                    | '_'
229                    | '['
230                    | ':'
231                    | '\''
232                    | '<'
233                    | '>'
234                    | '"'
235                    | '•'
236            )
237            && !first_line.contains("::")
238            && !first_line.contains(";;")
239            && !TableBlock::is_table_delimiter(&first_line)
240            && !ListItemMarker::starts_with_marker(first_line)
241            && !starts_with_admonition_label(first_line)
242            && parent_list_markers.is_none()
243            && let Some(MatchedItem {
244                item: simple_block,
245                after,
246            }) = SimpleBlock::parse_fast(source, parser)
247        {
248            let mut warnings = vec![];
249            let block = Self::Simple(simple_block);
250
251            Self::register_block_id(
252                block.id(),
253                Self::block_reftext(&block),
254                block.span(),
255                parser,
256                &mut warnings,
257            );
258
259            return MatchAndWarnings {
260                item: BlockParseOutcome::Parsed(MatchedItem { item: block, after }),
261                warnings,
262            };
263        }
264
265        // Look for document attributes first since these don't support block metadata.
266        if first_line.starts_with(':')
267            && (first_line.ends_with(':') || first_line.contains(": "))
268            && let Some(attr) = Attribute::parse(source, parser)
269        {
270            let mut warnings: Vec<Warning<'src>> = vec![];
271            parser.set_attribute_from_body(&attr.item, &mut warnings);
272
273            return MatchAndWarnings {
274                item: BlockParseOutcome::Parsed(MatchedItem {
275                    item: Self::DocumentAttribute(attr.item),
276                    after: attr.after,
277                }),
278                warnings,
279            };
280        }
281
282        // Optimization not possible; start by looking for block metadata (title,
283        // attrlist, etc.).
284        let MatchAndWarnings {
285            item: mut metadata,
286            mut warnings,
287        } = BlockMetadata::parse(source, parser);
288
289        // The `[literal]` block style normally marks a literal *paragraph*,
290        // which is handled directly as a simple (literal) block below, bypassing
291        // the delimited-block parsers. The one exception is when it is set on an
292        // open block (`--`), where it masquerades as a verbatim literal block;
293        // that case must fall through to `RawDelimitedBlock`.
294        let is_literal = metadata.attrlist.as_ref().and_then(|a| a.block_style())
295            == Some("literal")
296            && metadata.block_start.take_normalized_line().item.data() != "--";
297
298        // A simple block may be parsed speculatively inside the `!is_literal`
299        // branch below (to detect the "metadata with no block" edge case). When
300        // that speculative parse succeeds it is reused as the final result rather
301        // than re-parsed, so that the captioning side effect of
302        // `SimpleBlock::parse` (which can consume a caption counter) happens at
303        // most once per block.
304        let mut simple_block_mi = None;
305
306        if !is_literal {
307            if let Some(mut adm_maw) = AdmonitionBlock::parse(&metadata, parser)
308                && let Some(adm) = adm_maw.item
309            {
310                if !adm_maw.warnings.is_empty() {
311                    warnings.append(&mut adm_maw.warnings);
312                }
313
314                let block = Self::Admonition(adm.item);
315
316                Self::register_block_id(
317                    block.id(),
318                    Self::block_reftext(&block),
319                    block.span(),
320                    parser,
321                    &mut warnings,
322                );
323
324                return MatchAndWarnings {
325                    item: BlockParseOutcome::Parsed(MatchedItem {
326                        item: block,
327                        after: adm.after,
328                    }),
329                    warnings,
330                };
331            }
332
333            if let Some(mut quote_maw) = QuoteBlock::parse(&metadata, parser)
334                && let Some(quote) = quote_maw.item
335            {
336                if !quote_maw.warnings.is_empty() {
337                    warnings.append(&mut quote_maw.warnings);
338                }
339
340                let block = Self::Quote(quote.item);
341
342                Self::register_block_id(
343                    block.id(),
344                    Self::block_reftext(&block),
345                    block.span(),
346                    parser,
347                    &mut warnings,
348                );
349
350                return MatchAndWarnings {
351                    item: BlockParseOutcome::Parsed(MatchedItem {
352                        item: block,
353                        after: quote.after,
354                    }),
355                    warnings,
356                };
357            }
358
359            if let Some(mut rdb_maw) = RawDelimitedBlock::parse(&metadata, parser)
360                && let Some(rdb) = rdb_maw.item
361            {
362                if !rdb_maw.warnings.is_empty() {
363                    warnings.append(&mut rdb_maw.warnings);
364                }
365
366                let block = Self::RawDelimited(rdb.item);
367
368                Self::register_block_id(
369                    block.id(),
370                    Self::block_reftext(&block),
371                    block.span(),
372                    parser,
373                    &mut warnings,
374                );
375
376                return MatchAndWarnings {
377                    item: BlockParseOutcome::Parsed(MatchedItem {
378                        item: block,
379                        after: rdb.after,
380                    }),
381                    warnings,
382                };
383            }
384
385            if let Some(mut cdb_maw) = CompoundDelimitedBlock::parse(&metadata, parser)
386                && let Some(cdb) = cdb_maw.item
387            {
388                if !cdb_maw.warnings.is_empty() {
389                    warnings.append(&mut cdb_maw.warnings);
390                }
391
392                let block = Self::CompoundDelimited(cdb.item);
393
394                Self::register_block_id(
395                    block.id(),
396                    Self::block_reftext(&block),
397                    block.span(),
398                    parser,
399                    &mut warnings,
400                );
401
402                return MatchAndWarnings {
403                    item: BlockParseOutcome::Parsed(MatchedItem {
404                        item: block,
405                        after: cdb.after,
406                    }),
407                    warnings,
408                };
409            }
410
411            if let Some(mut table_maw) = TableBlock::parse(&metadata, parser)
412                && let Some(table) = table_maw.item
413            {
414                if !table_maw.warnings.is_empty() {
415                    warnings.append(&mut table_maw.warnings);
416                }
417
418                let block = Self::Table(table.item);
419
420                Self::register_block_id(
421                    block.id(),
422                    Self::block_reftext(&block),
423                    block.span(),
424                    parser,
425                    &mut warnings,
426                );
427
428                return MatchAndWarnings {
429                    item: BlockParseOutcome::Parsed(MatchedItem {
430                        item: block,
431                        after: table.after,
432                    }),
433                    warnings,
434                };
435            }
436
437            // Try to discern the block type by scanning the first line.
438            let line = metadata.block_start.take_normalized_line();
439
440            if line.item.starts_with("image::")
441                || line.item.starts_with("video::")
442                || line.item.starts_with("audio::")
443            {
444                let mut media_block_maw = MediaBlock::parse(&metadata, parser);
445
446                if let Some(mut media_block) = media_block_maw.item {
447                    // Only propagate warnings from media block parsing if we think this
448                    // *is* a media block. Otherwise, there would likely be too many false
449                    // positives.
450                    if !media_block_maw.warnings.is_empty() {
451                        warnings.append(&mut media_block_maw.warnings);
452                    }
453
454                    // Resolve attribute references in the macro target. Under
455                    // `attribute-missing=drop-line`, a reference to a missing
456                    // attribute drops the entire block (Asciidoctor behavior).
457                    if media_block.item.resolve_target(parser) == TargetResolution::Drop {
458                        return MatchAndWarnings {
459                            item: BlockParseOutcome::Dropped(media_block.after),
460                            warnings,
461                        };
462                    }
463
464                    let block = Self::Media(media_block.item);
465
466                    Self::register_block_id(
467                        block.id(),
468                        Self::block_reftext(&block),
469                        block.span(),
470                        parser,
471                        &mut warnings,
472                    );
473
474                    return MatchAndWarnings {
475                        item: BlockParseOutcome::Parsed(MatchedItem {
476                            item: block,
477                            after: media_block.after,
478                        }),
479                        warnings,
480                    };
481                }
482
483                // This might be some other kind of block, so we don't
484                // automatically error out on a parse failure.
485            }
486
487            if (line.item.starts_with('=') || line.item.starts_with('#'))
488                && let Some(mi_section_block) =
489                    SectionBlock::parse(&metadata, parser, &mut warnings)
490            {
491                // A line starting with `=` or `#` might be some other kind of block, so we
492                // continue quietly if `SectionBlock` parser rejects this block.
493
494                return MatchAndWarnings {
495                    item: BlockParseOutcome::Parsed(MatchedItem {
496                        item: Self::Section(mi_section_block.item),
497                        after: mi_section_block.after,
498                    }),
499                    warnings,
500                };
501            }
502
503            if (line.item.starts_with('\'')
504                || line.item.starts_with('-')
505                || line.item.starts_with('*')
506                || line.item.starts_with('<'))
507                && let Some(mi_break) = Break::parse(&metadata, parser)
508            {
509                // Continue quietly if `Break` parser rejects this block.
510
511                return MatchAndWarnings {
512                    item: BlockParseOutcome::Parsed(MatchedItem {
513                        item: Self::Break(mi_break.item),
514                        after: mi_break.after,
515                    }),
516                    warnings,
517                };
518            }
519
520            // Only try to parse as a new list if we're NOT inside a list item context.
521            // If we are inside a list context, lists can only be created when the first
522            // line is a list item marker (handled above).
523            if parent_list_markers.is_none()
524                && let Some(mi_list) = ListBlock::parse(&metadata, parser, &mut warnings)
525            {
526                return MatchAndWarnings {
527                    item: BlockParseOutcome::Parsed(MatchedItem {
528                        item: Self::List(mi_list.item),
529                        after: mi_list.after,
530                    }),
531                    warnings,
532                };
533            }
534
535            // First, let's look for a fun edge case. Perhaps the text contains block
536            // metadata but no block immediately following. If we're not careful, we could
537            // spin in a loop (for example, `parse_blocks_until`) thinking there will be
538            // another block, but there isn't.
539
540            // The following check disables that spin loop.
541            simple_block_mi = if let Some(plm) = parent_list_markers {
542                SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
543            } else {
544                SimpleBlock::parse(&metadata, parser)
545            };
546
547            if simple_block_mi.is_none() && !metadata.is_empty() {
548                // We have a metadata with no block. Treat it as a simple block but issue a
549                // warning.
550
551                warnings.push(Warning {
552                    source: metadata.source,
553                    warning: WarningType::MissingBlockAfterTitleOrAttributeList,
554                });
555
556                // Remove the metadata content so that SimpleBlock will read the title/attrlist
557                // line(s) as regular content. The speculative parse failed, so the
558                // block is re-parsed below with this stripped metadata.
559                metadata.title_source = None;
560                metadata.title = None;
561                metadata.anchor = None;
562                metadata.attrlist = None;
563                metadata.block_start = metadata.source;
564            }
565        }
566
567        // If no other block kind matches, we can always use SimpleBlock. Reuse the
568        // speculative parse from the `!is_literal` branch when it succeeded;
569        // otherwise (a literal block, or metadata stripped above) parse now.
570        let simple_block_mi = match simple_block_mi {
571            Some(mi) => Some(mi),
572            None => {
573                if let Some(plm) = parent_list_markers {
574                    SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
575                } else {
576                    SimpleBlock::parse(&metadata, parser)
577                }
578            }
579        };
580
581        let mut result = MatchAndWarnings {
582            item: match simple_block_mi {
583                Some(mi) => BlockParseOutcome::Parsed(MatchedItem {
584                    item: Self::Simple(mi.item),
585                    after: mi.after,
586                }),
587                None => BlockParseOutcome::NoMatch,
588            },
589            warnings,
590        };
591
592        if let BlockParseOutcome::Parsed(ref matched_item) = result.item {
593            Self::register_block_id(
594                matched_item.item.id(),
595                Self::block_reftext(&matched_item.item),
596                matched_item.item.span(),
597                parser,
598                &mut result.warnings,
599            );
600        }
601
602        result
603    }
604
605    /// Determine the reftext (a.k.a. xreflabel) used as the link text when a
606    /// block is the target of a cross reference. Asciidoctor's precedence is:
607    /// an explicit `reftext` attribute, then the reftext supplied with a
608    /// block anchor (`[[id,reftext]]`), and finally the block title.
609    fn block_reftext<'a>(block: &'a Block<'a>) -> Option<&'a str> {
610        block
611            .attrlist()
612            .and_then(|attrlist| attrlist.named_attribute("reftext"))
613            .map(|attr| attr.value())
614            .or_else(|| block.anchor_reftext().map(|span| span.data()))
615            .or_else(|| block.title())
616    }
617
618    /// Register a block's ID with the catalog if the block has an ID.
619    ///
620    /// This should be called for all block types except `SectionBlock`,
621    /// which handles its own catalog registration.
622    fn register_block_id(
623        id: Option<&str>,
624        reftext: Option<&str>,
625        span: Span<'src>,
626        parser: &mut Parser,
627        warnings: &mut Vec<Warning<'src>>,
628    ) {
629        if let Some(id) = id
630            && let Err(_duplicate_error) = parser.register_ref(id, reftext, RefType::Anchor)
631        {
632            // If registration fails due to duplicate ID, issue a warning.
633            warnings.push(Warning {
634                source: span,
635                warning: WarningType::DuplicateId(id.to_string()),
636            });
637        }
638    }
639
640    /// Returns a reference to the inner [`ListItem`] if this is a
641    /// `Block::ListItem`, or `None` otherwise.
642    pub(crate) fn as_list_item(&self) -> Option<&ListItem<'src>> {
643        match self {
644            Self::ListItem(li) => Some(li),
645            _ => None,
646        }
647    }
648
649    /// Resolve any deferred cross-references in this block and its descendants,
650    /// using `resolver` to map targets to destinations and `renderer` to render
651    /// the resulting links. Unresolved targets are reported in `warnings`.
652    ///
653    /// This drives the recursion uniformly via the [`IsBlock::content_mut`] and
654    /// [`IsBlock::nested_blocks_mut`] accessors, so it needs no per-block-type
655    /// special casing.
656    pub(crate) fn resolve_references(
657        &mut self,
658        resolver: &dyn ReferenceResolver,
659        renderer: &dyn InlineSubstitutionRenderer,
660        warnings: &mut Vec<ReferenceWarning>,
661    ) {
662        if let Some(content) = self.content_mut() {
663            content.resolve_references(resolver, renderer, warnings);
664        }
665
666        // Tables hold their resolvable content in cells rather than in a single
667        // `content_mut()` value, so they are resolved explicitly here.
668        if let Self::Table(table) = self {
669            table.resolve_references(resolver, renderer, warnings);
670        }
671
672        // A Markdown-style blockquote holds its nested blocks in its own owned
673        // source, which the generic `nested_blocks_mut()` walk below does not
674        // reach, so they are resolved explicitly here.
675        if let Self::Quote(quote) = self {
676            quote.resolve_references(resolver, renderer, warnings);
677        }
678
679        for child in self.nested_blocks_mut() {
680            child.resolve_references(resolver, renderer, warnings);
681        }
682    }
683}
684
685impl<'src> IsBlock<'src> for Block<'src> {
686    fn content_model(&self) -> ContentModel {
687        match self {
688            Self::Simple(_) => ContentModel::Simple,
689            Self::Media(b) => b.content_model(),
690            Self::Section(_) => ContentModel::Compound,
691            Self::List(b) => b.content_model(),
692            Self::ListItem(b) => b.content_model(),
693            Self::RawDelimited(b) => b.content_model(),
694            Self::CompoundDelimited(b) => b.content_model(),
695            Self::Admonition(b) => b.content_model(),
696            Self::Quote(b) => b.content_model(),
697            Self::Table(b) => b.content_model(),
698            Self::Preamble(b) => b.content_model(),
699            Self::Break(b) => b.content_model(),
700            Self::DocumentAttribute(b) => b.content_model(),
701        }
702    }
703
704    fn declared_style(&'src self) -> Option<&'src str> {
705        match self {
706            Self::Simple(b) => b.declared_style(),
707            Self::Media(b) => b.declared_style(),
708            Self::Section(b) => b.declared_style(),
709            Self::List(b) => b.declared_style(),
710            Self::ListItem(b) => b.declared_style(),
711            Self::RawDelimited(b) => b.declared_style(),
712            Self::CompoundDelimited(b) => b.declared_style(),
713            Self::Admonition(b) => b.declared_style(),
714            Self::Quote(b) => b.declared_style(),
715            Self::Table(b) => b.declared_style(),
716            Self::Preamble(b) => b.declared_style(),
717            Self::Break(b) => b.declared_style(),
718            Self::DocumentAttribute(b) => b.declared_style(),
719        }
720    }
721
722    fn rendered_content(&'src self) -> Option<&'src str> {
723        match self {
724            Self::Simple(b) => b.rendered_content(),
725            Self::Media(b) => b.rendered_content(),
726            Self::Section(b) => b.rendered_content(),
727            Self::List(b) => b.rendered_content(),
728            Self::ListItem(b) => b.rendered_content(),
729            Self::RawDelimited(b) => b.rendered_content(),
730            Self::CompoundDelimited(b) => b.rendered_content(),
731            Self::Admonition(b) => b.rendered_content(),
732            Self::Quote(b) => b.rendered_content(),
733            Self::Table(b) => b.rendered_content(),
734            Self::Preamble(b) => b.rendered_content(),
735            Self::Break(b) => b.rendered_content(),
736            Self::DocumentAttribute(b) => b.rendered_content(),
737        }
738    }
739
740    fn raw_context(&self) -> CowStr<'src> {
741        match self {
742            Self::Simple(b) => b.raw_context(),
743            Self::Media(b) => b.raw_context(),
744            Self::Section(b) => b.raw_context(),
745            Self::List(b) => b.raw_context(),
746            Self::ListItem(b) => b.raw_context(),
747            Self::RawDelimited(b) => b.raw_context(),
748            Self::CompoundDelimited(b) => b.raw_context(),
749            Self::Admonition(b) => b.raw_context(),
750            Self::Quote(b) => b.raw_context(),
751            Self::Table(b) => b.raw_context(),
752            Self::Preamble(b) => b.raw_context(),
753            Self::Break(b) => b.raw_context(),
754            Self::DocumentAttribute(b) => b.raw_context(),
755        }
756    }
757
758    fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
759        match self {
760            Self::Simple(b) => b.nested_blocks(),
761            Self::Media(b) => b.nested_blocks(),
762            Self::Section(b) => b.nested_blocks(),
763            Self::List(b) => b.nested_blocks(),
764            Self::ListItem(b) => b.nested_blocks(),
765            Self::RawDelimited(b) => b.nested_blocks(),
766            Self::CompoundDelimited(b) => b.nested_blocks(),
767            Self::Admonition(b) => b.nested_blocks(),
768            Self::Quote(b) => b.nested_blocks(),
769            Self::Table(b) => b.nested_blocks(),
770            Self::Preamble(b) => b.nested_blocks(),
771            Self::Break(b) => b.nested_blocks(),
772            Self::DocumentAttribute(b) => b.nested_blocks(),
773        }
774    }
775
776    fn nested_blocks_mut(&mut self) -> &mut [Block<'src>] {
777        match self {
778            Self::Simple(b) => b.nested_blocks_mut(),
779            Self::Media(b) => b.nested_blocks_mut(),
780            Self::Section(b) => b.nested_blocks_mut(),
781            Self::List(b) => b.nested_blocks_mut(),
782            Self::ListItem(b) => b.nested_blocks_mut(),
783            Self::RawDelimited(b) => b.nested_blocks_mut(),
784            Self::CompoundDelimited(b) => b.nested_blocks_mut(),
785            Self::Admonition(b) => b.nested_blocks_mut(),
786            Self::Quote(b) => b.nested_blocks_mut(),
787            Self::Table(b) => b.nested_blocks_mut(),
788            Self::Preamble(b) => b.nested_blocks_mut(),
789            Self::Break(b) => b.nested_blocks_mut(),
790            Self::DocumentAttribute(b) => b.nested_blocks_mut(),
791        }
792    }
793
794    fn content_mut(&mut self) -> Option<&mut Content<'src>> {
795        match self {
796            Self::Simple(b) => b.content_mut(),
797            Self::Media(b) => b.content_mut(),
798            Self::Section(b) => b.content_mut(),
799            Self::List(b) => b.content_mut(),
800            Self::ListItem(b) => b.content_mut(),
801            Self::RawDelimited(b) => b.content_mut(),
802            Self::CompoundDelimited(b) => b.content_mut(),
803            Self::Admonition(b) => b.content_mut(),
804            Self::Quote(b) => b.content_mut(),
805            Self::Table(b) => b.content_mut(),
806            Self::Preamble(b) => b.content_mut(),
807            Self::Break(b) => b.content_mut(),
808            Self::DocumentAttribute(b) => b.content_mut(),
809        }
810    }
811
812    fn title_source(&'src self) -> Option<Span<'src>> {
813        match self {
814            Self::Simple(b) => b.title_source(),
815            Self::Media(b) => b.title_source(),
816            Self::Section(b) => b.title_source(),
817            Self::List(b) => b.title_source(),
818            Self::ListItem(b) => b.title_source(),
819            Self::RawDelimited(b) => b.title_source(),
820            Self::CompoundDelimited(b) => b.title_source(),
821            Self::Admonition(b) => b.title_source(),
822            Self::Quote(b) => b.title_source(),
823            Self::Table(b) => b.title_source(),
824            Self::Preamble(b) => b.title_source(),
825            Self::Break(b) => b.title_source(),
826            Self::DocumentAttribute(b) => b.title_source(),
827        }
828    }
829
830    fn title(&self) -> Option<&str> {
831        match self {
832            Self::Simple(b) => b.title(),
833            Self::Media(b) => b.title(),
834            Self::Section(b) => b.title(),
835            Self::List(b) => b.title(),
836            Self::ListItem(b) => b.title(),
837            Self::RawDelimited(b) => b.title(),
838            Self::CompoundDelimited(b) => b.title(),
839            Self::Admonition(b) => b.title(),
840            Self::Quote(b) => b.title(),
841            Self::Table(b) => b.title(),
842            Self::Preamble(b) => b.title(),
843            Self::Break(b) => b.title(),
844            Self::DocumentAttribute(b) => b.title(),
845        }
846    }
847
848    fn caption(&self) -> Option<&str> {
849        match self {
850            Self::Simple(b) => b.caption(),
851            Self::Media(b) => b.caption(),
852            Self::Section(b) => b.caption(),
853            Self::List(b) => b.caption(),
854            Self::ListItem(b) => b.caption(),
855            Self::RawDelimited(b) => b.caption(),
856            Self::CompoundDelimited(b) => b.caption(),
857            Self::Admonition(b) => b.caption(),
858            Self::Quote(b) => b.caption(),
859            Self::Table(b) => b.caption(),
860            Self::Preamble(b) => b.caption(),
861            Self::Break(b) => b.caption(),
862            Self::DocumentAttribute(b) => b.caption(),
863        }
864    }
865
866    fn number(&self) -> Option<usize> {
867        match self {
868            Self::Simple(b) => b.number(),
869            Self::Media(b) => b.number(),
870            Self::Section(b) => b.number(),
871            Self::List(b) => b.number(),
872            Self::ListItem(b) => b.number(),
873            Self::RawDelimited(b) => b.number(),
874            Self::CompoundDelimited(b) => b.number(),
875            Self::Admonition(b) => b.number(),
876            Self::Quote(b) => b.number(),
877            Self::Table(b) => b.number(),
878            Self::Preamble(b) => b.number(),
879            Self::Break(b) => b.number(),
880            Self::DocumentAttribute(b) => b.number(),
881        }
882    }
883
884    fn anchor(&'src self) -> Option<Span<'src>> {
885        match self {
886            Self::Simple(b) => b.anchor(),
887            Self::Media(b) => b.anchor(),
888            Self::Section(b) => b.anchor(),
889            Self::List(b) => b.anchor(),
890            Self::ListItem(b) => b.anchor(),
891            Self::RawDelimited(b) => b.anchor(),
892            Self::CompoundDelimited(b) => b.anchor(),
893            Self::Admonition(b) => b.anchor(),
894            Self::Quote(b) => b.anchor(),
895            Self::Table(b) => b.anchor(),
896            Self::Preamble(b) => b.anchor(),
897            Self::Break(b) => b.anchor(),
898            Self::DocumentAttribute(b) => b.anchor(),
899        }
900    }
901
902    fn anchor_reftext(&'src self) -> Option<Span<'src>> {
903        match self {
904            Self::Simple(b) => b.anchor_reftext(),
905            Self::Media(b) => b.anchor_reftext(),
906            Self::Section(b) => b.anchor_reftext(),
907            Self::List(b) => b.anchor_reftext(),
908            Self::ListItem(b) => b.anchor_reftext(),
909            Self::RawDelimited(b) => b.anchor_reftext(),
910            Self::CompoundDelimited(b) => b.anchor_reftext(),
911            Self::Admonition(b) => b.anchor_reftext(),
912            Self::Quote(b) => b.anchor_reftext(),
913            Self::Table(b) => b.anchor_reftext(),
914            Self::Preamble(b) => b.anchor_reftext(),
915            Self::Break(b) => b.anchor_reftext(),
916            Self::DocumentAttribute(b) => b.anchor_reftext(),
917        }
918    }
919
920    fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
921        match self {
922            Self::Simple(b) => b.attrlist(),
923            Self::Media(b) => b.attrlist(),
924            Self::Section(b) => b.attrlist(),
925            Self::List(b) => b.attrlist(),
926            Self::ListItem(b) => b.attrlist(),
927            Self::RawDelimited(b) => b.attrlist(),
928            Self::CompoundDelimited(b) => b.attrlist(),
929            Self::Admonition(b) => b.attrlist(),
930            Self::Quote(b) => b.attrlist(),
931            Self::Table(b) => b.attrlist(),
932            Self::Preamble(b) => b.attrlist(),
933            Self::Break(b) => b.attrlist(),
934            Self::DocumentAttribute(b) => b.attrlist(),
935        }
936    }
937
938    fn substitution_group(&self) -> SubstitutionGroup {
939        match self {
940            Self::Simple(b) => b.substitution_group(),
941            Self::Media(b) => b.substitution_group(),
942            Self::Section(b) => b.substitution_group(),
943            Self::List(b) => b.substitution_group(),
944            Self::ListItem(b) => b.substitution_group(),
945            Self::RawDelimited(b) => b.substitution_group(),
946            Self::CompoundDelimited(b) => b.substitution_group(),
947            Self::Admonition(b) => b.substitution_group(),
948            Self::Quote(b) => b.substitution_group(),
949            Self::Table(b) => b.substitution_group(),
950            Self::Preamble(b) => b.substitution_group(),
951            Self::Break(b) => b.substitution_group(),
952            Self::DocumentAttribute(b) => b.substitution_group(),
953        }
954    }
955}
956
957impl<'src> HasSpan<'src> for Block<'src> {
958    fn span(&self) -> Span<'src> {
959        match self {
960            Self::Simple(b) => b.span(),
961            Self::Media(b) => b.span(),
962            Self::Section(b) => b.span(),
963            Self::List(b) => b.span(),
964            Self::ListItem(b) => b.span(),
965            Self::RawDelimited(b) => b.span(),
966            Self::CompoundDelimited(b) => b.span(),
967            Self::Admonition(b) => b.span(),
968            Self::Quote(b) => b.span(),
969            Self::Table(b) => b.span(),
970            Self::Preamble(b) => b.span(),
971            Self::Break(b) => b.span(),
972            Self::DocumentAttribute(b) => b.span(),
973        }
974    }
975}