1use std::slice::Iter;
2
3use crate::{
4 HasSpan, Parser, Span,
5 attributes::Attrlist,
6 blocks::{
7 Break, CompoundDelimitedBlock, ContentModel, IsBlock, ListBlock, ListItem, ListItemMarker,
8 MediaBlock, Preamble, RawDelimitedBlock, SectionBlock, SimpleBlock, TableBlock,
9 metadata::BlockMetadata,
10 },
11 content::{Content, SubstitutionGroup},
12 document::{Attribute, RefType},
13 parser::{InlineSubstitutionRenderer, ReferenceResolver, ReferenceWarning},
14 span::MatchedItem,
15 strings::CowStr,
16 warnings::{MatchAndWarnings, Warning, WarningType},
17};
18
19#[derive(Clone, Eq, PartialEq)]
32#[allow(clippy::large_enum_variant)] #[non_exhaustive]
34pub enum Block<'src> {
35 Simple(SimpleBlock<'src>),
38
39 Media(MediaBlock<'src>),
42
43 Section(SectionBlock<'src>),
46
47 List(ListBlock<'src>),
51
52 ListItem(ListItem<'src>),
55
56 RawDelimited(RawDelimitedBlock<'src>),
60
61 CompoundDelimited(CompoundDelimitedBlock<'src>),
63
64 Table(TableBlock<'src>),
66
67 Preamble(Preamble<'src>),
70
71 Break(Break<'src>),
73
74 DocumentAttribute(Attribute<'src>),
77}
78
79impl<'src> std::fmt::Debug for Block<'src> {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Block::Simple(block) => f.debug_tuple("Block::Simple").field(block).finish(),
83 Block::Media(block) => f.debug_tuple("Block::Media").field(block).finish(),
84 Block::Section(block) => f.debug_tuple("Block::Section").field(block).finish(),
85 Block::List(block) => f.debug_tuple("Block::List").field(block).finish(),
86 Block::ListItem(block) => f.debug_tuple("Block::ListItem").field(block).finish(),
87
88 Block::RawDelimited(block) => {
89 f.debug_tuple("Block::RawDelimited").field(block).finish()
90 }
91
92 Block::CompoundDelimited(block) => f
93 .debug_tuple("Block::CompoundDelimited")
94 .field(block)
95 .finish(),
96
97 Block::Table(block) => f.debug_tuple("Block::Table").field(block).finish(),
98 Block::Preamble(block) => f.debug_tuple("Block::Preamble").field(block).finish(),
99 Block::Break(break_) => f.debug_tuple("Block::Break").field(break_).finish(),
100
101 Block::DocumentAttribute(block) => f
102 .debug_tuple("Block::DocumentAttribute")
103 .field(block)
104 .finish(),
105 }
106 }
107}
108
109impl<'src> Block<'src> {
110 pub(crate) fn parse(
114 source: Span<'src>,
115 parser: &mut Parser,
116 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
117 Self::parse_internal(source, parser, None, false)
118 }
119
120 pub(crate) fn parse_for_list_item(
131 source: Span<'src>,
132 parser: &mut Parser,
133 parent_list_markers: &[ListItemMarker<'src>],
134 is_continuation: bool,
135 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
136 Self::parse_internal(source, parser, Some(parent_list_markers), is_continuation)
137 }
138
139 fn parse_internal(
141 source: Span<'src>,
142 parser: &mut Parser,
143 parent_list_markers: Option<&[ListItemMarker<'src>]>,
144 is_continuation: bool,
145 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
146 let first_line = source.take_line().item.discard_whitespace();
150
151 if let Some(first_char) = first_line.chars().next()
154 && !matches!(
155 first_char,
156 '.' | '#' | '=' | '/' | '-' | '+' | '*' | '_' | '[' | ':' | '\'' | '<' | '•'
157 )
158 && !first_line.contains("::")
159 && !first_line.contains(";;")
160 && !TableBlock::is_table_delimiter(&first_line)
161 && !ListItemMarker::starts_with_marker(first_line)
162 && parent_list_markers.is_none()
163 && let Some(MatchedItem {
164 item: simple_block,
165 after,
166 }) = SimpleBlock::parse_fast(source, parser)
167 {
168 let mut warnings = vec![];
169 let block = Self::Simple(simple_block);
170
171 Self::register_block_id(
172 block.id(),
173 block.title(),
174 block.span(),
175 parser,
176 &mut warnings,
177 );
178
179 return MatchAndWarnings {
180 item: Some(MatchedItem { item: block, after }),
181 warnings,
182 };
183 }
184
185 if first_line.starts_with(':')
187 && (first_line.ends_with(':') || first_line.contains(": "))
188 && let Some(attr) = Attribute::parse(source, parser)
189 {
190 let mut warnings: Vec<Warning<'src>> = vec![];
191 parser.set_attribute_from_body(&attr.item, &mut warnings);
192
193 return MatchAndWarnings {
194 item: Some(MatchedItem {
195 item: Self::DocumentAttribute(attr.item),
196 after: attr.after,
197 }),
198 warnings,
199 };
200 }
201
202 let MatchAndWarnings {
205 item: mut metadata,
206 mut warnings,
207 } = BlockMetadata::parse(source, parser);
208
209 let is_literal =
210 metadata.attrlist.as_ref().and_then(|a| a.block_style()) == Some("literal");
211
212 if !is_literal {
213 if let Some(mut rdb_maw) = RawDelimitedBlock::parse(&metadata, parser)
214 && let Some(rdb) = rdb_maw.item
215 {
216 if !rdb_maw.warnings.is_empty() {
217 warnings.append(&mut rdb_maw.warnings);
218 }
219
220 let block = Self::RawDelimited(rdb.item);
221
222 Self::register_block_id(
223 block.id(),
224 block.title(),
225 block.span(),
226 parser,
227 &mut warnings,
228 );
229
230 return MatchAndWarnings {
231 item: Some(MatchedItem {
232 item: block,
233 after: rdb.after,
234 }),
235 warnings,
236 };
237 }
238
239 if let Some(mut cdb_maw) = CompoundDelimitedBlock::parse(&metadata, parser)
240 && let Some(cdb) = cdb_maw.item
241 {
242 if !cdb_maw.warnings.is_empty() {
243 warnings.append(&mut cdb_maw.warnings);
244 }
245
246 let block = Self::CompoundDelimited(cdb.item);
247
248 Self::register_block_id(
249 block.id(),
250 block.title(),
251 block.span(),
252 parser,
253 &mut warnings,
254 );
255
256 return MatchAndWarnings {
257 item: Some(MatchedItem {
258 item: block,
259 after: cdb.after,
260 }),
261 warnings,
262 };
263 }
264
265 if let Some(mut table_maw) = TableBlock::parse(&metadata, parser)
266 && let Some(table) = table_maw.item
267 {
268 if !table_maw.warnings.is_empty() {
269 warnings.append(&mut table_maw.warnings);
270 }
271
272 let block = Self::Table(table.item);
273
274 Self::register_block_id(
275 block.id(),
276 block.title(),
277 block.span(),
278 parser,
279 &mut warnings,
280 );
281
282 return MatchAndWarnings {
283 item: Some(MatchedItem {
284 item: block,
285 after: table.after,
286 }),
287 warnings,
288 };
289 }
290
291 let line = metadata.block_start.take_normalized_line();
293
294 if line.item.starts_with("image::")
295 || line.item.starts_with("video::")
296 || line.item.starts_with("video::")
297 {
298 let mut media_block_maw = MediaBlock::parse(&metadata, parser);
299
300 if let Some(media_block) = media_block_maw.item {
301 if !media_block_maw.warnings.is_empty() {
305 warnings.append(&mut media_block_maw.warnings);
306 }
307
308 let block = Self::Media(media_block.item);
309
310 Self::register_block_id(
311 block.id(),
312 block.title(),
313 block.span(),
314 parser,
315 &mut warnings,
316 );
317
318 return MatchAndWarnings {
319 item: Some(MatchedItem {
320 item: block,
321 after: media_block.after,
322 }),
323 warnings,
324 };
325 }
326
327 }
330
331 if (line.item.starts_with('=') || line.item.starts_with('#'))
332 && let Some(mi_section_block) =
333 SectionBlock::parse(&metadata, parser, &mut warnings)
334 {
335 return MatchAndWarnings {
339 item: Some(MatchedItem {
340 item: Self::Section(mi_section_block.item),
341 after: mi_section_block.after,
342 }),
343 warnings,
344 };
345 }
346
347 if (line.item.starts_with('\'')
348 || line.item.starts_with('-')
349 || line.item.starts_with('*')
350 || line.item.starts_with('<'))
351 && let Some(mi_break) = Break::parse(&metadata, parser)
352 {
353 return MatchAndWarnings {
356 item: Some(MatchedItem {
357 item: Self::Break(mi_break.item),
358 after: mi_break.after,
359 }),
360 warnings,
361 };
362 }
363
364 if parent_list_markers.is_none()
368 && let Some(mi_list) = ListBlock::parse(&metadata, parser, &mut warnings)
369 {
370 return MatchAndWarnings {
371 item: Some(MatchedItem {
372 item: Self::List(mi_list.item),
373 after: mi_list.after,
374 }),
375 warnings,
376 };
377 }
378
379 let simple_block_mi = if let Some(plm) = parent_list_markers {
386 SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
387 } else {
388 SimpleBlock::parse(&metadata, parser)
389 };
390
391 if simple_block_mi.is_none() && !metadata.is_empty() {
392 warnings.push(Warning {
396 source: metadata.source,
397 warning: WarningType::MissingBlockAfterTitleOrAttributeList,
398 });
399
400 metadata.title_source = None;
403 metadata.title = None;
404 metadata.anchor = None;
405 metadata.attrlist = None;
406 metadata.block_start = metadata.source;
407 }
408 }
409
410 let simple_block_mi = if let Some(plm) = parent_list_markers {
412 SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
413 } else {
414 SimpleBlock::parse(&metadata, parser)
415 };
416
417 let mut result = MatchAndWarnings {
418 item: simple_block_mi.map(|mi| MatchedItem {
419 item: Self::Simple(mi.item),
420 after: mi.after,
421 }),
422 warnings,
423 };
424
425 if let Some(ref matched_item) = result.item {
426 Self::register_block_id(
427 matched_item.item.id(),
428 matched_item.item.title(),
429 matched_item.item.span(),
430 parser,
431 &mut result.warnings,
432 );
433 }
434
435 result
436 }
437
438 fn register_block_id(
443 id: Option<&str>,
444 title: Option<&str>,
445 span: Span<'src>,
446 parser: &mut Parser,
447 warnings: &mut Vec<Warning<'src>>,
448 ) {
449 if let Some(id) = id
450 && let Err(_duplicate_error) = parser.register_ref(
451 id,
452 title, RefType::Anchor,
454 )
455 {
456 warnings.push(Warning {
458 source: span,
459 warning: WarningType::DuplicateId(id.to_string()),
460 });
461 }
462 }
463
464 pub(crate) fn as_list_item(&self) -> Option<&ListItem<'src>> {
467 match self {
468 Self::ListItem(li) => Some(li),
469 _ => None,
470 }
471 }
472
473 pub(crate) fn resolve_references(
481 &mut self,
482 resolver: &dyn ReferenceResolver,
483 renderer: &dyn InlineSubstitutionRenderer,
484 warnings: &mut Vec<ReferenceWarning>,
485 ) {
486 if let Some(content) = self.content_mut() {
487 content.resolve_references(resolver, renderer, warnings);
488 }
489
490 if let Self::Table(table) = self {
493 table.resolve_references(resolver, renderer, warnings);
494 }
495
496 for child in self.nested_blocks_mut() {
497 child.resolve_references(resolver, renderer, warnings);
498 }
499 }
500}
501
502impl<'src> IsBlock<'src> for Block<'src> {
503 fn content_model(&self) -> ContentModel {
504 match self {
505 Self::Simple(_) => ContentModel::Simple,
506 Self::Media(b) => b.content_model(),
507 Self::Section(_) => ContentModel::Compound,
508 Self::List(b) => b.content_model(),
509 Self::ListItem(b) => b.content_model(),
510 Self::RawDelimited(b) => b.content_model(),
511 Self::CompoundDelimited(b) => b.content_model(),
512 Self::Table(b) => b.content_model(),
513 Self::Preamble(b) => b.content_model(),
514 Self::Break(b) => b.content_model(),
515 Self::DocumentAttribute(b) => b.content_model(),
516 }
517 }
518
519 fn rendered_content(&'src self) -> Option<&'src str> {
520 match self {
521 Self::Simple(b) => b.rendered_content(),
522 Self::Media(b) => b.rendered_content(),
523 Self::Section(b) => b.rendered_content(),
524 Self::List(b) => b.rendered_content(),
525 Self::ListItem(b) => b.rendered_content(),
526 Self::RawDelimited(b) => b.rendered_content(),
527 Self::CompoundDelimited(b) => b.rendered_content(),
528 Self::Table(b) => b.rendered_content(),
529 Self::Preamble(b) => b.rendered_content(),
530 Self::Break(b) => b.rendered_content(),
531 Self::DocumentAttribute(b) => b.rendered_content(),
532 }
533 }
534
535 fn raw_context(&self) -> CowStr<'src> {
536 match self {
537 Self::Simple(b) => b.raw_context(),
538 Self::Media(b) => b.raw_context(),
539 Self::Section(b) => b.raw_context(),
540 Self::List(b) => b.raw_context(),
541 Self::ListItem(b) => b.raw_context(),
542 Self::RawDelimited(b) => b.raw_context(),
543 Self::CompoundDelimited(b) => b.raw_context(),
544 Self::Table(b) => b.raw_context(),
545 Self::Preamble(b) => b.raw_context(),
546 Self::Break(b) => b.raw_context(),
547 Self::DocumentAttribute(b) => b.raw_context(),
548 }
549 }
550
551 fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
552 match self {
553 Self::Simple(b) => b.nested_blocks(),
554 Self::Media(b) => b.nested_blocks(),
555 Self::Section(b) => b.nested_blocks(),
556 Self::List(b) => b.nested_blocks(),
557 Self::ListItem(b) => b.nested_blocks(),
558 Self::RawDelimited(b) => b.nested_blocks(),
559 Self::CompoundDelimited(b) => b.nested_blocks(),
560 Self::Table(b) => b.nested_blocks(),
561 Self::Preamble(b) => b.nested_blocks(),
562 Self::Break(b) => b.nested_blocks(),
563 Self::DocumentAttribute(b) => b.nested_blocks(),
564 }
565 }
566
567 fn nested_blocks_mut(&mut self) -> &mut [Block<'src>] {
568 match self {
569 Self::Simple(b) => b.nested_blocks_mut(),
570 Self::Media(b) => b.nested_blocks_mut(),
571 Self::Section(b) => b.nested_blocks_mut(),
572 Self::List(b) => b.nested_blocks_mut(),
573 Self::ListItem(b) => b.nested_blocks_mut(),
574 Self::RawDelimited(b) => b.nested_blocks_mut(),
575 Self::CompoundDelimited(b) => b.nested_blocks_mut(),
576 Self::Table(b) => b.nested_blocks_mut(),
577 Self::Preamble(b) => b.nested_blocks_mut(),
578 Self::Break(b) => b.nested_blocks_mut(),
579 Self::DocumentAttribute(b) => b.nested_blocks_mut(),
580 }
581 }
582
583 fn content_mut(&mut self) -> Option<&mut Content<'src>> {
584 match self {
585 Self::Simple(b) => b.content_mut(),
586 Self::Media(b) => b.content_mut(),
587 Self::Section(b) => b.content_mut(),
588 Self::List(b) => b.content_mut(),
589 Self::ListItem(b) => b.content_mut(),
590 Self::RawDelimited(b) => b.content_mut(),
591 Self::CompoundDelimited(b) => b.content_mut(),
592 Self::Table(b) => b.content_mut(),
593 Self::Preamble(b) => b.content_mut(),
594 Self::Break(b) => b.content_mut(),
595 Self::DocumentAttribute(b) => b.content_mut(),
596 }
597 }
598
599 fn title_source(&'src self) -> Option<Span<'src>> {
600 match self {
601 Self::Simple(b) => b.title_source(),
602 Self::Media(b) => b.title_source(),
603 Self::Section(b) => b.title_source(),
604 Self::List(b) => b.title_source(),
605 Self::ListItem(b) => b.title_source(),
606 Self::RawDelimited(b) => b.title_source(),
607 Self::CompoundDelimited(b) => b.title_source(),
608 Self::Table(b) => b.title_source(),
609 Self::Preamble(b) => b.title_source(),
610 Self::Break(b) => b.title_source(),
611 Self::DocumentAttribute(b) => b.title_source(),
612 }
613 }
614
615 fn title(&self) -> Option<&str> {
616 match self {
617 Self::Simple(b) => b.title(),
618 Self::Media(b) => b.title(),
619 Self::Section(b) => b.title(),
620 Self::List(b) => b.title(),
621 Self::ListItem(b) => b.title(),
622 Self::RawDelimited(b) => b.title(),
623 Self::CompoundDelimited(b) => b.title(),
624 Self::Table(b) => b.title(),
625 Self::Preamble(b) => b.title(),
626 Self::Break(b) => b.title(),
627 Self::DocumentAttribute(b) => b.title(),
628 }
629 }
630
631 fn anchor(&'src self) -> Option<Span<'src>> {
632 match self {
633 Self::Simple(b) => b.anchor(),
634 Self::Media(b) => b.anchor(),
635 Self::Section(b) => b.anchor(),
636 Self::List(b) => b.anchor(),
637 Self::ListItem(b) => b.anchor(),
638 Self::RawDelimited(b) => b.anchor(),
639 Self::CompoundDelimited(b) => b.anchor(),
640 Self::Table(b) => b.anchor(),
641 Self::Preamble(b) => b.anchor(),
642 Self::Break(b) => b.anchor(),
643 Self::DocumentAttribute(b) => b.anchor(),
644 }
645 }
646
647 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
648 match self {
649 Self::Simple(b) => b.anchor_reftext(),
650 Self::Media(b) => b.anchor_reftext(),
651 Self::Section(b) => b.anchor_reftext(),
652 Self::List(b) => b.anchor_reftext(),
653 Self::ListItem(b) => b.anchor_reftext(),
654 Self::RawDelimited(b) => b.anchor_reftext(),
655 Self::CompoundDelimited(b) => b.anchor_reftext(),
656 Self::Table(b) => b.anchor_reftext(),
657 Self::Preamble(b) => b.anchor_reftext(),
658 Self::Break(b) => b.anchor_reftext(),
659 Self::DocumentAttribute(b) => b.anchor_reftext(),
660 }
661 }
662
663 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
664 match self {
665 Self::Simple(b) => b.attrlist(),
666 Self::Media(b) => b.attrlist(),
667 Self::Section(b) => b.attrlist(),
668 Self::List(b) => b.attrlist(),
669 Self::ListItem(b) => b.attrlist(),
670 Self::RawDelimited(b) => b.attrlist(),
671 Self::CompoundDelimited(b) => b.attrlist(),
672 Self::Table(b) => b.attrlist(),
673 Self::Preamble(b) => b.attrlist(),
674 Self::Break(b) => b.attrlist(),
675 Self::DocumentAttribute(b) => b.attrlist(),
676 }
677 }
678
679 fn substitution_group(&self) -> SubstitutionGroup {
680 match self {
681 Self::Simple(b) => b.substitution_group(),
682 Self::Media(b) => b.substitution_group(),
683 Self::Section(b) => b.substitution_group(),
684 Self::List(b) => b.substitution_group(),
685 Self::ListItem(b) => b.substitution_group(),
686 Self::RawDelimited(b) => b.substitution_group(),
687 Self::CompoundDelimited(b) => b.substitution_group(),
688 Self::Table(b) => b.substitution_group(),
689 Self::Preamble(b) => b.substitution_group(),
690 Self::Break(b) => b.substitution_group(),
691 Self::DocumentAttribute(b) => b.substitution_group(),
692 }
693 }
694}
695
696impl<'src> HasSpan<'src> for Block<'src> {
697 fn span(&self) -> Span<'src> {
698 match self {
699 Self::Simple(b) => b.span(),
700 Self::Media(b) => b.span(),
701 Self::Section(b) => b.span(),
702 Self::List(b) => b.span(),
703 Self::ListItem(b) => b.span(),
704 Self::RawDelimited(b) => b.span(),
705 Self::CompoundDelimited(b) => b.span(),
706 Self::Table(b) => b.span(),
707 Self::Preamble(b) => b.span(),
708 Self::Break(b) => b.span(),
709 Self::DocumentAttribute(b) => b.span(),
710 }
711 }
712}