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