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