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