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,
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 Preamble(Preamble<'src>),
67
68 Break(Break<'src>),
70
71 DocumentAttribute(Attribute<'src>),
74}
75
76impl<'src> std::fmt::Debug for Block<'src> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 Block::Simple(block) => f.debug_tuple("Block::Simple").field(block).finish(),
80 Block::Media(block) => f.debug_tuple("Block::Media").field(block).finish(),
81 Block::Section(block) => f.debug_tuple("Block::Section").field(block).finish(),
82 Block::List(block) => f.debug_tuple("Block::List").field(block).finish(),
83 Block::ListItem(block) => f.debug_tuple("Block::ListItem").field(block).finish(),
84
85 Block::RawDelimited(block) => {
86 f.debug_tuple("Block::RawDelimited").field(block).finish()
87 }
88
89 Block::CompoundDelimited(block) => f
90 .debug_tuple("Block::CompoundDelimited")
91 .field(block)
92 .finish(),
93
94 Block::Preamble(block) => f.debug_tuple("Block::Preamble").field(block).finish(),
95 Block::Break(break_) => f.debug_tuple("Block::Break").field(break_).finish(),
96
97 Block::DocumentAttribute(block) => f
98 .debug_tuple("Block::DocumentAttribute")
99 .field(block)
100 .finish(),
101 }
102 }
103}
104
105impl<'src> Block<'src> {
106 pub(crate) fn parse(
110 source: Span<'src>,
111 parser: &mut Parser,
112 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
113 Self::parse_internal(source, parser, None, false)
114 }
115
116 pub(crate) fn parse_for_list_item(
127 source: Span<'src>,
128 parser: &mut Parser,
129 parent_list_markers: &[ListItemMarker<'src>],
130 is_continuation: bool,
131 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
132 Self::parse_internal(source, parser, Some(parent_list_markers), is_continuation)
133 }
134
135 fn parse_internal(
137 source: Span<'src>,
138 parser: &mut Parser,
139 parent_list_markers: Option<&[ListItemMarker<'src>]>,
140 is_continuation: bool,
141 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
142 let first_line = source.take_line().item.discard_whitespace();
146
147 if let Some(first_char) = first_line.chars().next()
150 && !matches!(
151 first_char,
152 '.' | '#' | '=' | '/' | '-' | '+' | '*' | '_' | '[' | ':' | '\'' | '<' | '•'
153 )
154 && !first_line.contains("::")
155 && !first_line.contains(";;")
156 && !ListItemMarker::starts_with_marker(first_line)
157 && parent_list_markers.is_none()
158 && let Some(MatchedItem {
159 item: simple_block,
160 after,
161 }) = SimpleBlock::parse_fast(source, parser)
162 {
163 let mut warnings = vec![];
164 let block = Self::Simple(simple_block);
165
166 Self::register_block_id(
167 block.id(),
168 block.title(),
169 block.span(),
170 parser,
171 &mut warnings,
172 );
173
174 return MatchAndWarnings {
175 item: Some(MatchedItem { item: block, after }),
176 warnings,
177 };
178 }
179
180 if first_line.starts_with(':')
182 && (first_line.ends_with(':') || first_line.contains(": "))
183 && let Some(attr) = Attribute::parse(source, parser)
184 {
185 let mut warnings: Vec<Warning<'src>> = vec![];
186 parser.set_attribute_from_body(&attr.item, &mut warnings);
187
188 return MatchAndWarnings {
189 item: Some(MatchedItem {
190 item: Self::DocumentAttribute(attr.item),
191 after: attr.after,
192 }),
193 warnings,
194 };
195 }
196
197 let MatchAndWarnings {
200 item: mut metadata,
201 mut warnings,
202 } = BlockMetadata::parse(source, parser);
203
204 let is_literal =
205 metadata.attrlist.as_ref().and_then(|a| a.block_style()) == Some("literal");
206
207 if !is_literal {
208 if let Some(mut rdb_maw) = RawDelimitedBlock::parse(&metadata, parser)
209 && let Some(rdb) = rdb_maw.item
210 {
211 if !rdb_maw.warnings.is_empty() {
212 warnings.append(&mut rdb_maw.warnings);
213 }
214
215 let block = Self::RawDelimited(rdb.item);
216
217 Self::register_block_id(
218 block.id(),
219 block.title(),
220 block.span(),
221 parser,
222 &mut warnings,
223 );
224
225 return MatchAndWarnings {
226 item: Some(MatchedItem {
227 item: block,
228 after: rdb.after,
229 }),
230 warnings,
231 };
232 }
233
234 if let Some(mut cdb_maw) = CompoundDelimitedBlock::parse(&metadata, parser)
235 && let Some(cdb) = cdb_maw.item
236 {
237 if !cdb_maw.warnings.is_empty() {
238 warnings.append(&mut cdb_maw.warnings);
239 }
240
241 let block = Self::CompoundDelimited(cdb.item);
242
243 Self::register_block_id(
244 block.id(),
245 block.title(),
246 block.span(),
247 parser,
248 &mut warnings,
249 );
250
251 return MatchAndWarnings {
252 item: Some(MatchedItem {
253 item: block,
254 after: cdb.after,
255 }),
256 warnings,
257 };
258 }
259
260 let line = metadata.block_start.take_normalized_line();
262
263 if line.item.starts_with("image::")
264 || line.item.starts_with("video::")
265 || line.item.starts_with("video::")
266 {
267 let mut media_block_maw = MediaBlock::parse(&metadata, parser);
268
269 if let Some(media_block) = media_block_maw.item {
270 if !media_block_maw.warnings.is_empty() {
274 warnings.append(&mut media_block_maw.warnings);
275 }
276
277 let block = Self::Media(media_block.item);
278
279 Self::register_block_id(
280 block.id(),
281 block.title(),
282 block.span(),
283 parser,
284 &mut warnings,
285 );
286
287 return MatchAndWarnings {
288 item: Some(MatchedItem {
289 item: block,
290 after: media_block.after,
291 }),
292 warnings,
293 };
294 }
295
296 }
299
300 if (line.item.starts_with('=') || line.item.starts_with('#'))
301 && let Some(mi_section_block) =
302 SectionBlock::parse(&metadata, parser, &mut warnings)
303 {
304 return MatchAndWarnings {
308 item: Some(MatchedItem {
309 item: Self::Section(mi_section_block.item),
310 after: mi_section_block.after,
311 }),
312 warnings,
313 };
314 }
315
316 if (line.item.starts_with('\'')
317 || line.item.starts_with('-')
318 || line.item.starts_with('*')
319 || line.item.starts_with('<'))
320 && let Some(mi_break) = Break::parse(&metadata, parser)
321 {
322 return MatchAndWarnings {
325 item: Some(MatchedItem {
326 item: Self::Break(mi_break.item),
327 after: mi_break.after,
328 }),
329 warnings,
330 };
331 }
332
333 if parent_list_markers.is_none()
337 && let Some(mi_list) = ListBlock::parse(&metadata, parser, &mut warnings)
338 {
339 return MatchAndWarnings {
340 item: Some(MatchedItem {
341 item: Self::List(mi_list.item),
342 after: mi_list.after,
343 }),
344 warnings,
345 };
346 }
347
348 let simple_block_mi = if let Some(plm) = parent_list_markers {
355 SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
356 } else {
357 SimpleBlock::parse(&metadata, parser)
358 };
359
360 if simple_block_mi.is_none() && !metadata.is_empty() {
361 warnings.push(Warning {
365 source: metadata.source,
366 warning: WarningType::MissingBlockAfterTitleOrAttributeList,
367 });
368
369 metadata.title_source = None;
372 metadata.title = None;
373 metadata.anchor = None;
374 metadata.attrlist = None;
375 metadata.block_start = metadata.source;
376 }
377 }
378
379 let simple_block_mi = if let Some(plm) = parent_list_markers {
381 SimpleBlock::parse_for_list_item(&metadata, parser, is_continuation, plm)
382 } else {
383 SimpleBlock::parse(&metadata, parser)
384 };
385
386 let mut result = MatchAndWarnings {
387 item: simple_block_mi.map(|mi| MatchedItem {
388 item: Self::Simple(mi.item),
389 after: mi.after,
390 }),
391 warnings,
392 };
393
394 if let Some(ref matched_item) = result.item {
395 Self::register_block_id(
396 matched_item.item.id(),
397 matched_item.item.title(),
398 matched_item.item.span(),
399 parser,
400 &mut result.warnings,
401 );
402 }
403
404 result
405 }
406
407 fn register_block_id(
412 id: Option<&str>,
413 title: Option<&str>,
414 span: Span<'src>,
415 parser: &mut Parser,
416 warnings: &mut Vec<Warning<'src>>,
417 ) {
418 if let Some(id) = id
419 && let Err(_duplicate_error) = parser.register_ref(
420 id,
421 title, RefType::Anchor,
423 )
424 {
425 warnings.push(Warning {
427 source: span,
428 warning: WarningType::DuplicateId(id.to_string()),
429 });
430 }
431 }
432
433 pub(crate) fn as_list_item(&self) -> Option<&ListItem<'src>> {
436 match self {
437 Self::ListItem(li) => Some(li),
438 _ => None,
439 }
440 }
441
442 pub(crate) fn resolve_references(
450 &mut self,
451 resolver: &dyn ReferenceResolver,
452 renderer: &dyn InlineSubstitutionRenderer,
453 warnings: &mut Vec<ReferenceWarning>,
454 ) {
455 if let Some(content) = self.content_mut() {
456 content.resolve_references(resolver, renderer, warnings);
457 }
458
459 for child in self.nested_blocks_mut() {
460 child.resolve_references(resolver, renderer, warnings);
461 }
462 }
463}
464
465impl<'src> IsBlock<'src> for Block<'src> {
466 fn content_model(&self) -> ContentModel {
467 match self {
468 Self::Simple(_) => ContentModel::Simple,
469 Self::Media(b) => b.content_model(),
470 Self::Section(_) => ContentModel::Compound,
471 Self::List(b) => b.content_model(),
472 Self::ListItem(b) => b.content_model(),
473 Self::RawDelimited(b) => b.content_model(),
474 Self::CompoundDelimited(b) => b.content_model(),
475 Self::Preamble(b) => b.content_model(),
476 Self::Break(b) => b.content_model(),
477 Self::DocumentAttribute(b) => b.content_model(),
478 }
479 }
480
481 fn rendered_content(&'src self) -> Option<&'src str> {
482 match self {
483 Self::Simple(b) => b.rendered_content(),
484 Self::Media(b) => b.rendered_content(),
485 Self::Section(b) => b.rendered_content(),
486 Self::List(b) => b.rendered_content(),
487 Self::ListItem(b) => b.rendered_content(),
488 Self::RawDelimited(b) => b.rendered_content(),
489 Self::CompoundDelimited(b) => b.rendered_content(),
490 Self::Preamble(b) => b.rendered_content(),
491 Self::Break(b) => b.rendered_content(),
492 Self::DocumentAttribute(b) => b.rendered_content(),
493 }
494 }
495
496 fn raw_context(&self) -> CowStr<'src> {
497 match self {
498 Self::Simple(b) => b.raw_context(),
499 Self::Media(b) => b.raw_context(),
500 Self::Section(b) => b.raw_context(),
501 Self::List(b) => b.raw_context(),
502 Self::ListItem(b) => b.raw_context(),
503 Self::RawDelimited(b) => b.raw_context(),
504 Self::CompoundDelimited(b) => b.raw_context(),
505 Self::Preamble(b) => b.raw_context(),
506 Self::Break(b) => b.raw_context(),
507 Self::DocumentAttribute(b) => b.raw_context(),
508 }
509 }
510
511 fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
512 match self {
513 Self::Simple(b) => b.nested_blocks(),
514 Self::Media(b) => b.nested_blocks(),
515 Self::Section(b) => b.nested_blocks(),
516 Self::List(b) => b.nested_blocks(),
517 Self::ListItem(b) => b.nested_blocks(),
518 Self::RawDelimited(b) => b.nested_blocks(),
519 Self::CompoundDelimited(b) => b.nested_blocks(),
520 Self::Preamble(b) => b.nested_blocks(),
521 Self::Break(b) => b.nested_blocks(),
522 Self::DocumentAttribute(b) => b.nested_blocks(),
523 }
524 }
525
526 fn nested_blocks_mut(&mut self) -> &mut [Block<'src>] {
527 match self {
528 Self::Simple(b) => b.nested_blocks_mut(),
529 Self::Media(b) => b.nested_blocks_mut(),
530 Self::Section(b) => b.nested_blocks_mut(),
531 Self::List(b) => b.nested_blocks_mut(),
532 Self::ListItem(b) => b.nested_blocks_mut(),
533 Self::RawDelimited(b) => b.nested_blocks_mut(),
534 Self::CompoundDelimited(b) => b.nested_blocks_mut(),
535 Self::Preamble(b) => b.nested_blocks_mut(),
536 Self::Break(b) => b.nested_blocks_mut(),
537 Self::DocumentAttribute(b) => b.nested_blocks_mut(),
538 }
539 }
540
541 fn content_mut(&mut self) -> Option<&mut Content<'src>> {
542 match self {
543 Self::Simple(b) => b.content_mut(),
544 Self::Media(b) => b.content_mut(),
545 Self::Section(b) => b.content_mut(),
546 Self::List(b) => b.content_mut(),
547 Self::ListItem(b) => b.content_mut(),
548 Self::RawDelimited(b) => b.content_mut(),
549 Self::CompoundDelimited(b) => b.content_mut(),
550 Self::Preamble(b) => b.content_mut(),
551 Self::Break(b) => b.content_mut(),
552 Self::DocumentAttribute(b) => b.content_mut(),
553 }
554 }
555
556 fn title_source(&'src self) -> Option<Span<'src>> {
557 match self {
558 Self::Simple(b) => b.title_source(),
559 Self::Media(b) => b.title_source(),
560 Self::Section(b) => b.title_source(),
561 Self::List(b) => b.title_source(),
562 Self::ListItem(b) => b.title_source(),
563 Self::RawDelimited(b) => b.title_source(),
564 Self::CompoundDelimited(b) => b.title_source(),
565 Self::Preamble(b) => b.title_source(),
566 Self::Break(b) => b.title_source(),
567 Self::DocumentAttribute(b) => b.title_source(),
568 }
569 }
570
571 fn title(&self) -> Option<&str> {
572 match self {
573 Self::Simple(b) => b.title(),
574 Self::Media(b) => b.title(),
575 Self::Section(b) => b.title(),
576 Self::List(b) => b.title(),
577 Self::ListItem(b) => b.title(),
578 Self::RawDelimited(b) => b.title(),
579 Self::CompoundDelimited(b) => b.title(),
580 Self::Preamble(b) => b.title(),
581 Self::Break(b) => b.title(),
582 Self::DocumentAttribute(b) => b.title(),
583 }
584 }
585
586 fn anchor(&'src self) -> Option<Span<'src>> {
587 match self {
588 Self::Simple(b) => b.anchor(),
589 Self::Media(b) => b.anchor(),
590 Self::Section(b) => b.anchor(),
591 Self::List(b) => b.anchor(),
592 Self::ListItem(b) => b.anchor(),
593 Self::RawDelimited(b) => b.anchor(),
594 Self::CompoundDelimited(b) => b.anchor(),
595 Self::Preamble(b) => b.anchor(),
596 Self::Break(b) => b.anchor(),
597 Self::DocumentAttribute(b) => b.anchor(),
598 }
599 }
600
601 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
602 match self {
603 Self::Simple(b) => b.anchor_reftext(),
604 Self::Media(b) => b.anchor_reftext(),
605 Self::Section(b) => b.anchor_reftext(),
606 Self::List(b) => b.anchor_reftext(),
607 Self::ListItem(b) => b.anchor_reftext(),
608 Self::RawDelimited(b) => b.anchor_reftext(),
609 Self::CompoundDelimited(b) => b.anchor_reftext(),
610 Self::Preamble(b) => b.anchor_reftext(),
611 Self::Break(b) => b.anchor_reftext(),
612 Self::DocumentAttribute(b) => b.anchor_reftext(),
613 }
614 }
615
616 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
617 match self {
618 Self::Simple(b) => b.attrlist(),
619 Self::Media(b) => b.attrlist(),
620 Self::Section(b) => b.attrlist(),
621 Self::List(b) => b.attrlist(),
622 Self::ListItem(b) => b.attrlist(),
623 Self::RawDelimited(b) => b.attrlist(),
624 Self::CompoundDelimited(b) => b.attrlist(),
625 Self::Preamble(b) => b.attrlist(),
626 Self::Break(b) => b.attrlist(),
627 Self::DocumentAttribute(b) => b.attrlist(),
628 }
629 }
630
631 fn substitution_group(&self) -> SubstitutionGroup {
632 match self {
633 Self::Simple(b) => b.substitution_group(),
634 Self::Media(b) => b.substitution_group(),
635 Self::Section(b) => b.substitution_group(),
636 Self::List(b) => b.substitution_group(),
637 Self::ListItem(b) => b.substitution_group(),
638 Self::RawDelimited(b) => b.substitution_group(),
639 Self::CompoundDelimited(b) => b.substitution_group(),
640 Self::Preamble(b) => b.substitution_group(),
641 Self::Break(b) => b.substitution_group(),
642 Self::DocumentAttribute(b) => b.substitution_group(),
643 }
644 }
645}
646
647impl<'src> HasSpan<'src> for Block<'src> {
648 fn span(&self) -> Span<'src> {
649 match self {
650 Self::Simple(b) => b.span(),
651 Self::Media(b) => b.span(),
652 Self::Section(b) => b.span(),
653 Self::List(b) => b.span(),
654 Self::ListItem(b) => b.span(),
655 Self::RawDelimited(b) => b.span(),
656 Self::CompoundDelimited(b) => b.span(),
657 Self::Preamble(b) => b.span(),
658 Self::Break(b) => b.span(),
659 Self::DocumentAttribute(b) => b.span(),
660 }
661 }
662}