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