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