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