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