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::{BreakType, 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::Break(Break {
1197 type_: BreakType::Thematic,
1198 source: Span {
1199 data: "---",
1200 line: 4,
1201 col: 1,
1202 offset: 11,
1203 },
1204 title_source: None,
1205 title: None,
1206 anchor: None,
1207 attrlist: None,
1208 },),
1209 Block::Simple(SimpleBlock {
1210 content: Content {
1211 original: Span {
1212 data: "block2\n---",
1213 line: 5,
1214 col: 1,
1215 offset: 15,
1216 },
1217 rendered: "block2\n---",
1218 },
1219 source: Span {
1220 data: "block2\n---",
1221 line: 5,
1222 col: 1,
1223 offset: 15,
1224 },
1225 title_source: None,
1226 title: None,
1227 anchor: None,
1228 anchor_reftext: None,
1229 attrlist: None,
1230 },),
1231 ],
1232 context: "open",
1233 source: Span {
1234 data: "--\nblock1\n\n---\nblock2\n---\n--",
1235 line: 1,
1236 col: 1,
1237 offset: 0,
1238 },
1239 title_source: None,
1240 title: None,
1241 anchor: None,
1242 anchor_reftext: None,
1243 attrlist: None,
1244 }
1245 );
1246
1247 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1248 assert_eq!(mi.item.raw_context().as_ref(), "open");
1249 assert_eq!(mi.item.resolved_context().as_ref(), "open");
1250 assert!(mi.item.declared_style().is_none());
1251 assert!(mi.item.id().is_none());
1252 assert!(mi.item.roles().is_empty());
1253 assert!(mi.item.options().is_empty());
1254 assert!(mi.item.title_source().is_none());
1255 assert!(mi.item.title().is_none());
1256 assert!(mi.item.anchor().is_none());
1257 assert!(mi.item.anchor_reftext().is_none());
1258 assert!(mi.item.attrlist().is_none());
1259 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1260
1261 let mut blocks = mi.item.nested_blocks();
1262 assert_eq!(
1263 blocks.next().unwrap(),
1264 &Block::Simple(SimpleBlock {
1265 content: Content {
1266 original: Span {
1267 data: "block1",
1268 line: 2,
1269 col: 1,
1270 offset: 3,
1271 },
1272 rendered: "block1",
1273 },
1274 source: Span {
1275 data: "block1",
1276 line: 2,
1277 col: 1,
1278 offset: 3,
1279 },
1280 title_source: None,
1281 title: None,
1282 anchor: None,
1283 anchor_reftext: None,
1284 attrlist: None,
1285 },)
1286 );
1287
1288 assert_eq!(
1289 blocks.next().unwrap(),
1290 &Block::Break(Break {
1291 type_: BreakType::Thematic,
1292 source: Span {
1293 data: "---",
1294 line: 4,
1295 col: 1,
1296 offset: 11,
1297 },
1298 title_source: None,
1299 title: None,
1300 anchor: None,
1301 attrlist: None,
1302 },)
1303 );
1304
1305 assert_eq!(
1306 blocks.next().unwrap(),
1307 &Block::Simple(SimpleBlock {
1308 content: Content {
1309 original: Span {
1310 data: "block2\n---",
1311 line: 5,
1312 col: 1,
1313 offset: 15,
1314 },
1315 rendered: "block2\n---",
1316 },
1317 source: Span {
1318 data: "block2\n---",
1319 line: 5,
1320 col: 1,
1321 offset: 15,
1322 },
1323 title_source: None,
1324 title: None,
1325 anchor: None,
1326 anchor_reftext: None,
1327 attrlist: None,
1328 },)
1329 );
1330
1331 assert!(blocks.next().is_none());
1332 }
1333 }
1334
1335 mod sidebar {
1336 use pretty_assertions_sorted::assert_eq;
1337
1338 use crate::{
1339 Parser,
1340 blocks::{ContentModel, IsBlock, metadata::BlockMetadata},
1341 content::SubstitutionGroup,
1342 tests::prelude::*,
1343 };
1344
1345 #[test]
1346 fn empty() {
1347 let mut parser = Parser::default();
1348
1349 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1350 &BlockMetadata::new("****\n****"),
1351 &mut parser,
1352 )
1353 .unwrap();
1354
1355 let mi = maw.item.unwrap().clone();
1356
1357 assert_eq!(
1358 mi.item,
1359 CompoundDelimitedBlock {
1360 blocks: &[],
1361 context: "sidebar",
1362 source: Span {
1363 data: "****\n****",
1364 line: 1,
1365 col: 1,
1366 offset: 0,
1367 },
1368 title_source: None,
1369 title: None,
1370 anchor: None,
1371 anchor_reftext: None,
1372 attrlist: None,
1373 }
1374 );
1375
1376 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1377 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1378 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1379 assert!(mi.item.declared_style().is_none());
1380 assert!(mi.item.nested_blocks().next().is_none());
1381 assert!(mi.item.id().is_none());
1382 assert!(mi.item.roles().is_empty());
1383 assert!(mi.item.options().is_empty());
1384 assert!(mi.item.title_source().is_none());
1385 assert!(mi.item.title().is_none());
1386 assert!(mi.item.anchor().is_none());
1387 assert!(mi.item.anchor_reftext().is_none());
1388 assert!(mi.item.attrlist().is_none());
1389 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1390 }
1391
1392 #[test]
1393 fn multiple_blocks() {
1394 let mut parser = Parser::default();
1395
1396 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1397 &BlockMetadata::new("****\nblock1\n\nblock2\n****"),
1398 &mut parser,
1399 )
1400 .unwrap();
1401
1402 let mi = maw.item.unwrap().clone();
1403
1404 assert_eq!(
1405 mi.item,
1406 CompoundDelimitedBlock {
1407 blocks: &[
1408 Block::Simple(SimpleBlock {
1409 content: Content {
1410 original: Span {
1411 data: "block1",
1412 line: 2,
1413 col: 1,
1414 offset: 5,
1415 },
1416 rendered: "block1",
1417 },
1418 source: Span {
1419 data: "block1",
1420 line: 2,
1421 col: 1,
1422 offset: 5,
1423 },
1424 title_source: None,
1425 title: None,
1426 anchor: None,
1427 anchor_reftext: None,
1428 attrlist: None,
1429 },),
1430 Block::Simple(SimpleBlock {
1431 content: Content {
1432 original: Span {
1433 data: "block2",
1434 line: 4,
1435 col: 1,
1436 offset: 13,
1437 },
1438 rendered: "block2",
1439 },
1440 source: Span {
1441 data: "block2",
1442 line: 4,
1443 col: 1,
1444 offset: 13,
1445 },
1446 title_source: None,
1447 title: None,
1448 anchor: None,
1449 anchor_reftext: None,
1450 attrlist: None,
1451 },),
1452 ],
1453 context: "sidebar",
1454 source: Span {
1455 data: "****\nblock1\n\nblock2\n****",
1456 line: 1,
1457 col: 1,
1458 offset: 0,
1459 },
1460 title_source: None,
1461 title: None,
1462 anchor: None,
1463 anchor_reftext: None,
1464 attrlist: None,
1465 }
1466 );
1467
1468 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1469 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1470 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1471 assert!(mi.item.declared_style().is_none());
1472 assert!(mi.item.id().is_none());
1473 assert!(mi.item.roles().is_empty());
1474 assert!(mi.item.options().is_empty());
1475 assert!(mi.item.title_source().is_none());
1476 assert!(mi.item.title().is_none());
1477 assert!(mi.item.anchor().is_none());
1478 assert!(mi.item.anchor_reftext().is_none());
1479 assert!(mi.item.attrlist().is_none());
1480 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1481
1482 let mut blocks = mi.item.nested_blocks();
1483 assert_eq!(
1484 blocks.next().unwrap(),
1485 &Block::Simple(SimpleBlock {
1486 content: Content {
1487 original: Span {
1488 data: "block1",
1489 line: 2,
1490 col: 1,
1491 offset: 5,
1492 },
1493 rendered: "block1",
1494 },
1495 source: Span {
1496 data: "block1",
1497 line: 2,
1498 col: 1,
1499 offset: 5,
1500 },
1501 title_source: None,
1502 title: None,
1503 anchor: None,
1504 anchor_reftext: None,
1505 attrlist: None,
1506 },)
1507 );
1508
1509 assert_eq!(
1510 blocks.next().unwrap(),
1511 &Block::Simple(SimpleBlock {
1512 content: Content {
1513 original: Span {
1514 data: "block2",
1515 line: 4,
1516 col: 1,
1517 offset: 13,
1518 },
1519 rendered: "block2",
1520 },
1521 source: Span {
1522 data: "block2",
1523 line: 4,
1524 col: 1,
1525 offset: 13,
1526 },
1527 title_source: None,
1528 title: None,
1529 anchor: None,
1530 anchor_reftext: None,
1531 attrlist: None,
1532 },)
1533 );
1534
1535 assert!(blocks.next().is_none());
1536 }
1537
1538 #[test]
1539 fn nested_blocks() {
1540 let mut parser = Parser::default();
1541
1542 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1543 &BlockMetadata::new("****\nblock1\n\n*****\nblock2\n*****\n****"),
1544 &mut parser,
1545 )
1546 .unwrap();
1547
1548 let mi = maw.item.unwrap().clone();
1549
1550 assert_eq!(
1551 mi.item,
1552 CompoundDelimitedBlock {
1553 blocks: &[
1554 Block::Simple(SimpleBlock {
1555 content: Content {
1556 original: Span {
1557 data: "block1",
1558 line: 2,
1559 col: 1,
1560 offset: 5,
1561 },
1562 rendered: "block1",
1563 },
1564 source: Span {
1565 data: "block1",
1566 line: 2,
1567 col: 1,
1568 offset: 5,
1569 },
1570 title_source: None,
1571 title: None,
1572 anchor: None,
1573 anchor_reftext: None,
1574 attrlist: None,
1575 },),
1576 Block::CompoundDelimited(CompoundDelimitedBlock {
1577 blocks: &[Block::Simple(SimpleBlock {
1578 content: Content {
1579 original: Span {
1580 data: "block2",
1581 line: 5,
1582 col: 1,
1583 offset: 19,
1584 },
1585 rendered: "block2",
1586 },
1587 source: Span {
1588 data: "block2",
1589 line: 5,
1590 col: 1,
1591 offset: 19,
1592 },
1593 title_source: None,
1594 title: None,
1595 anchor: None,
1596 anchor_reftext: None,
1597 attrlist: None,
1598 },),],
1599 context: "sidebar",
1600 source: Span {
1601 data: "*****\nblock2\n*****",
1602 line: 4,
1603 col: 1,
1604 offset: 13,
1605 },
1606 title_source: None,
1607 title: None,
1608 anchor: None,
1609 anchor_reftext: None,
1610 attrlist: None,
1611 })
1612 ],
1613 context: "sidebar",
1614 source: Span {
1615 data: "****\nblock1\n\n*****\nblock2\n*****\n****",
1616 line: 1,
1617 col: 1,
1618 offset: 0,
1619 },
1620 title_source: None,
1621 title: None,
1622 anchor: None,
1623 anchor_reftext: None,
1624 attrlist: None,
1625 }
1626 );
1627
1628 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1629 assert_eq!(mi.item.raw_context().as_ref(), "sidebar");
1630 assert_eq!(mi.item.resolved_context().as_ref(), "sidebar");
1631 assert!(mi.item.declared_style().is_none());
1632 assert!(mi.item.id().is_none());
1633 assert!(mi.item.roles().is_empty());
1634 assert!(mi.item.options().is_empty());
1635 assert!(mi.item.title_source().is_none());
1636 assert!(mi.item.title().is_none());
1637 assert!(mi.item.anchor().is_none());
1638 assert!(mi.item.anchor_reftext().is_none());
1639 assert!(mi.item.attrlist().is_none());
1640 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1641
1642 let mut blocks = mi.item.nested_blocks();
1643 assert_eq!(
1644 blocks.next().unwrap(),
1645 &Block::Simple(SimpleBlock {
1646 content: Content {
1647 original: Span {
1648 data: "block1",
1649 line: 2,
1650 col: 1,
1651 offset: 5,
1652 },
1653 rendered: "block1",
1654 },
1655 source: Span {
1656 data: "block1",
1657 line: 2,
1658 col: 1,
1659 offset: 5,
1660 },
1661 title_source: None,
1662 title: None,
1663 anchor: None,
1664 anchor_reftext: None,
1665 attrlist: None,
1666 },)
1667 );
1668
1669 assert_eq!(
1670 blocks.next().unwrap(),
1671 &Block::CompoundDelimited(CompoundDelimitedBlock {
1672 blocks: &[Block::Simple(SimpleBlock {
1673 content: Content {
1674 original: Span {
1675 data: "block2",
1676 line: 5,
1677 col: 1,
1678 offset: 19,
1679 },
1680 rendered: "block2",
1681 },
1682 source: Span {
1683 data: "block2",
1684 line: 5,
1685 col: 1,
1686 offset: 19,
1687 },
1688 title_source: None,
1689 title: None,
1690 anchor: None,
1691 anchor_reftext: None,
1692 attrlist: None,
1693 },),],
1694 context: "sidebar",
1695 source: Span {
1696 data: "*****\nblock2\n*****",
1697 line: 4,
1698 col: 1,
1699 offset: 13,
1700 },
1701 title_source: None,
1702 title: None,
1703 anchor: None,
1704 anchor_reftext: None,
1705 attrlist: None,
1706 })
1707 );
1708
1709 assert!(blocks.next().is_none());
1710 }
1711 }
1712
1713 mod table {
1714 use crate::{Parser, blocks::metadata::BlockMetadata};
1715
1716 #[test]
1717 fn empty() {
1718 let mut parser = Parser::default();
1719 assert!(
1720 crate::blocks::CompoundDelimitedBlock::parse(
1721 &BlockMetadata::new("|===\n|==="),
1722 &mut parser
1723 )
1724 .is_none()
1725 );
1726
1727 let mut parser = Parser::default();
1728 assert!(
1729 crate::blocks::CompoundDelimitedBlock::parse(
1730 &BlockMetadata::new(",===\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(":===\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("!===\n!==="),
1749 &mut parser
1750 )
1751 .is_none()
1752 );
1753 }
1754
1755 #[test]
1756 fn multiple_lines() {
1757 let mut parser = Parser::default();
1758 assert!(
1759 crate::blocks::CompoundDelimitedBlock::parse(
1760 &BlockMetadata::new("|===\nline1 \nline2\n|==="),
1761 &mut parser
1762 )
1763 .is_none()
1764 );
1765
1766 let mut parser = Parser::default();
1767 assert!(
1768 crate::blocks::CompoundDelimitedBlock::parse(
1769 &BlockMetadata::new(",===\nline1 \nline2\n,==="),
1770 &mut parser
1771 )
1772 .is_none()
1773 );
1774
1775 let mut parser = Parser::default();
1776 assert!(
1777 crate::blocks::CompoundDelimitedBlock::parse(
1778 &BlockMetadata::new(":===\nline1 \nline2\n:==="),
1779 &mut parser
1780 )
1781 .is_none()
1782 );
1783
1784 let mut parser = Parser::default();
1785 assert!(
1786 crate::blocks::CompoundDelimitedBlock::parse(
1787 &BlockMetadata::new("!===\nline1 \nline2\n!==="),
1788 &mut parser
1789 )
1790 .is_none()
1791 );
1792 }
1793 }
1794
1795 mod pass {
1796 use crate::{Parser, blocks::metadata::BlockMetadata};
1797
1798 #[test]
1799 fn empty() {
1800 let mut parser = Parser::default();
1801 assert!(
1802 crate::blocks::CompoundDelimitedBlock::parse(
1803 &BlockMetadata::new("++++\n++++"),
1804 &mut parser
1805 )
1806 .is_none()
1807 );
1808 }
1809
1810 #[test]
1811 fn multiple_lines() {
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 quote {
1824 use pretty_assertions_sorted::assert_eq;
1825
1826 use crate::{
1827 Parser,
1828 blocks::{ContentModel, IsBlock, metadata::BlockMetadata},
1829 content::SubstitutionGroup,
1830 tests::prelude::*,
1831 };
1832
1833 #[test]
1834 fn empty() {
1835 let mut parser = Parser::default();
1836
1837 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1838 &BlockMetadata::new("____\n____"),
1839 &mut parser,
1840 )
1841 .unwrap();
1842
1843 let mi = maw.item.unwrap().clone();
1844
1845 assert_eq!(
1846 mi.item,
1847 CompoundDelimitedBlock {
1848 blocks: &[],
1849 context: "quote",
1850 source: Span {
1851 data: "____\n____",
1852 line: 1,
1853 col: 1,
1854 offset: 0,
1855 },
1856 title_source: None,
1857 title: None,
1858 anchor: None,
1859 anchor_reftext: None,
1860 attrlist: None,
1861 }
1862 );
1863
1864 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1865 assert_eq!(mi.item.raw_context().as_ref(), "quote");
1866 assert_eq!(mi.item.resolved_context().as_ref(), "quote");
1867 assert!(mi.item.declared_style().is_none());
1868 assert!(mi.item.nested_blocks().next().is_none());
1869 assert!(mi.item.id().is_none());
1870 assert!(mi.item.roles().is_empty());
1871 assert!(mi.item.options().is_empty());
1872 assert!(mi.item.title_source().is_none());
1873 assert!(mi.item.title().is_none());
1874 assert!(mi.item.anchor().is_none());
1875 assert!(mi.item.anchor_reftext().is_none());
1876 assert!(mi.item.attrlist().is_none());
1877 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1878 }
1879
1880 #[test]
1881 fn multiple_blocks() {
1882 let mut parser = Parser::default();
1883
1884 let maw = crate::blocks::CompoundDelimitedBlock::parse(
1885 &BlockMetadata::new("____\nblock1\n\nblock2\n____"),
1886 &mut parser,
1887 )
1888 .unwrap();
1889
1890 let mi = maw.item.unwrap().clone();
1891
1892 assert_eq!(
1893 mi.item,
1894 CompoundDelimitedBlock {
1895 blocks: &[
1896 Block::Simple(SimpleBlock {
1897 content: Content {
1898 original: Span {
1899 data: "block1",
1900 line: 2,
1901 col: 1,
1902 offset: 5,
1903 },
1904 rendered: "block1",
1905 },
1906 source: Span {
1907 data: "block1",
1908 line: 2,
1909 col: 1,
1910 offset: 5,
1911 },
1912 title_source: None,
1913 title: None,
1914 anchor: None,
1915 anchor_reftext: None,
1916 attrlist: None,
1917 },),
1918 Block::Simple(SimpleBlock {
1919 content: Content {
1920 original: Span {
1921 data: "block2",
1922 line: 4,
1923 col: 1,
1924 offset: 13,
1925 },
1926 rendered: "block2",
1927 },
1928 source: Span {
1929 data: "block2",
1930 line: 4,
1931 col: 1,
1932 offset: 13,
1933 },
1934 title_source: None,
1935 title: None,
1936 anchor: None,
1937 anchor_reftext: None,
1938 attrlist: None,
1939 },),
1940 ],
1941 context: "quote",
1942 source: Span {
1943 data: "____\nblock1\n\nblock2\n____",
1944 line: 1,
1945 col: 1,
1946 offset: 0,
1947 },
1948 title_source: None,
1949 title: None,
1950 anchor: None,
1951 anchor_reftext: None,
1952 attrlist: None,
1953 }
1954 );
1955
1956 assert_eq!(mi.item.content_model(), ContentModel::Compound);
1957 assert_eq!(mi.item.raw_context().as_ref(), "quote");
1958 assert_eq!(mi.item.resolved_context().as_ref(), "quote");
1959 assert!(mi.item.declared_style().is_none());
1960 assert!(mi.item.id().is_none());
1961 assert!(mi.item.roles().is_empty());
1962 assert!(mi.item.options().is_empty());
1963 assert!(mi.item.title_source().is_none());
1964 assert!(mi.item.title().is_none());
1965 assert!(mi.item.anchor().is_none());
1966 assert!(mi.item.anchor_reftext().is_none());
1967 assert!(mi.item.attrlist().is_none());
1968 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
1969
1970 let mut blocks = mi.item.nested_blocks();
1971 assert_eq!(
1972 blocks.next().unwrap(),
1973 &Block::Simple(SimpleBlock {
1974 content: Content {
1975 original: Span {
1976 data: "block1",
1977 line: 2,
1978 col: 1,
1979 offset: 5,
1980 },
1981 rendered: "block1",
1982 },
1983 source: Span {
1984 data: "block1",
1985 line: 2,
1986 col: 1,
1987 offset: 5,
1988 },
1989 title_source: None,
1990 title: None,
1991 anchor: None,
1992 anchor_reftext: None,
1993 attrlist: None,
1994 },)
1995 );
1996
1997 assert_eq!(
1998 blocks.next().unwrap(),
1999 &Block::Simple(SimpleBlock {
2000 content: Content {
2001 original: Span {
2002 data: "block2",
2003 line: 4,
2004 col: 1,
2005 offset: 13,
2006 },
2007 rendered: "block2",
2008 },
2009 source: Span {
2010 data: "block2",
2011 line: 4,
2012 col: 1,
2013 offset: 13,
2014 },
2015 title_source: None,
2016 title: None,
2017 anchor: None,
2018 anchor_reftext: None,
2019 attrlist: None,
2020 },)
2021 );
2022
2023 assert!(blocks.next().is_none());
2024 }
2025
2026 #[test]
2027 fn nested_blocks() {
2028 let mut parser = Parser::default();
2029
2030 let maw = crate::blocks::CompoundDelimitedBlock::parse(
2031 &BlockMetadata::new("____\nblock1\n\n_____\nblock2\n_____\n____"),
2032 &mut parser,
2033 )
2034 .unwrap();
2035
2036 let mi = maw.item.unwrap().clone();
2037
2038 assert_eq!(
2039 mi.item,
2040 CompoundDelimitedBlock {
2041 blocks: &[
2042 Block::Simple(SimpleBlock {
2043 content: Content {
2044 original: Span {
2045 data: "block1",
2046 line: 2,
2047 col: 1,
2048 offset: 5,
2049 },
2050 rendered: "block1",
2051 },
2052 source: Span {
2053 data: "block1",
2054 line: 2,
2055 col: 1,
2056 offset: 5,
2057 },
2058 title_source: None,
2059 title: None,
2060 anchor: None,
2061 anchor_reftext: None,
2062 attrlist: None,
2063 },),
2064 Block::CompoundDelimited(CompoundDelimitedBlock {
2065 blocks: &[Block::Simple(SimpleBlock {
2066 content: Content {
2067 original: Span {
2068 data: "block2",
2069 line: 5,
2070 col: 1,
2071 offset: 19,
2072 },
2073 rendered: "block2",
2074 },
2075 source: Span {
2076 data: "block2",
2077 line: 5,
2078 col: 1,
2079 offset: 19,
2080 },
2081 title_source: None,
2082 title: None,
2083 anchor: None,
2084 anchor_reftext: None,
2085 attrlist: None,
2086 },),],
2087 context: "quote",
2088 source: Span {
2089 data: "_____\nblock2\n_____",
2090 line: 4,
2091 col: 1,
2092 offset: 13,
2093 },
2094 title_source: None,
2095 title: None,
2096 anchor: None,
2097 anchor_reftext: None,
2098 attrlist: None,
2099 })
2100 ],
2101 context: "quote",
2102 source: Span {
2103 data: "____\nblock1\n\n_____\nblock2\n_____\n____",
2104 line: 1,
2105 col: 1,
2106 offset: 0,
2107 },
2108 title_source: None,
2109 title: None,
2110 anchor: None,
2111 anchor_reftext: None,
2112 attrlist: None,
2113 }
2114 );
2115
2116 assert_eq!(mi.item.content_model(), ContentModel::Compound);
2117 assert_eq!(mi.item.raw_context().as_ref(), "quote");
2118 assert_eq!(mi.item.resolved_context().as_ref(), "quote");
2119 assert!(mi.item.declared_style().is_none());
2120 assert!(mi.item.id().is_none());
2121 assert!(mi.item.roles().is_empty());
2122 assert!(mi.item.options().is_empty());
2123 assert!(mi.item.title_source().is_none());
2124 assert!(mi.item.title().is_none());
2125 assert!(mi.item.anchor().is_none());
2126 assert!(mi.item.anchor_reftext().is_none());
2127 assert!(mi.item.attrlist().is_none());
2128 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
2129
2130 let mut blocks = mi.item.nested_blocks();
2131 assert_eq!(
2132 blocks.next().unwrap(),
2133 &Block::Simple(SimpleBlock {
2134 content: Content {
2135 original: Span {
2136 data: "block1",
2137 line: 2,
2138 col: 1,
2139 offset: 5,
2140 },
2141 rendered: "block1",
2142 },
2143 source: Span {
2144 data: "block1",
2145 line: 2,
2146 col: 1,
2147 offset: 5,
2148 },
2149 title_source: None,
2150 title: None,
2151 anchor: None,
2152 anchor_reftext: None,
2153 attrlist: None,
2154 },)
2155 );
2156
2157 assert_eq!(
2158 blocks.next().unwrap(),
2159 &Block::CompoundDelimited(CompoundDelimitedBlock {
2160 blocks: &[Block::Simple(SimpleBlock {
2161 content: Content {
2162 original: Span {
2163 data: "block2",
2164 line: 5,
2165 col: 1,
2166 offset: 19,
2167 },
2168 rendered: "block2",
2169 },
2170 source: Span {
2171 data: "block2",
2172 line: 5,
2173 col: 1,
2174 offset: 19,
2175 },
2176 title_source: None,
2177 title: None,
2178 anchor: None,
2179 anchor_reftext: None,
2180 attrlist: None,
2181 },),],
2182 context: "quote",
2183 source: Span {
2184 data: "_____\nblock2\n_____",
2185 line: 4,
2186 col: 1,
2187 offset: 13,
2188 },
2189 title_source: None,
2190 title: None,
2191 anchor: None,
2192 anchor_reftext: None,
2193 attrlist: None,
2194 })
2195 );
2196
2197 assert!(blocks.next().is_none());
2198 }
2199 }
2200
2201 #[test]
2202 fn impl_debug() {
2203 let mut parser = Parser::default();
2204
2205 let cdb = crate::blocks::CompoundDelimitedBlock::parse(
2206 &BlockMetadata::new("====\nblock1\n\nblock2\n===="),
2207 &mut parser,
2208 )
2209 .unwrap()
2210 .unwrap_if_no_warnings()
2211 .unwrap()
2212 .item;
2213
2214 assert_eq!(
2215 format!("{cdb:#?}"),
2216 r#"CompoundDelimitedBlock {
2217 blocks: &[
2218 Block::Simple(
2219 SimpleBlock {
2220 content: Content {
2221 original: Span {
2222 data: "block1",
2223 line: 2,
2224 col: 1,
2225 offset: 5,
2226 },
2227 rendered: "block1",
2228 },
2229 source: Span {
2230 data: "block1",
2231 line: 2,
2232 col: 1,
2233 offset: 5,
2234 },
2235 title_source: None,
2236 title: None,
2237 anchor: None,
2238 anchor_reftext: None,
2239 attrlist: None,
2240 },
2241 ),
2242 Block::Simple(
2243 SimpleBlock {
2244 content: Content {
2245 original: Span {
2246 data: "block2",
2247 line: 4,
2248 col: 1,
2249 offset: 13,
2250 },
2251 rendered: "block2",
2252 },
2253 source: Span {
2254 data: "block2",
2255 line: 4,
2256 col: 1,
2257 offset: 13,
2258 },
2259 title_source: None,
2260 title: None,
2261 anchor: None,
2262 anchor_reftext: None,
2263 attrlist: None,
2264 },
2265 ),
2266 ],
2267 context: "example",
2268 source: Span {
2269 data: "====\nblock1\n\nblock2\n====",
2270 line: 1,
2271 col: 1,
2272 offset: 0,
2273 },
2274 title_source: None,
2275 title: None,
2276 anchor: None,
2277 anchor_reftext: None,
2278 attrlist: None,
2279}"#
2280 );
2281 }
2282}