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