1use crate::{
2 HasSpan, Parser, Span,
3 attributes::Attrlist,
4 blocks::{
5 Block, ChildBlocks, ContentModel, IsBlock, caption::assign_block_caption,
6 metadata::BlockMetadata, parse_utils::parse_blocks_until,
7 },
8 content::Content,
9 internal::debug::DebugSliceReference,
10 span::MatchedItem,
11 strings::CowStr,
12 warnings::{MatchAndWarnings, Warning, WarningType},
13};
14
15#[derive(Clone, Eq, Hash, PartialEq)]
30pub struct CompoundDelimitedBlock<'src> {
31 blocks: Vec<Block<'src>>,
32 context: CowStr<'src>,
33 source: Span<'src>,
34 title_source: Option<Span<'src>>,
35 title: Option<Content<'src>>,
36 caption: Option<String>,
37 number: Option<usize>,
38 anchor: Option<Span<'src>>,
39 anchor_reftext: Option<Span<'src>>,
40 attrlist: Option<Attrlist<'src>>,
41}
42
43impl<'src> CompoundDelimitedBlock<'src> {
44 pub fn child_blocks(&'src self) -> ChildBlocks<'src> {
51 ChildBlocks::from_slice(&self.blocks)
52 }
53
54 pub(crate) fn title_content_mut(&mut self) -> Option<&mut Content<'src>> {
62 self.title.as_mut()
63 }
64
65 pub(crate) fn is_valid_delimiter(line: &Span<'src>) -> bool {
66 let data = line.data();
67
68 if data == "--" {
69 return true;
70 }
71
72 if data.len() >= 4 {
80 if data.starts_with("====") {
81 data.split_at(4).1.chars().all(|c| c == '=')
82 } else if data.starts_with("****") {
83 data.split_at(4).1.chars().all(|c| c == '*')
84 } else if data.starts_with("____") {
85 data.split_at(4).1.chars().all(|c| c == '_')
86 } else {
87 false
88 }
89 } else {
90 false
91 }
92 }
93
94 pub(crate) fn into_nested_blocks(self) -> Vec<Block<'src>> {
96 self.blocks
97 }
98
99 pub fn context_kind(&self) -> CompoundDelimitedContext {
106 match self.context.as_ref() {
110 "example" => CompoundDelimitedContext::Example,
111 "open" => CompoundDelimitedContext::Open,
112 _ => CompoundDelimitedContext::Sidebar,
113 }
114 }
115
116 pub(crate) fn parse(
117 metadata: &BlockMetadata<'src>,
118 parser: &mut Parser,
119 ) -> Option<MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>>> {
120 let delimiter = metadata.block_start.take_normalized_line();
121 let maybe_delimiter_text = delimiter.item.data();
122
123 let context = match maybe_delimiter_text
129 .split_at_checked(maybe_delimiter_text.len().min(4))?
130 .0
131 {
132 "====" => "example",
133 "--" => "open",
134 "****" => "sidebar",
135
136 _ => return None,
141 };
142
143 if !Self::is_valid_delimiter(&delimiter.item) {
144 return None;
145 }
146
147 let mut next = delimiter.after;
148 let (closing_delimiter, after) = loop {
149 if next.is_empty() {
150 break (next, next);
151 }
152
153 let line = next.take_normalized_line();
154 if line.item.data() == delimiter.item.data() {
155 break (line.item, line.after);
156 }
157 next = line.after;
158 };
159
160 let inside_delimiters = delimiter.after.trim_remainder(closing_delimiter);
161
162 let maw_blocks = parse_blocks_until(inside_delimiters, |_, _| false, parser);
163
164 let blocks = maw_blocks.item;
165 let source = metadata
166 .source
167 .trim_remainder(closing_delimiter.discard_all());
168
169 let caption = assign_block_caption(
174 parser,
175 context,
176 metadata.attrlist.as_ref(),
177 metadata.title.is_some(),
178 );
179 let number = caption.as_ref().and_then(|c| c.number);
180 let caption = caption.map(|c| c.prefix);
181
182 Some(MatchAndWarnings {
183 item: Some(MatchedItem {
184 item: Self {
185 blocks: blocks.item,
186 context: context.into(),
187 source: source.trim_trailing_whitespace(),
188 title_source: metadata.title_source,
189 title: metadata.title.clone(),
190 caption,
191 number,
192 anchor: metadata.anchor,
193 anchor_reftext: metadata.anchor_reftext,
194 attrlist: metadata.attrlist.clone(),
195 },
196 after,
197 }),
198 warnings: if closing_delimiter.is_empty() {
199 let mut warnings = maw_blocks.warnings;
200 warnings.insert(
201 0,
202 Warning {
203 source: delimiter.item,
204 warning: WarningType::UnterminatedDelimitedBlock,
205 origin: None,
206 },
207 );
208 warnings
209 } else {
210 maw_blocks.warnings
211 },
212 })
213 }
214}
215
216impl<'src> IsBlock<'src> for CompoundDelimitedBlock<'src> {
217 fn content_model(&self) -> ContentModel {
218 ContentModel::Compound
219 }
220
221 fn raw_context(&self) -> CowStr<'src> {
222 self.context.clone()
223 }
224
225 fn child_blocks_mut(&mut self) -> &mut [Block<'src>] {
226 &mut self.blocks
227 }
228
229 fn title_source(&'src self) -> Option<Span<'src>> {
230 self.title_source
231 }
232
233 fn title(&self) -> Option<&str> {
234 self.title.as_ref().map(Content::rendered_str)
235 }
236
237 fn caption(&self) -> Option<&str> {
238 self.caption.as_deref()
239 }
240
241 fn number(&self) -> Option<usize> {
242 self.number
243 }
244
245 fn anchor(&'src self) -> Option<Span<'src>> {
246 self.anchor
247 }
248
249 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
250 self.anchor_reftext
251 }
252
253 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
254 self.attrlist.as_ref()
255 }
256}
257
258impl<'src> HasSpan<'src> for CompoundDelimitedBlock<'src> {
259 fn span(&self) -> Span<'src> {
260 self.source
261 }
262}
263
264impl std::fmt::Debug for CompoundDelimitedBlock<'_> {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 f.debug_struct("CompoundDelimitedBlock")
267 .field("blocks", &DebugSliceReference(&self.blocks))
268 .field("context", &self.context)
269 .field("source", &self.source)
270 .field("title_source", &self.title_source)
271 .field("title", &self.title)
272 .field("caption", &self.caption)
273 .field("number", &self.number)
274 .field("anchor", &self.anchor)
275 .field("anchor_reftext", &self.anchor_reftext)
276 .field("attrlist", &self.attrlist)
277 .finish()
278 }
279}
280
281#[derive(Clone, Copy, Eq, PartialEq, Hash)]
290pub enum CompoundDelimitedContext {
291 Example,
293
294 Open,
296
297 Sidebar,
299}
300
301impl CompoundDelimitedContext {
302 pub fn as_str(self) -> &'static str {
307 match self {
308 Self::Example => "example",
309 Self::Open => "open",
310 Self::Sidebar => "sidebar",
311 }
312 }
313}
314
315impl std::fmt::Debug for CompoundDelimitedContext {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 match self {
318 Self::Example => write!(f, "CompoundDelimitedContext::Example"),
319 Self::Open => write!(f, "CompoundDelimitedContext::Open"),
320 Self::Sidebar => write!(f, "CompoundDelimitedContext::Sidebar"),
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 #![allow(clippy::unwrap_used)]
328
329 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
330
331 mod context_kind {
332 use std::ops::Deref;
333
334 use crate::blocks::{
335 CompoundDelimitedBlock, CompoundDelimitedContext, IsBlock, metadata::BlockMetadata,
336 };
337
338 fn kind_of(source: &str) -> CompoundDelimitedContext {
339 let mut parser = crate::Parser::default();
340 let maw =
341 CompoundDelimitedBlock::parse(&BlockMetadata::new(source), &mut parser).unwrap();
342 let block = maw.item.unwrap().item;
343
344 assert_eq!(
346 block.context_kind().as_str(),
347 block.resolved_context().deref()
348 );
349
350 block.context_kind()
351 }
352
353 #[test]
354 fn example() {
355 assert_eq!(
356 kind_of("====\nblah\n===="),
357 CompoundDelimitedContext::Example
358 );
359 }
360
361 #[test]
362 fn open() {
363 assert_eq!(kind_of("--\nblah\n--"), CompoundDelimitedContext::Open);
364 }
365
366 #[test]
367 fn sidebar() {
368 assert_eq!(
369 kind_of("****\nblah\n****"),
370 CompoundDelimitedContext::Sidebar
371 );
372 }
373
374 #[test]
375 fn impl_debug() {
376 assert_eq!(
377 format!("{:?}", CompoundDelimitedContext::Example),
378 "CompoundDelimitedContext::Example"
379 );
380 assert_eq!(
381 format!("{:?}", CompoundDelimitedContext::Open),
382 "CompoundDelimitedContext::Open"
383 );
384 assert_eq!(
385 format!("{:?}", CompoundDelimitedContext::Sidebar),
386 "CompoundDelimitedContext::Sidebar"
387 );
388 }
389 }
390
391 mod is_valid_delimiter {
392 use crate::blocks::CompoundDelimitedBlock;
393
394 #[test]
395 fn comment() {
396 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
397 &crate::Span::new("////")
398 ));
399 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
400 &crate::Span::new("/////")
401 ));
402 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
403 &crate::Span::new("/////////")
404 ));
405
406 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
407 &crate::Span::new("///")
408 ));
409 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
410 &crate::Span::new("//-/")
411 ));
412 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
413 &crate::Span::new("////-")
414 ));
415 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
416 &crate::Span::new("//////////x")
417 ));
418 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
419 &crate::Span::new("//😀/")
420 ));
421 }
422
423 #[test]
424 fn example() {
425 assert!(CompoundDelimitedBlock::is_valid_delimiter(
426 &crate::Span::new("====")
427 ));
428 assert!(CompoundDelimitedBlock::is_valid_delimiter(
429 &crate::Span::new("=====")
430 ));
431 assert!(CompoundDelimitedBlock::is_valid_delimiter(
432 &crate::Span::new("=======")
433 ));
434
435 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
436 &crate::Span::new("===")
437 ));
438 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
439 &crate::Span::new("==-=")
440 ));
441 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
442 &crate::Span::new("====-")
443 ));
444 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
445 &crate::Span::new("==========x")
446 ));
447 }
448
449 #[test]
450 fn listing() {
451 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
452 &crate::Span::new("----")
453 ));
454 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
455 &crate::Span::new("-----")
456 ));
457 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
458 &crate::Span::new("---------")
459 ));
460 }
461
462 #[test]
463 fn literal() {
464 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
465 &crate::Span::new("....")
466 ));
467 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
468 &crate::Span::new(".....")
469 ));
470 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
471 &crate::Span::new(".........")
472 ));
473 }
474
475 #[test]
476 fn sidebar() {
477 assert!(CompoundDelimitedBlock::is_valid_delimiter(
478 &crate::Span::new("****")
479 ));
480 assert!(CompoundDelimitedBlock::is_valid_delimiter(
481 &crate::Span::new("*****")
482 ));
483 assert!(CompoundDelimitedBlock::is_valid_delimiter(
484 &crate::Span::new("*********")
485 ));
486
487 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
488 &crate::Span::new("***")
489 ));
490 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
491 &crate::Span::new("**-*")
492 ));
493 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
494 &crate::Span::new("****-")
495 ));
496 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
497 &crate::Span::new("**********x")
498 ));
499 }
500
501 #[test]
502 fn table() {
503 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
504 &crate::Span::new("|===")
505 ));
506 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
507 &crate::Span::new(",===")
508 ));
509 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
510 &crate::Span::new(":===")
511 ));
512 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
513 &crate::Span::new("!===")
514 ));
515 }
516
517 #[test]
518 fn pass() {
519 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
520 &crate::Span::new("++++")
521 ));
522 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
523 &crate::Span::new("+++++")
524 ));
525 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
526 &crate::Span::new("+++++++++")
527 ));
528 }
529
530 #[test]
531 fn quote() {
532 assert!(CompoundDelimitedBlock::is_valid_delimiter(
533 &crate::Span::new("____")
534 ));
535 assert!(CompoundDelimitedBlock::is_valid_delimiter(
536 &crate::Span::new("_____")
537 ));
538 assert!(CompoundDelimitedBlock::is_valid_delimiter(
539 &crate::Span::new("_________")
540 ));
541
542 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
543 &crate::Span::new("___")
544 ));
545 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
546 &crate::Span::new("__-_")
547 ));
548 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
549 &crate::Span::new("____-")
550 ));
551 assert!(!CompoundDelimitedBlock::is_valid_delimiter(
552 &crate::Span::new("_________x")
553 ));
554 }
555 }
556
557 mod parse {
558 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
559
560 #[test]
561 fn err_invalid_delimiter() {
562 let mut parser = Parser::default();
563 assert!(
564 crate::blocks::CompoundDelimitedBlock::parse(&BlockMetadata::new(""), &mut parser)
565 .is_none()
566 );
567
568 let mut parser = Parser::default();
569 assert!(
570 crate::blocks::CompoundDelimitedBlock::parse(
571 &BlockMetadata::new("///"),
572 &mut parser
573 )
574 .is_none()
575 );
576
577 let mut parser = Parser::default();
578 assert!(
579 crate::blocks::CompoundDelimitedBlock::parse(
580 &BlockMetadata::new("////x"),
581 &mut parser
582 )
583 .is_none()
584 );
585
586 let mut parser = Parser::default();
587 assert!(
588 crate::blocks::CompoundDelimitedBlock::parse(
589 &BlockMetadata::new("--x"),
590 &mut parser
591 )
592 .is_none()
593 );
594
595 let mut parser = Parser::default();
596 assert!(
597 crate::blocks::CompoundDelimitedBlock::parse(
598 &BlockMetadata::new("****x"),
599 &mut parser
600 )
601 .is_none()
602 );
603
604 let mut parser = Parser::default();
605 assert!(
606 crate::blocks::CompoundDelimitedBlock::parse(
607 &BlockMetadata::new("__\n__"),
608 &mut parser
609 )
610 .is_none()
611 );
612 }
613
614 #[test]
615 fn err_unterminated() {
616 let mut parser = Parser::default();
617
618 let maw = crate::blocks::CompoundDelimitedBlock::parse(
619 &BlockMetadata::new("====\nblah blah blah"),
620 &mut parser,
621 )
622 .unwrap();
623
624 assert_eq!(
625 maw.item.unwrap().item,
626 CompoundDelimitedBlock {
627 blocks: &[Block::Simple(SimpleBlock {
628 content: Content {
629 original: Span {
630 data: "blah blah blah",
631 line: 2,
632 col: 1,
633 offset: 5,
634 },
635 rendered: "blah blah blah",
636 },
637 source: Span {
638 data: "blah blah blah",
639 line: 2,
640 col: 1,
641 offset: 5,
642 },
643 style: SimpleBlockStyle::Paragraph,
644 title_source: None,
645 title: None,
646 caption: None,
647 number: None,
648 anchor: None,
649 anchor_reftext: None,
650 attrlist: None,
651 },),],
652 context: "example",
653 source: Span {
654 data: "====\nblah blah blah",
655 line: 1,
656 col: 1,
657 offset: 0,
658 },
659 title_source: None,
660 title: None,
661 caption: None,
662 number: None,
663 anchor: None,
664 anchor_reftext: None,
665 attrlist: None,
666 },
667 );
668
669 assert_eq!(
670 maw.warnings,
671 vec![Warning {
672 source: Span {
673 data: "====",
674 line: 1,
675 col: 1,
676 offset: 0,
677 },
678 warning: WarningType::UnterminatedDelimitedBlock,
679 }]
680 );
681 }
682 }
683
684 mod comment {
685 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
686
687 #[test]
688 fn empty() {
689 let mut parser = Parser::default();
690 assert!(
691 crate::blocks::CompoundDelimitedBlock::parse(
692 &BlockMetadata::new("////\n////"),
693 &mut parser
694 )
695 .is_none()
696 );
697 }
698
699 #[test]
700 fn multiple_lines() {
701 let mut parser = Parser::default();
702 assert!(
703 crate::blocks::CompoundDelimitedBlock::parse(
704 &BlockMetadata::new("////\nline1 \nline2\n////"),
705 &mut parser
706 )
707 .is_none()
708 );
709 }
710 }
711
712 mod example {
713 use crate::{
714 blocks::{ContentModel, metadata::BlockMetadata},
715 tests::prelude::*,
716 };
717
718 #[test]
719 fn empty() {
720 let mut parser = Parser::default();
721
722 let maw = crate::blocks::CompoundDelimitedBlock::parse(
723 &BlockMetadata::new("====\n===="),
724 &mut parser,
725 )
726 .unwrap();
727
728 let mi = maw.item.unwrap().clone();
729
730 assert_eq!(
731 mi.item,
732 CompoundDelimitedBlock {
733 blocks: &[],
734 context: "example",
735 source: Span {
736 data: "====\n====",
737 line: 1,
738 col: 1,
739 offset: 0,
740 },
741 title_source: None,
742 title: None,
743 caption: None,
744 number: None,
745 anchor: None,
746 anchor_reftext: None,
747 attrlist: None,
748 }
749 );
750
751 assert_eq!(mi.item.content_model(), ContentModel::Compound);
752 assert!(mi.item.rendered_content().is_none());
753 assert_eq!(mi.item.raw_context().as_ref(), "example");
754 assert_eq!(mi.item.resolved_context().as_ref(), "example");
755 assert!(mi.item.declared_style().is_none());
756 assert!(mi.item.child_blocks().next().is_none());
757 assert!(mi.item.id().is_none());
758 assert!(mi.item.roles().is_empty());
759 assert!(mi.item.options().is_empty());
760 assert!(mi.item.title_source().is_none());
761 assert!(mi.item.title().is_none());
762 assert!(mi.item.anchor().is_none());
763 assert!(mi.item.anchor_reftext().is_none());
764 assert!(mi.item.attrlist().is_none());
765 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
766 }
767
768 #[test]
769 fn multiple_blocks() {
770 let mut parser = Parser::default();
771
772 let maw = crate::blocks::CompoundDelimitedBlock::parse(
773 &BlockMetadata::new("====\nblock1\n\nblock2\n===="),
774 &mut parser,
775 )
776 .unwrap();
777
778 let mi = maw.item.unwrap().clone();
779
780 assert_eq!(
781 mi.item,
782 CompoundDelimitedBlock {
783 blocks: &[
784 Block::Simple(SimpleBlock {
785 content: Content {
786 original: Span {
787 data: "block1",
788 line: 2,
789 col: 1,
790 offset: 5,
791 },
792 rendered: "block1",
793 },
794 source: Span {
795 data: "block1",
796 line: 2,
797 col: 1,
798 offset: 5,
799 },
800 style: SimpleBlockStyle::Paragraph,
801 title_source: None,
802 title: None,
803 caption: None,
804 number: None,
805 anchor: None,
806 anchor_reftext: None,
807 attrlist: None,
808 },),
809 Block::Simple(SimpleBlock {
810 content: Content {
811 original: Span {
812 data: "block2",
813 line: 4,
814 col: 1,
815 offset: 13,
816 },
817 rendered: "block2",
818 },
819 source: Span {
820 data: "block2",
821 line: 4,
822 col: 1,
823 offset: 13,
824 },
825 style: SimpleBlockStyle::Paragraph,
826 title_source: None,
827 title: None,
828 caption: None,
829 number: None,
830 anchor: None,
831 anchor_reftext: None,
832 attrlist: None,
833 },),
834 ],
835 context: "example",
836 source: Span {
837 data: "====\nblock1\n\nblock2\n====",
838 line: 1,
839 col: 1,
840 offset: 0,
841 },
842 title_source: None,
843 title: None,
844 caption: None,
845 number: None,
846 anchor: None,
847 anchor_reftext: None,
848 attrlist: None,
849 }
850 );
851
852 assert_eq!(mi.item.content_model(), ContentModel::Compound);
853 assert_eq!(mi.item.raw_context().as_ref(), "example");
854 assert_eq!(mi.item.resolved_context().as_ref(), "example");
855 assert!(mi.item.declared_style().is_none());
856 assert!(mi.item.id().is_none());
857 assert!(mi.item.roles().is_empty());
858 assert!(mi.item.options().is_empty());
859 assert!(mi.item.title_source().is_none());
860 assert!(mi.item.title().is_none());
861 assert!(mi.item.anchor().is_none());
862 assert!(mi.item.anchor_reftext().is_none());
863 assert!(mi.item.attrlist().is_none());
864 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
865
866 let mut blocks = mi.item.child_blocks();
867 assert_eq!(
868 blocks.next().unwrap(),
869 &Block::Simple(SimpleBlock {
870 content: Content {
871 original: Span {
872 data: "block1",
873 line: 2,
874 col: 1,
875 offset: 5,
876 },
877 rendered: "block1",
878 },
879 source: Span {
880 data: "block1",
881 line: 2,
882 col: 1,
883 offset: 5,
884 },
885 style: SimpleBlockStyle::Paragraph,
886 title_source: None,
887 title: None,
888 caption: None,
889 number: None,
890 anchor: None,
891 anchor_reftext: None,
892 attrlist: None,
893 },)
894 );
895
896 assert_eq!(
897 blocks.next().unwrap(),
898 &Block::Simple(SimpleBlock {
899 content: Content {
900 original: Span {
901 data: "block2",
902 line: 4,
903 col: 1,
904 offset: 13,
905 },
906 rendered: "block2",
907 },
908 source: Span {
909 data: "block2",
910 line: 4,
911 col: 1,
912 offset: 13,
913 },
914 style: SimpleBlockStyle::Paragraph,
915 title_source: None,
916 title: None,
917 caption: None,
918 number: None,
919 anchor: None,
920 anchor_reftext: None,
921 attrlist: None,
922 },)
923 );
924
925 assert!(blocks.next().is_none());
926 }
927
928 #[test]
929 fn nested_blocks() {
930 let mut parser = Parser::default();
931
932 let maw = crate::blocks::CompoundDelimitedBlock::parse(
933 &BlockMetadata::new("====\nblock1\n\n=====\nblock2\n=====\n===="),
934 &mut parser,
935 )
936 .unwrap();
937
938 let mi = maw.item.unwrap().clone();
939
940 assert_eq!(
941 mi.item,
942 CompoundDelimitedBlock {
943 blocks: &[
944 Block::Simple(SimpleBlock {
945 content: Content {
946 original: Span {
947 data: "block1",
948 line: 2,
949 col: 1,
950 offset: 5,
951 },
952 rendered: "block1",
953 },
954 source: Span {
955 data: "block1",
956 line: 2,
957 col: 1,
958 offset: 5,
959 },
960 style: SimpleBlockStyle::Paragraph,
961 title_source: None,
962 title: None,
963 caption: None,
964 number: None,
965 anchor: None,
966 anchor_reftext: None,
967 attrlist: None,
968 },),
969 Block::CompoundDelimited(CompoundDelimitedBlock {
970 blocks: &[Block::Simple(SimpleBlock {
971 content: Content {
972 original: Span {
973 data: "block2",
974 line: 5,
975 col: 1,
976 offset: 19,
977 },
978 rendered: "block2",
979 },
980 source: Span {
981 data: "block2",
982 line: 5,
983 col: 1,
984 offset: 19,
985 },
986 style: SimpleBlockStyle::Paragraph,
987 title_source: None,
988 title: None,
989 caption: None,
990 number: None,
991 anchor: None,
992 anchor_reftext: None,
993 attrlist: None,
994 },),],
995 context: "example",
996 source: Span {
997 data: "=====\nblock2\n=====",
998 line: 4,
999 col: 1,
1000 offset: 13,
1001 },
1002 title_source: None,
1003 title: None,
1004 caption: None,
1005 number: None,
1006 anchor: None,
1007 anchor_reftext: None,
1008 attrlist: None,
1009 })
1010 ],
1011 context: "example",
1012 source: Span {
1013 data: "====\nblock1\n\n=====\nblock2\n=====\n====",
1014 line: 1,
1015 col: 1,
1016 offset: 0,
1017 },
1018 title_source: None,
1019 title: None,
1020 caption: None,
1021 number: None,
1022 anchor: None,
1023 anchor_reftext: None,
1024 attrlist: None,
1025 }
1026 );
1027
1028 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1029 assert_eq!(mi.item.raw_context().as_ref(), "example");
1030 assert_eq!(mi.item.resolved_context().as_ref(), "example");
1031 assert!(mi.item.declared_style().is_none());
1032 assert!(mi.item.id().is_none());
1033 assert!(mi.item.roles().is_empty());
1034 assert!(mi.item.options().is_empty());
1035 assert!(mi.item.title_source().is_none());
1036 assert!(mi.item.title().is_none());
1037 assert!(mi.item.anchor().is_none());
1038 assert!(mi.item.anchor_reftext().is_none());
1039 assert!(mi.item.attrlist().is_none());
1040 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1041
1042 let mut blocks = mi.item.child_blocks();
1043 assert_eq!(
1044 blocks.next().unwrap(),
1045 &Block::Simple(SimpleBlock {
1046 content: Content {
1047 original: Span {
1048 data: "block1",
1049 line: 2,
1050 col: 1,
1051 offset: 5,
1052 },
1053 rendered: "block1",
1054 },
1055 source: Span {
1056 data: "block1",
1057 line: 2,
1058 col: 1,
1059 offset: 5,
1060 },
1061 style: SimpleBlockStyle::Paragraph,
1062 title_source: None,
1063 title: None,
1064 caption: None,
1065 number: None,
1066 anchor: None,
1067 anchor_reftext: None,
1068 attrlist: None,
1069 },)
1070 );
1071
1072 assert_eq!(
1073 blocks.next().unwrap(),
1074 &Block::CompoundDelimited(CompoundDelimitedBlock {
1075 blocks: &[Block::Simple(SimpleBlock {
1076 content: Content {
1077 original: Span {
1078 data: "block2",
1079 line: 5,
1080 col: 1,
1081 offset: 19,
1082 },
1083 rendered: "block2",
1084 },
1085 source: Span {
1086 data: "block2",
1087 line: 5,
1088 col: 1,
1089 offset: 19,
1090 },
1091 style: SimpleBlockStyle::Paragraph,
1092 title_source: None,
1093 title: None,
1094 caption: None,
1095 number: None,
1096 anchor: None,
1097 anchor_reftext: None,
1098 attrlist: None,
1099 },),],
1100 context: "example",
1101 source: Span {
1102 data: "=====\nblock2\n=====",
1103 line: 4,
1104 col: 1,
1105 offset: 13,
1106 },
1107 title_source: None,
1108 title: None,
1109 caption: None,
1110 number: None,
1111 anchor: None,
1112 anchor_reftext: None,
1113 attrlist: None,
1114 })
1115 );
1116
1117 assert!(blocks.next().is_none());
1118 }
1119 #[test]
1120 fn no_panic_for_utf8_code_point_using_more_than_one_byte() {
1121 let mut parser = Parser::default();
1122 assert!(
1123 crate::blocks::CompoundDelimitedBlock::parse(
1124 &BlockMetadata::new("===😀"),
1125 &mut parser
1126 )
1127 .is_none()
1128 );
1129 }
1130 }
1131
1132 mod listing {
1133 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
1134
1135 #[test]
1136 fn empty() {
1137 let mut parser = Parser::default();
1138 assert!(
1139 crate::blocks::CompoundDelimitedBlock::parse(
1140 &BlockMetadata::new("----\n----"),
1141 &mut parser
1142 )
1143 .is_none()
1144 );
1145 }
1146
1147 #[test]
1148 fn multiple_lines() {
1149 let mut parser = Parser::default();
1150 assert!(
1151 crate::blocks::CompoundDelimitedBlock::parse(
1152 &BlockMetadata::new("----\nline1 \nline2\n----"),
1153 &mut parser
1154 )
1155 .is_none()
1156 );
1157 }
1158 }
1159
1160 mod literal {
1161 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
1162
1163 #[test]
1164 fn empty() {
1165 let mut parser = Parser::default();
1166 assert!(
1167 crate::blocks::CompoundDelimitedBlock::parse(
1168 &BlockMetadata::new("....\n...."),
1169 &mut parser
1170 )
1171 .is_none()
1172 );
1173 }
1174
1175 #[test]
1176 fn multiple_lines() {
1177 let mut parser = Parser::default();
1178 assert!(
1179 crate::blocks::CompoundDelimitedBlock::parse(
1180 &BlockMetadata::new("....\nline1 \nline2\n...."),
1181 &mut parser
1182 )
1183 .is_none()
1184 );
1185 }
1186 }
1187
1188 mod open {
1189 use crate::{
1190 blocks::{BreakType, ContentModel, metadata::BlockMetadata},
1191 tests::prelude::*,
1192 };
1193
1194 #[test]
1195 fn empty() {
1196 let mut parser = Parser::default();
1197
1198 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1199 &BlockMetadata::new("--\n--"),
1200 &mut parser,
1201 )
1202 .unwrap();
1203
1204 let mi = maw.item.unwrap().clone();
1205
1206 assert_eq!(
1207 mi.item,
1208 CompoundDelimitedBlock {
1209 blocks: &[],
1210 context: "open",
1211 source: Span {
1212 data: "--\n--",
1213 line: 1,
1214 col: 1,
1215 offset: 0,
1216 },
1217 title_source: None,
1218 title: None,
1219 caption: None,
1220 number: None,
1221 anchor: None,
1222 anchor_reftext: None,
1223 attrlist: None,
1224 }
1225 );
1226
1227 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1228 assert_eq!(mi.item.raw_context().as_ref(), "open");
1229 assert_eq!(mi.item.resolved_context().as_ref(), "open");
1230 assert!(mi.item.declared_style().is_none());
1231 assert!(mi.item.child_blocks().next().is_none());
1232 assert!(mi.item.id().is_none());
1233 assert!(mi.item.roles().is_empty());
1234 assert!(mi.item.options().is_empty());
1235 assert!(mi.item.title_source().is_none());
1236 assert!(mi.item.title().is_none());
1237 assert!(mi.item.anchor().is_none());
1238 assert!(mi.item.anchor_reftext().is_none());
1239 assert!(mi.item.attrlist().is_none());
1240 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1241 }
1242
1243 #[test]
1244 fn multiple_blocks() {
1245 let mut parser = Parser::default();
1246
1247 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1248 &BlockMetadata::new("--\nblock1\n\nblock2\n--"),
1249 &mut parser,
1250 )
1251 .unwrap();
1252
1253 let mi = maw.item.unwrap().clone();
1254
1255 assert_eq!(
1256 mi.item,
1257 CompoundDelimitedBlock {
1258 blocks: &[
1259 Block::Simple(SimpleBlock {
1260 content: Content {
1261 original: Span {
1262 data: "block1",
1263 line: 2,
1264 col: 1,
1265 offset: 3,
1266 },
1267 rendered: "block1",
1268 },
1269 source: Span {
1270 data: "block1",
1271 line: 2,
1272 col: 1,
1273 offset: 3,
1274 },
1275 style: SimpleBlockStyle::Paragraph,
1276 title_source: None,
1277 title: None,
1278 caption: None,
1279 number: None,
1280 anchor: None,
1281 anchor_reftext: None,
1282 attrlist: None,
1283 },),
1284 Block::Simple(SimpleBlock {
1285 content: Content {
1286 original: Span {
1287 data: "block2",
1288 line: 4,
1289 col: 1,
1290 offset: 11,
1291 },
1292 rendered: "block2",
1293 },
1294 source: Span {
1295 data: "block2",
1296 line: 4,
1297 col: 1,
1298 offset: 11,
1299 },
1300 style: SimpleBlockStyle::Paragraph,
1301 title_source: None,
1302 title: None,
1303 caption: None,
1304 number: None,
1305 anchor: None,
1306 anchor_reftext: None,
1307 attrlist: None,
1308 },),
1309 ],
1310 context: "open",
1311 source: Span {
1312 data: "--\nblock1\n\nblock2\n--",
1313 line: 1,
1314 col: 1,
1315 offset: 0,
1316 },
1317 title_source: None,
1318 title: None,
1319 caption: None,
1320 number: None,
1321 anchor: None,
1322 anchor_reftext: None,
1323 attrlist: None,
1324 }
1325 );
1326
1327 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1328 assert_eq!(mi.item.raw_context().as_ref(), "open");
1329 assert_eq!(mi.item.resolved_context().as_ref(), "open");
1330 assert!(mi.item.declared_style().is_none());
1331 assert!(mi.item.id().is_none());
1332 assert!(mi.item.roles().is_empty());
1333 assert!(mi.item.options().is_empty());
1334 assert!(mi.item.title_source().is_none());
1335 assert!(mi.item.title().is_none());
1336 assert!(mi.item.anchor().is_none());
1337 assert!(mi.item.anchor_reftext().is_none());
1338 assert!(mi.item.attrlist().is_none());
1339 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1340
1341 let mut blocks = mi.item.child_blocks();
1342 assert_eq!(
1343 blocks.next().unwrap(),
1344 &Block::Simple(SimpleBlock {
1345 content: Content {
1346 original: Span {
1347 data: "block1",
1348 line: 2,
1349 col: 1,
1350 offset: 3,
1351 },
1352 rendered: "block1",
1353 },
1354 source: Span {
1355 data: "block1",
1356 line: 2,
1357 col: 1,
1358 offset: 3,
1359 },
1360 style: SimpleBlockStyle::Paragraph,
1361 title_source: None,
1362 title: None,
1363 caption: None,
1364 number: None,
1365 anchor: None,
1366 anchor_reftext: None,
1367 attrlist: None,
1368 },)
1369 );
1370
1371 assert_eq!(
1372 blocks.next().unwrap(),
1373 &Block::Simple(SimpleBlock {
1374 content: Content {
1375 original: Span {
1376 data: "block2",
1377 line: 4,
1378 col: 1,
1379 offset: 11,
1380 },
1381 rendered: "block2",
1382 },
1383 source: Span {
1384 data: "block2",
1385 line: 4,
1386 col: 1,
1387 offset: 11,
1388 },
1389 style: SimpleBlockStyle::Paragraph,
1390 title_source: None,
1391 title: None,
1392 caption: None,
1393 number: None,
1394 anchor: None,
1395 anchor_reftext: None,
1396 attrlist: None,
1397 },)
1398 );
1399
1400 assert!(blocks.next().is_none());
1401 }
1402
1403 #[test]
1404 fn nested_blocks() {
1405 let mut parser = Parser::default();
1407
1408 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1409 &BlockMetadata::new("--\nblock1\n\n---\nblock2\n---\n--"),
1410 &mut parser,
1411 )
1412 .unwrap();
1413
1414 let mi = maw.item.unwrap().clone();
1415
1416 assert_eq!(
1417 mi.item,
1418 CompoundDelimitedBlock {
1419 blocks: &[
1420 Block::Simple(SimpleBlock {
1421 content: Content {
1422 original: Span {
1423 data: "block1",
1424 line: 2,
1425 col: 1,
1426 offset: 3,
1427 },
1428 rendered: "block1",
1429 },
1430 source: Span {
1431 data: "block1",
1432 line: 2,
1433 col: 1,
1434 offset: 3,
1435 },
1436 style: SimpleBlockStyle::Paragraph,
1437 title_source: None,
1438 title: None,
1439 caption: None,
1440 number: None,
1441 anchor: None,
1442 anchor_reftext: None,
1443 attrlist: None,
1444 },),
1445 Block::Break(Break {
1446 type_: BreakType::Thematic,
1447 source: Span {
1448 data: "---",
1449 line: 4,
1450 col: 1,
1451 offset: 11,
1452 },
1453 title_source: None,
1454 title: None,
1455 anchor: None,
1456 attrlist: None,
1457 },),
1458 Block::Simple(SimpleBlock {
1459 content: Content {
1460 original: Span {
1461 data: "block2\n---",
1462 line: 5,
1463 col: 1,
1464 offset: 15,
1465 },
1466 rendered: "block2\n---",
1467 },
1468 source: Span {
1469 data: "block2\n---",
1470 line: 5,
1471 col: 1,
1472 offset: 15,
1473 },
1474 style: SimpleBlockStyle::Paragraph,
1475 title_source: None,
1476 title: None,
1477 caption: None,
1478 number: None,
1479 anchor: None,
1480 anchor_reftext: None,
1481 attrlist: None,
1482 },),
1483 ],
1484 context: "open",
1485 source: Span {
1486 data: "--\nblock1\n\n---\nblock2\n---\n--",
1487 line: 1,
1488 col: 1,
1489 offset: 0,
1490 },
1491 title_source: None,
1492 title: None,
1493 caption: None,
1494 number: None,
1495 anchor: None,
1496 anchor_reftext: None,
1497 attrlist: None,
1498 }
1499 );
1500
1501 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1502 assert_eq!(mi.item.raw_context().as_ref(), "open");
1503 assert_eq!(mi.item.resolved_context().as_ref(), "open");
1504 assert!(mi.item.declared_style().is_none());
1505 assert!(mi.item.id().is_none());
1506 assert!(mi.item.roles().is_empty());
1507 assert!(mi.item.options().is_empty());
1508 assert!(mi.item.title_source().is_none());
1509 assert!(mi.item.title().is_none());
1510 assert!(mi.item.anchor().is_none());
1511 assert!(mi.item.anchor_reftext().is_none());
1512 assert!(mi.item.attrlist().is_none());
1513 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1514
1515 let mut blocks = mi.item.child_blocks();
1516 assert_eq!(
1517 blocks.next().unwrap(),
1518 &Block::Simple(SimpleBlock {
1519 content: Content {
1520 original: Span {
1521 data: "block1",
1522 line: 2,
1523 col: 1,
1524 offset: 3,
1525 },
1526 rendered: "block1",
1527 },
1528 source: Span {
1529 data: "block1",
1530 line: 2,
1531 col: 1,
1532 offset: 3,
1533 },
1534 style: SimpleBlockStyle::Paragraph,
1535 title_source: None,
1536 title: None,
1537 caption: None,
1538 number: None,
1539 anchor: None,
1540 anchor_reftext: None,
1541 attrlist: None,
1542 },)
1543 );
1544
1545 assert_eq!(
1546 blocks.next().unwrap(),
1547 &Block::Break(Break {
1548 type_: BreakType::Thematic,
1549 source: Span {
1550 data: "---",
1551 line: 4,
1552 col: 1,
1553 offset: 11,
1554 },
1555 title_source: None,
1556 title: None,
1557 anchor: None,
1558 attrlist: None,
1559 },)
1560 );
1561
1562 assert_eq!(
1563 blocks.next().unwrap(),
1564 &Block::Simple(SimpleBlock {
1565 content: Content {
1566 original: Span {
1567 data: "block2\n---",
1568 line: 5,
1569 col: 1,
1570 offset: 15,
1571 },
1572 rendered: "block2\n---",
1573 },
1574 source: Span {
1575 data: "block2\n---",
1576 line: 5,
1577 col: 1,
1578 offset: 15,
1579 },
1580 style: SimpleBlockStyle::Paragraph,
1581 title_source: None,
1582 title: None,
1583 caption: None,
1584 number: None,
1585 anchor: None,
1586 anchor_reftext: None,
1587 attrlist: None,
1588 },)
1589 );
1590
1591 assert!(blocks.next().is_none());
1592 }
1593 }
1594
1595 mod sidebar {
1596 use crate::{
1597 blocks::{ContentModel, metadata::BlockMetadata},
1598 tests::prelude::*,
1599 };
1600
1601 #[test]
1602 fn empty() {
1603 let mut parser = Parser::default();
1604
1605 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1606 &BlockMetadata::new("****\n****"),
1607 &mut parser,
1608 )
1609 .unwrap();
1610
1611 let mi = maw.item.unwrap().clone();
1612
1613 assert_eq!(
1614 mi.item,
1615 CompoundDelimitedBlock {
1616 blocks: &[],
1617 context: "sidebar",
1618 source: Span {
1619 data: "****\n****",
1620 line: 1,
1621 col: 1,
1622 offset: 0,
1623 },
1624 title_source: None,
1625 title: None,
1626 caption: None,
1627 number: None,
1628 anchor: None,
1629 anchor_reftext: None,
1630 attrlist: None,
1631 }
1632 );
1633
1634 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1635 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1636 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1637 assert!(mi.item.declared_style().is_none());
1638 assert!(mi.item.child_blocks().next().is_none());
1639 assert!(mi.item.id().is_none());
1640 assert!(mi.item.roles().is_empty());
1641 assert!(mi.item.options().is_empty());
1642 assert!(mi.item.title_source().is_none());
1643 assert!(mi.item.title().is_none());
1644 assert!(mi.item.anchor().is_none());
1645 assert!(mi.item.anchor_reftext().is_none());
1646 assert!(mi.item.attrlist().is_none());
1647 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1648 }
1649
1650 #[test]
1651 fn multiple_blocks() {
1652 let mut parser = Parser::default();
1653
1654 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1655 &BlockMetadata::new("****\nblock1\n\nblock2\n****"),
1656 &mut parser,
1657 )
1658 .unwrap();
1659
1660 let mi = maw.item.unwrap().clone();
1661
1662 assert_eq!(
1663 mi.item,
1664 CompoundDelimitedBlock {
1665 blocks: &[
1666 Block::Simple(SimpleBlock {
1667 content: Content {
1668 original: Span {
1669 data: "block1",
1670 line: 2,
1671 col: 1,
1672 offset: 5,
1673 },
1674 rendered: "block1",
1675 },
1676 source: Span {
1677 data: "block1",
1678 line: 2,
1679 col: 1,
1680 offset: 5,
1681 },
1682 style: SimpleBlockStyle::Paragraph,
1683 title_source: None,
1684 title: None,
1685 caption: None,
1686 number: None,
1687 anchor: None,
1688 anchor_reftext: None,
1689 attrlist: None,
1690 },),
1691 Block::Simple(SimpleBlock {
1692 content: Content {
1693 original: Span {
1694 data: "block2",
1695 line: 4,
1696 col: 1,
1697 offset: 13,
1698 },
1699 rendered: "block2",
1700 },
1701 source: Span {
1702 data: "block2",
1703 line: 4,
1704 col: 1,
1705 offset: 13,
1706 },
1707 style: SimpleBlockStyle::Paragraph,
1708 title_source: None,
1709 title: None,
1710 caption: None,
1711 number: None,
1712 anchor: None,
1713 anchor_reftext: None,
1714 attrlist: None,
1715 },),
1716 ],
1717 context: "sidebar",
1718 source: Span {
1719 data: "****\nblock1\n\nblock2\n****",
1720 line: 1,
1721 col: 1,
1722 offset: 0,
1723 },
1724 title_source: None,
1725 title: None,
1726 caption: None,
1727 number: None,
1728 anchor: None,
1729 anchor_reftext: None,
1730 attrlist: None,
1731 }
1732 );
1733
1734 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1735 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1736 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1737 assert!(mi.item.declared_style().is_none());
1738 assert!(mi.item.id().is_none());
1739 assert!(mi.item.roles().is_empty());
1740 assert!(mi.item.options().is_empty());
1741 assert!(mi.item.title_source().is_none());
1742 assert!(mi.item.title().is_none());
1743 assert!(mi.item.anchor().is_none());
1744 assert!(mi.item.anchor_reftext().is_none());
1745 assert!(mi.item.attrlist().is_none());
1746 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1747
1748 let mut blocks = mi.item.child_blocks();
1749 assert_eq!(
1750 blocks.next().unwrap(),
1751 &Block::Simple(SimpleBlock {
1752 content: Content {
1753 original: Span {
1754 data: "block1",
1755 line: 2,
1756 col: 1,
1757 offset: 5,
1758 },
1759 rendered: "block1",
1760 },
1761 source: Span {
1762 data: "block1",
1763 line: 2,
1764 col: 1,
1765 offset: 5,
1766 },
1767 style: SimpleBlockStyle::Paragraph,
1768 title_source: None,
1769 title: None,
1770 caption: None,
1771 number: None,
1772 anchor: None,
1773 anchor_reftext: None,
1774 attrlist: None,
1775 },)
1776 );
1777
1778 assert_eq!(
1779 blocks.next().unwrap(),
1780 &Block::Simple(SimpleBlock {
1781 content: Content {
1782 original: Span {
1783 data: "block2",
1784 line: 4,
1785 col: 1,
1786 offset: 13,
1787 },
1788 rendered: "block2",
1789 },
1790 source: Span {
1791 data: "block2",
1792 line: 4,
1793 col: 1,
1794 offset: 13,
1795 },
1796 style: SimpleBlockStyle::Paragraph,
1797 title_source: None,
1798 title: None,
1799 caption: None,
1800 number: None,
1801 anchor: None,
1802 anchor_reftext: None,
1803 attrlist: None,
1804 },)
1805 );
1806
1807 assert!(blocks.next().is_none());
1808 }
1809
1810 #[test]
1811 fn nested_blocks() {
1812 let mut parser = Parser::default();
1813
1814 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1815 &BlockMetadata::new("****\nblock1\n\n*****\nblock2\n*****\n****"),
1816 &mut parser,
1817 )
1818 .unwrap();
1819
1820 let mi = maw.item.unwrap().clone();
1821
1822 assert_eq!(
1823 mi.item,
1824 CompoundDelimitedBlock {
1825 blocks: &[
1826 Block::Simple(SimpleBlock {
1827 content: Content {
1828 original: Span {
1829 data: "block1",
1830 line: 2,
1831 col: 1,
1832 offset: 5,
1833 },
1834 rendered: "block1",
1835 },
1836 source: Span {
1837 data: "block1",
1838 line: 2,
1839 col: 1,
1840 offset: 5,
1841 },
1842 style: SimpleBlockStyle::Paragraph,
1843 title_source: None,
1844 title: None,
1845 caption: None,
1846 number: None,
1847 anchor: None,
1848 anchor_reftext: None,
1849 attrlist: None,
1850 },),
1851 Block::CompoundDelimited(CompoundDelimitedBlock {
1852 blocks: &[Block::Simple(SimpleBlock {
1853 content: Content {
1854 original: Span {
1855 data: "block2",
1856 line: 5,
1857 col: 1,
1858 offset: 19,
1859 },
1860 rendered: "block2",
1861 },
1862 source: Span {
1863 data: "block2",
1864 line: 5,
1865 col: 1,
1866 offset: 19,
1867 },
1868 style: SimpleBlockStyle::Paragraph,
1869 title_source: None,
1870 title: None,
1871 caption: None,
1872 number: None,
1873 anchor: None,
1874 anchor_reftext: None,
1875 attrlist: None,
1876 },),],
1877 context: "sidebar",
1878 source: Span {
1879 data: "*****\nblock2\n*****",
1880 line: 4,
1881 col: 1,
1882 offset: 13,
1883 },
1884 title_source: None,
1885 title: None,
1886 caption: None,
1887 number: None,
1888 anchor: None,
1889 anchor_reftext: None,
1890 attrlist: None,
1891 })
1892 ],
1893 context: "sidebar",
1894 source: Span {
1895 data: "****\nblock1\n\n*****\nblock2\n*****\n****",
1896 line: 1,
1897 col: 1,
1898 offset: 0,
1899 },
1900 title_source: None,
1901 title: None,
1902 caption: None,
1903 number: None,
1904 anchor: None,
1905 anchor_reftext: None,
1906 attrlist: None,
1907 }
1908 );
1909
1910 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1911 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1912 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1913 assert!(mi.item.declared_style().is_none());
1914 assert!(mi.item.id().is_none());
1915 assert!(mi.item.roles().is_empty());
1916 assert!(mi.item.options().is_empty());
1917 assert!(mi.item.title_source().is_none());
1918 assert!(mi.item.title().is_none());
1919 assert!(mi.item.anchor().is_none());
1920 assert!(mi.item.anchor_reftext().is_none());
1921 assert!(mi.item.attrlist().is_none());
1922 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1923
1924 let mut blocks = mi.item.child_blocks();
1925 assert_eq!(
1926 blocks.next().unwrap(),
1927 &Block::Simple(SimpleBlock {
1928 content: Content {
1929 original: Span {
1930 data: "block1",
1931 line: 2,
1932 col: 1,
1933 offset: 5,
1934 },
1935 rendered: "block1",
1936 },
1937 source: Span {
1938 data: "block1",
1939 line: 2,
1940 col: 1,
1941 offset: 5,
1942 },
1943 style: SimpleBlockStyle::Paragraph,
1944 title_source: None,
1945 title: None,
1946 caption: None,
1947 number: None,
1948 anchor: None,
1949 anchor_reftext: None,
1950 attrlist: None,
1951 },)
1952 );
1953
1954 assert_eq!(
1955 blocks.next().unwrap(),
1956 &Block::CompoundDelimited(CompoundDelimitedBlock {
1957 blocks: &[Block::Simple(SimpleBlock {
1958 content: Content {
1959 original: Span {
1960 data: "block2",
1961 line: 5,
1962 col: 1,
1963 offset: 19,
1964 },
1965 rendered: "block2",
1966 },
1967 source: Span {
1968 data: "block2",
1969 line: 5,
1970 col: 1,
1971 offset: 19,
1972 },
1973 style: SimpleBlockStyle::Paragraph,
1974 title_source: None,
1975 title: None,
1976 caption: None,
1977 number: None,
1978 anchor: None,
1979 anchor_reftext: None,
1980 attrlist: None,
1981 },),],
1982 context: "sidebar",
1983 source: Span {
1984 data: "*****\nblock2\n*****",
1985 line: 4,
1986 col: 1,
1987 offset: 13,
1988 },
1989 title_source: None,
1990 title: None,
1991 caption: None,
1992 number: None,
1993 anchor: None,
1994 anchor_reftext: None,
1995 attrlist: None,
1996 })
1997 );
1998
1999 assert!(blocks.next().is_none());
2000 }
2001 }
2002
2003 mod table {
2004 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
2005
2006 #[test]
2007 fn empty() {
2008 let mut parser = Parser::default();
2009 assert!(
2010 crate::blocks::CompoundDelimitedBlock::parse(
2011 &BlockMetadata::new("|===\n|==="),
2012 &mut parser
2013 )
2014 .is_none()
2015 );
2016
2017 let mut parser = Parser::default();
2018 assert!(
2019 crate::blocks::CompoundDelimitedBlock::parse(
2020 &BlockMetadata::new(",===\n,==="),
2021 &mut parser
2022 )
2023 .is_none()
2024 );
2025
2026 let mut parser = Parser::default();
2027 assert!(
2028 crate::blocks::CompoundDelimitedBlock::parse(
2029 &BlockMetadata::new(":===\n:==="),
2030 &mut parser
2031 )
2032 .is_none()
2033 );
2034
2035 let mut parser = Parser::default();
2036 assert!(
2037 crate::blocks::CompoundDelimitedBlock::parse(
2038 &BlockMetadata::new("!===\n!==="),
2039 &mut parser
2040 )
2041 .is_none()
2042 );
2043 }
2044
2045 #[test]
2046 fn multiple_lines() {
2047 let mut parser = Parser::default();
2048 assert!(
2049 crate::blocks::CompoundDelimitedBlock::parse(
2050 &BlockMetadata::new("|===\nline1 \nline2\n|==="),
2051 &mut parser
2052 )
2053 .is_none()
2054 );
2055
2056 let mut parser = Parser::default();
2057 assert!(
2058 crate::blocks::CompoundDelimitedBlock::parse(
2059 &BlockMetadata::new(",===\nline1 \nline2\n,==="),
2060 &mut parser
2061 )
2062 .is_none()
2063 );
2064
2065 let mut parser = Parser::default();
2066 assert!(
2067 crate::blocks::CompoundDelimitedBlock::parse(
2068 &BlockMetadata::new(":===\nline1 \nline2\n:==="),
2069 &mut parser
2070 )
2071 .is_none()
2072 );
2073
2074 let mut parser = Parser::default();
2075 assert!(
2076 crate::blocks::CompoundDelimitedBlock::parse(
2077 &BlockMetadata::new("!===\nline1 \nline2\n!==="),
2078 &mut parser
2079 )
2080 .is_none()
2081 );
2082 }
2083 }
2084
2085 mod pass {
2086 use crate::{blocks::metadata::BlockMetadata, tests::prelude::*};
2087
2088 #[test]
2089 fn empty() {
2090 let mut parser = Parser::default();
2091 assert!(
2092 crate::blocks::CompoundDelimitedBlock::parse(
2093 &BlockMetadata::new("++++\n++++"),
2094 &mut parser
2095 )
2096 .is_none()
2097 );
2098 }
2099
2100 #[test]
2101 fn multiple_lines() {
2102 let mut parser = Parser::default();
2103 assert!(
2104 crate::blocks::CompoundDelimitedBlock::parse(
2105 &BlockMetadata::new("++++\nline1 \nline2\n++++"),
2106 &mut parser
2107 )
2108 .is_none()
2109 );
2110 }
2111 }
2112
2113 #[test]
2114 fn impl_debug() {
2115 let mut parser = Parser::default();
2116
2117 let cdb = crate::blocks::CompoundDelimitedBlock::parse(
2118 &BlockMetadata::new("====\nblock1\n\nblock2\n===="),
2119 &mut parser,
2120 )
2121 .unwrap()
2122 .unwrap_if_no_warnings()
2123 .unwrap()
2124 .item;
2125
2126 assert_eq!(
2127 format!("{cdb:#?}"),
2128 r#"CompoundDelimitedBlock {
2129 blocks: &[
2130 Block::Simple(
2131 SimpleBlock {
2132 content: Content {
2133 original: Span {
2134 data: "block1",
2135 line: 2,
2136 col: 1,
2137 offset: 5,
2138 },
2139 rendered: "block1",
2140 },
2141 source: Span {
2142 data: "block1",
2143 line: 2,
2144 col: 1,
2145 offset: 5,
2146 },
2147 style: SimpleBlockStyle::Paragraph,
2148 title_source: None,
2149 title: None,
2150 caption: None,
2151 number: None,
2152 anchor: None,
2153 anchor_reftext: None,
2154 attrlist: None,
2155 },
2156 ),
2157 Block::Simple(
2158 SimpleBlock {
2159 content: Content {
2160 original: Span {
2161 data: "block2",
2162 line: 4,
2163 col: 1,
2164 offset: 13,
2165 },
2166 rendered: "block2",
2167 },
2168 source: Span {
2169 data: "block2",
2170 line: 4,
2171 col: 1,
2172 offset: 13,
2173 },
2174 style: SimpleBlockStyle::Paragraph,
2175 title_source: None,
2176 title: None,
2177 caption: None,
2178 number: None,
2179 anchor: None,
2180 anchor_reftext: None,
2181 attrlist: None,
2182 },
2183 ),
2184 ],
2185 context: "example",
2186 source: Span {
2187 data: "====\nblock1\n\nblock2\n====",
2188 line: 1,
2189 col: 1,
2190 offset: 0,
2191 },
2192 title_source: None,
2193 title: None,
2194 caption: None,
2195 number: None,
2196 anchor: None,
2197 anchor_reftext: None,
2198 attrlist: None,
2199}"#
2200 );
2201 }
2202}