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