1use std::{marker::PhantomData, slice::Iter};
4
5use self_cell::self_cell;
6
7use crate::{
8 Parser, Span,
9 attributes::Attrlist,
10 blocks::{Block, ContentModel, IsBlock, parse_utils::parse_blocks_until},
11 document::{Catalog, Header},
12 internal::debug::DebugSliceReference,
13 parser::SourceMap,
14 strings::CowStr,
15 warnings::Warning,
16};
17
18#[derive(Eq, PartialEq)]
31pub struct Document<'src> {
32 internal: Internal,
33 _phantom: PhantomData<&'src ()>,
34}
35
36#[derive(Debug, Eq, PartialEq)]
39struct InternalDependent<'src> {
40 header: Header<'src>,
41 blocks: Vec<Block<'src>>,
42 source: Span<'src>,
43 warnings: Vec<Warning<'src>>,
44 source_map: SourceMap,
45 catalog: Catalog,
46}
47
48self_cell! {
49 struct Internal {
51 owner: String,
52 #[covariant]
53 dependent: InternalDependent,
54 }
55 impl {Debug, Eq, PartialEq}
56}
57
58impl<'src> Document<'src> {
59 pub(crate) fn parse(source: &str, source_map: SourceMap, parser: &mut Parser) -> Self {
60 let owned_source = source.to_string();
61
62 let internal = Internal::new(owned_source, |owned_src| {
63 let source = Span::new(owned_src);
64
65 let mi = Header::parse(source, parser);
66 let next = mi.item.after;
67
68 parser.sectnumlevels = parser
69 .attribute_value("sectnumlevels")
70 .as_maybe_str()
71 .and_then(|s| s.parse::<usize>().ok())
72 .unwrap_or(3);
73
74 let header = mi.item.item;
75 let mut warnings = mi.warnings;
76
77 let mut maw_blocks = parse_blocks_until(next, |_| false, parser);
78
79 if !maw_blocks.warnings.is_empty() {
80 warnings.append(&mut maw_blocks.warnings);
81 }
82
83 InternalDependent {
84 header,
85 blocks: maw_blocks.item.item,
86 source: source.trim_trailing_whitespace(),
87 warnings,
88 source_map,
89 catalog: parser.take_catalog(),
90 }
91 });
92
93 Self {
94 internal,
95 _phantom: PhantomData,
96 }
97 }
98
99 pub fn header(&self) -> &Header<'_> {
101 &self.internal.borrow_dependent().header
102 }
103
104 pub fn warnings(&self) -> Iter<'_, Warning<'_>> {
106 self.internal.borrow_dependent().warnings.iter()
107 }
108
109 pub fn span(&self) -> Span<'_> {
111 self.internal.borrow_dependent().source
112 }
113
114 pub fn source_map(&self) -> &SourceMap {
116 &self.internal.borrow_dependent().source_map
117 }
118
119 pub fn catalog(&self) -> &Catalog {
121 &self.internal.borrow_dependent().catalog
122 }
123}
124
125impl<'src> IsBlock<'src> for Document<'src> {
126 fn content_model(&self) -> ContentModel {
127 ContentModel::Compound
128 }
129
130 fn raw_context(&self) -> CowStr<'src> {
131 "document".into()
132 }
133
134 fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
135 self.internal.borrow_dependent().blocks.iter()
136 }
137
138 fn title_source(&'src self) -> Option<Span<'src>> {
139 None
141 }
142
143 fn title(&self) -> Option<&str> {
144 None
146 }
147
148 fn anchor(&'src self) -> Option<Span<'src>> {
149 None
150 }
151
152 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
153 None
154 }
155
156 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
157 None
159 }
160}
161
162impl std::fmt::Debug for Document<'_> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 let dependent = self.internal.borrow_dependent();
165 f.debug_struct("Document")
166 .field("header", &dependent.header)
167 .field("blocks", &DebugSliceReference(&dependent.blocks))
168 .field("source", &dependent.source)
169 .field("warnings", &DebugSliceReference(&dependent.warnings))
170 .field("source_map", &dependent.source_map)
171 .field("catalog", &dependent.catalog)
172 .finish()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 #![allow(clippy::unwrap_used)]
179
180 use std::{collections::HashMap, ops::Deref};
181
182 use pretty_assertions_sorted::assert_eq;
183
184 use crate::{
185 Parser,
186 blocks::{ContentModel, IsBlock, MediaType},
187 content::SubstitutionGroup,
188 document::RefType,
189 tests::prelude::*,
190 warnings::WarningType,
191 };
192
193 #[test]
194 fn empty_source() {
195 let doc = Parser::default().parse("");
196
197 assert_eq!(doc.content_model(), ContentModel::Compound);
198 assert_eq!(doc.raw_context().deref(), "document");
199 assert_eq!(doc.resolved_context().deref(), "document");
200 assert!(doc.declared_style().is_none());
201 assert!(doc.id().is_none());
202 assert!(doc.roles().is_empty());
203 assert!(doc.title_source().is_none());
204 assert!(doc.title().is_none());
205 assert!(doc.anchor().is_none());
206 assert!(doc.anchor_reftext().is_none());
207 assert!(doc.attrlist().is_none());
208 assert_eq!(doc.substitution_group(), SubstitutionGroup::Normal);
209
210 assert_eq!(
211 doc,
212 Document {
213 header: Header {
214 title_source: None,
215 title: None,
216 attributes: &[],
217 author_line: None,
218 revision_line: None,
219 comments: &[],
220 source: Span {
221 data: "",
222 line: 1,
223 col: 1,
224 offset: 0
225 },
226 },
227 source: Span {
228 data: "",
229 line: 1,
230 col: 1,
231 offset: 0
232 },
233 blocks: &[],
234 warnings: &[],
235 source_map: SourceMap(&[]),
236 catalog: Catalog::default(),
237 }
238 );
239 }
240
241 #[test]
242 fn only_spaces() {
243 assert_eq!(
244 Parser::default().parse(" "),
245 Document {
246 header: Header {
247 title_source: None,
248 title: None,
249 attributes: &[],
250 author_line: None,
251 revision_line: None,
252 comments: &[],
253 source: Span {
254 data: "",
255 line: 1,
256 col: 5,
257 offset: 4
258 },
259 },
260 source: Span {
261 data: "",
262 line: 1,
263 col: 1,
264 offset: 0
265 },
266 blocks: &[],
267 warnings: &[],
268 source_map: SourceMap(&[]),
269 catalog: Catalog::default(),
270 }
271 );
272 }
273
274 #[test]
275 fn one_simple_block() {
276 let doc = Parser::default().parse("abc");
277 assert_eq!(
278 doc,
279 Document {
280 header: Header {
281 title_source: None,
282 title: None,
283 attributes: &[],
284 author_line: None,
285 revision_line: None,
286 comments: &[],
287 source: Span {
288 data: "",
289 line: 1,
290 col: 1,
291 offset: 0
292 },
293 },
294 source: Span {
295 data: "abc",
296 line: 1,
297 col: 1,
298 offset: 0
299 },
300 blocks: &[Block::Simple(SimpleBlock {
301 content: Content {
302 original: Span {
303 data: "abc",
304 line: 1,
305 col: 1,
306 offset: 0,
307 },
308 rendered: "abc",
309 },
310 source: Span {
311 data: "abc",
312 line: 1,
313 col: 1,
314 offset: 0,
315 },
316 title_source: None,
317 title: None,
318 anchor: None,
319 anchor_reftext: None,
320 attrlist: None,
321 })],
322 warnings: &[],
323 source_map: SourceMap(&[]),
324 catalog: Catalog::default(),
325 }
326 );
327
328 assert!(doc.anchor().is_none());
329 assert!(doc.anchor_reftext().is_none());
330 }
331
332 #[test]
333 fn two_simple_blocks() {
334 assert_eq!(
335 Parser::default().parse("abc\n\ndef"),
336 Document {
337 header: Header {
338 title_source: None,
339 title: None,
340 attributes: &[],
341 author_line: None,
342 revision_line: None,
343 comments: &[],
344 source: Span {
345 data: "",
346 line: 1,
347 col: 1,
348 offset: 0
349 },
350 },
351 source: Span {
352 data: "abc\n\ndef",
353 line: 1,
354 col: 1,
355 offset: 0
356 },
357 blocks: &[
358 Block::Simple(SimpleBlock {
359 content: Content {
360 original: Span {
361 data: "abc",
362 line: 1,
363 col: 1,
364 offset: 0,
365 },
366 rendered: "abc",
367 },
368 source: Span {
369 data: "abc",
370 line: 1,
371 col: 1,
372 offset: 0,
373 },
374 title_source: None,
375 title: None,
376 anchor: None,
377 anchor_reftext: None,
378 attrlist: None,
379 }),
380 Block::Simple(SimpleBlock {
381 content: Content {
382 original: Span {
383 data: "def",
384 line: 3,
385 col: 1,
386 offset: 5,
387 },
388 rendered: "def",
389 },
390 source: Span {
391 data: "def",
392 line: 3,
393 col: 1,
394 offset: 5,
395 },
396 title_source: None,
397 title: None,
398 anchor: None,
399 anchor_reftext: None,
400 attrlist: None,
401 })
402 ],
403 warnings: &[],
404 source_map: SourceMap(&[]),
405 catalog: Catalog::default(),
406 }
407 );
408 }
409
410 #[test]
411 fn two_blocks_and_title() {
412 assert_eq!(
413 Parser::default().parse("= Example Title\n\nabc\n\ndef"),
414 Document {
415 header: Header {
416 title_source: Some(Span {
417 data: "Example Title",
418 line: 1,
419 col: 3,
420 offset: 2,
421 }),
422 title: Some("Example Title"),
423 attributes: &[],
424 author_line: None,
425 revision_line: None,
426 comments: &[],
427 source: Span {
428 data: "= Example Title",
429 line: 1,
430 col: 1,
431 offset: 0,
432 }
433 },
434 blocks: &[
435 Block::Simple(SimpleBlock {
436 content: Content {
437 original: Span {
438 data: "abc",
439 line: 3,
440 col: 1,
441 offset: 17,
442 },
443 rendered: "abc",
444 },
445 source: Span {
446 data: "abc",
447 line: 3,
448 col: 1,
449 offset: 17,
450 },
451 title_source: None,
452 title: None,
453 anchor: None,
454 anchor_reftext: None,
455 attrlist: None,
456 }),
457 Block::Simple(SimpleBlock {
458 content: Content {
459 original: Span {
460 data: "def",
461 line: 5,
462 col: 1,
463 offset: 22,
464 },
465 rendered: "def",
466 },
467 source: Span {
468 data: "def",
469 line: 5,
470 col: 1,
471 offset: 22,
472 },
473 title_source: None,
474 title: None,
475 anchor: None,
476 anchor_reftext: None,
477 attrlist: None,
478 })
479 ],
480 source: Span {
481 data: "= Example Title\n\nabc\n\ndef",
482 line: 1,
483 col: 1,
484 offset: 0
485 },
486 warnings: &[],
487 source_map: SourceMap(&[]),
488 catalog: Catalog::default(),
489 }
490 );
491 }
492
493 #[test]
494 fn blank_lines_before_header() {
495 let doc = Parser::default().parse("\n\n= Example Title\n\nabc\n\ndef");
496
497 assert_eq!(
498 doc,
499 Document {
500 header: Header {
501 title_source: Some(Span {
502 data: "Example Title",
503 line: 3,
504 col: 3,
505 offset: 4,
506 },),
507 title: Some("Example Title",),
508 attributes: &[],
509 author_line: None,
510 revision_line: None,
511 comments: &[],
512 source: Span {
513 data: "= Example Title",
514 line: 3,
515 col: 1,
516 offset: 2,
517 },
518 },
519 blocks: &[
520 Block::Simple(SimpleBlock {
521 content: Content {
522 original: Span {
523 data: "abc",
524 line: 5,
525 col: 1,
526 offset: 19,
527 },
528 rendered: "abc",
529 },
530 source: Span {
531 data: "abc",
532 line: 5,
533 col: 1,
534 offset: 19,
535 },
536 title_source: None,
537 title: None,
538 anchor: None,
539 anchor_reftext: None,
540 attrlist: None,
541 },),
542 Block::Simple(SimpleBlock {
543 content: Content {
544 original: Span {
545 data: "def",
546 line: 7,
547 col: 1,
548 offset: 24,
549 },
550 rendered: "def",
551 },
552 source: Span {
553 data: "def",
554 line: 7,
555 col: 1,
556 offset: 24,
557 },
558 title_source: None,
559 title: None,
560 anchor: None,
561 anchor_reftext: None,
562 attrlist: None,
563 },),
564 ],
565 source: Span {
566 data: "\n\n= Example Title\n\nabc\n\ndef",
567 line: 1,
568 col: 1,
569 offset: 0,
570 },
571 warnings: &[],
572 source_map: SourceMap(&[]),
573 catalog: Catalog::default(),
574 }
575 );
576 }
577
578 #[test]
579 fn blank_lines_and_comment_before_header() {
580 let doc =
581 Parser::default().parse("\n// ignore this comment\n= Example Title\n\nabc\n\ndef");
582
583 assert_eq!(
584 doc,
585 Document {
586 header: Header {
587 title_source: Some(Span {
588 data: "Example Title",
589 line: 3,
590 col: 3,
591 offset: 26,
592 },),
593 title: Some("Example Title",),
594 attributes: &[],
595 author_line: None,
596 revision_line: None,
597 comments: &[Span {
598 data: "// ignore this comment",
599 line: 2,
600 col: 1,
601 offset: 1,
602 },],
603 source: Span {
604 data: "// ignore this comment\n= Example Title",
605 line: 2,
606 col: 1,
607 offset: 1,
608 },
609 },
610 blocks: &[
611 Block::Simple(SimpleBlock {
612 content: Content {
613 original: Span {
614 data: "abc",
615 line: 5,
616 col: 1,
617 offset: 41,
618 },
619 rendered: "abc",
620 },
621 source: Span {
622 data: "abc",
623 line: 5,
624 col: 1,
625 offset: 41,
626 },
627 title_source: None,
628 title: None,
629 anchor: None,
630 anchor_reftext: None,
631 attrlist: None,
632 },),
633 Block::Simple(SimpleBlock {
634 content: Content {
635 original: Span {
636 data: "def",
637 line: 7,
638 col: 1,
639 offset: 46,
640 },
641 rendered: "def",
642 },
643 source: Span {
644 data: "def",
645 line: 7,
646 col: 1,
647 offset: 46,
648 },
649 title_source: None,
650 title: None,
651 anchor: None,
652 anchor_reftext: None,
653 attrlist: None,
654 },),
655 ],
656 source: Span {
657 data: "\n// ignore this comment\n= Example Title\n\nabc\n\ndef",
658 line: 1,
659 col: 1,
660 offset: 0,
661 },
662 warnings: &[],
663 source_map: SourceMap(&[]),
664 catalog: Catalog::default(),
665 }
666 );
667 }
668
669 #[test]
670 fn extra_space_before_title() {
671 assert_eq!(
672 Parser::default().parse("= Example Title\n\nabc"),
673 Document {
674 header: Header {
675 title_source: Some(Span {
676 data: "Example Title",
677 line: 1,
678 col: 5,
679 offset: 4,
680 }),
681 title: Some("Example Title"),
682 attributes: &[],
683 author_line: None,
684 revision_line: None,
685 comments: &[],
686 source: Span {
687 data: "= Example Title",
688 line: 1,
689 col: 1,
690 offset: 0,
691 }
692 },
693 blocks: &[Block::Simple(SimpleBlock {
694 content: Content {
695 original: Span {
696 data: "abc",
697 line: 3,
698 col: 1,
699 offset: 19,
700 },
701 rendered: "abc",
702 },
703 source: Span {
704 data: "abc",
705 line: 3,
706 col: 1,
707 offset: 19,
708 },
709 title_source: None,
710 title: None,
711 anchor: None,
712 anchor_reftext: None,
713 attrlist: None,
714 })],
715 source: Span {
716 data: "= Example Title\n\nabc",
717 line: 1,
718 col: 1,
719 offset: 0
720 },
721 warnings: &[],
722 source_map: SourceMap(&[]),
723 catalog: Catalog::default(),
724 }
725 );
726 }
727
728 #[test]
729 fn err_bad_header() {
730 assert_eq!(
731 Parser::default().parse(
732 "= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28\nnot an attribute\n"
733 ),
734 Document {
735 header: Header {
736 title_source: Some(Span {
737 data: "Title",
738 line: 1,
739 col: 3,
740 offset: 2,
741 }),
742 title: Some("Title"),
743 attributes: &[],
744 author_line: Some(AuthorLine {
745 authors: &[Author {
746 name: "Jane Smith",
747 firstname: "Jane",
748 middlename: None,
749 lastname: Some("Smith"),
750 email: Some("jane@example.com"),
751 }],
752 source: Span {
753 data: "Jane Smith <jane@example.com>",
754 line: 2,
755 col: 1,
756 offset: 8,
757 },
758 }),
759 revision_line: Some(RevisionLine {
760 revnumber: Some("1",),
761 revdate: "2025-09-28",
762 revremark: None,
763 source: Span {
764 data: "v1, 2025-09-28",
765 line: 3,
766 col: 1,
767 offset: 38,
768 },
769 },),
770 comments: &[],
771 source: Span {
772 data: "= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28",
773 line: 1,
774 col: 1,
775 offset: 0,
776 }
777 },
778 blocks: &[Block::Simple(SimpleBlock {
779 content: Content {
780 original: Span {
781 data: "not an attribute",
782 line: 4,
783 col: 1,
784 offset: 53,
785 },
786 rendered: "not an attribute",
787 },
788 source: Span {
789 data: "not an attribute",
790 line: 4,
791 col: 1,
792 offset: 53,
793 },
794 title_source: None,
795 title: None,
796 anchor: None,
797 anchor_reftext: None,
798 attrlist: None,
799 })],
800 source: Span {
801 data: "= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28\nnot an attribute",
802 line: 1,
803 col: 1,
804 offset: 0
805 },
806 warnings: &[Warning {
807 source: Span {
808 data: "not an attribute",
809 line: 4,
810 col: 1,
811 offset: 53,
812 },
813 warning: WarningType::DocumentHeaderNotTerminated,
814 },],
815 source_map: SourceMap(&[]),
816 catalog: Catalog::default(),
817 }
818 );
819 }
820
821 #[test]
822 fn err_bad_header_and_bad_macro() {
823 assert_eq!(
824 Parser::default().parse("= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28\nnot an attribute\n\n== Section Title\n\nimage::bar[alt=Sunset,width=300,,height=400]"),
825 Document {
826 header: Header {
827 title_source: Some(Span {
828 data: "Title",
829 line: 1,
830 col: 3,
831 offset: 2,
832 }),
833 title: Some("Title"),
834 attributes: &[],
835 author_line: Some(AuthorLine {
836 authors: &[Author {
837 name: "Jane Smith",
838 firstname: "Jane",
839 middlename: None,
840 lastname: Some("Smith"),
841 email: Some("jane@example.com"),
842 }],
843 source: Span {
844 data: "Jane Smith <jane@example.com>",
845 line: 2,
846 col: 1,
847 offset: 8,
848 },
849 }),
850 revision_line: Some(
851 RevisionLine {
852 revnumber: Some(
853 "1",
854 ),
855 revdate: "2025-09-28",
856 revremark: None,
857 source: Span {
858 data: "v1, 2025-09-28",
859 line: 3,
860 col: 1,
861 offset: 38,
862 },
863 },
864 ),
865 comments: &[],
866 source: Span {
867 data: "= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28",
868 line: 1,
869 col: 1,
870 offset: 0,
871 }
872 },
873 blocks: &[
874 Block::Simple(SimpleBlock {
875 content: Content {
876 original: Span {
877 data: "not an attribute",
878 line: 4,
879 col: 1,
880 offset: 53,
881 },
882 rendered: "not an attribute",
883 },
884 source: Span {
885 data: "not an attribute",
886 line: 4,
887 col: 1,
888 offset: 53,
889 },
890 title_source: None,
891 title: None,
892 anchor: None,
893 anchor_reftext: None,
894 attrlist: None,
895 }
896 ),
897 Block::Section(
898 SectionBlock {
899 level: 1,
900 section_title: Content {
901 original: Span {
902 data: "Section Title",
903 line: 6,
904 col: 4,
905 offset: 74,
906 },
907 rendered: "Section Title",
908 },
909 blocks: &[
910 Block::Media(
911 MediaBlock {
912 type_: MediaType::Image,
913 target: Span {
914 data: "bar",
915 line: 8,
916 col: 8,
917 offset: 96,
918 },
919 macro_attrlist: Attrlist {
920 attributes: &[
921 ElementAttribute {
922 name: Some("alt"),
923 shorthand_items: &[],
924 value: "Sunset"
925 },
926 ElementAttribute {
927 name: Some("width"),
928 shorthand_items: &[],
929 value: "300"
930 },
931 ElementAttribute {
932 name: Some("height"),
933 shorthand_items: &[],
934 value: "400"
935 },
936 ],
937 anchor: None,
938 source: Span {
939 data: "alt=Sunset,width=300,,height=400",
940 line: 8,
941 col: 12,
942 offset: 100,
943 },
944 },
945 source: Span {
946 data: "image::bar[alt=Sunset,width=300,,height=400]",
947 line: 8,
948 col: 1,
949 offset: 89,
950 },
951 title_source: None,
952 title: None,
953 anchor: None,
954 anchor_reftext: None,
955 attrlist: None,
956 },
957 ),
958 ],
959 source: Span {
960 data: "== Section Title\n\nimage::bar[alt=Sunset,width=300,,height=400]",
961 line: 6,
962 col: 1,
963 offset: 71,
964 },
965 title_source: None,
966 title: None,
967 anchor: None,
968 anchor_reftext: None,
969 attrlist: None,
970 section_type: SectionType::Normal,
971 section_id: Some("_section_title"),
972 section_number: None,
973 },
974 )],
975 source: Span {
976 data: "= Title\nJane Smith <jane@example.com>\nv1, 2025-09-28\nnot an attribute\n\n== Section Title\n\nimage::bar[alt=Sunset,width=300,,height=400]",
977 line: 1,
978 col: 1,
979 offset: 0
980 },
981 warnings: &[
982 Warning {
983 source: Span {
984 data: "not an attribute",
985 line: 4,
986 col: 1,
987 offset: 53,
988 },
989 warning: WarningType::DocumentHeaderNotTerminated,
990 },
991 Warning {
992 source: Span {
993 data: "alt=Sunset,width=300,,height=400",
994 line: 8,
995 col: 12,
996 offset: 100,
997 },
998 warning: WarningType::EmptyAttributeValue,
999 },
1000 ],
1001 source_map: SourceMap(&[]),
1002 catalog: Catalog {
1003 refs: HashMap::from([
1004 ("_section_title", RefEntry {
1005 id: "_section_title",
1006 reftext: Some(
1007 "Section Title",
1008 ),
1009 ref_type: RefType::Section,
1010 }),
1011 ]),
1012 reftext_to_id: HashMap::from([
1013 ("Section Title", "_section_title"),
1014 ]),
1015 }
1016 }
1017 );
1018 }
1019
1020 #[test]
1021 fn impl_debug() {
1022 let doc = Parser::default().parse("= Example Title\n\nabc\n\ndef");
1023
1024 assert_eq!(
1025 format!("{doc:#?}"),
1026 r#"Document {
1027 header: Header {
1028 title_source: Some(
1029 Span {
1030 data: "Example Title",
1031 line: 1,
1032 col: 3,
1033 offset: 2,
1034 },
1035 ),
1036 title: Some(
1037 "Example Title",
1038 ),
1039 attributes: &[],
1040 author_line: None,
1041 revision_line: None,
1042 comments: &[],
1043 source: Span {
1044 data: "= Example Title",
1045 line: 1,
1046 col: 1,
1047 offset: 0,
1048 },
1049 },
1050 blocks: &[
1051 Block::Simple(
1052 SimpleBlock {
1053 content: Content {
1054 original: Span {
1055 data: "abc",
1056 line: 3,
1057 col: 1,
1058 offset: 17,
1059 },
1060 rendered: "abc",
1061 },
1062 source: Span {
1063 data: "abc",
1064 line: 3,
1065 col: 1,
1066 offset: 17,
1067 },
1068 title_source: None,
1069 title: None,
1070 anchor: None,
1071 anchor_reftext: None,
1072 attrlist: None,
1073 },
1074 ),
1075 Block::Simple(
1076 SimpleBlock {
1077 content: Content {
1078 original: Span {
1079 data: "def",
1080 line: 5,
1081 col: 1,
1082 offset: 22,
1083 },
1084 rendered: "def",
1085 },
1086 source: Span {
1087 data: "def",
1088 line: 5,
1089 col: 1,
1090 offset: 22,
1091 },
1092 title_source: None,
1093 title: None,
1094 anchor: None,
1095 anchor_reftext: None,
1096 attrlist: None,
1097 },
1098 ),
1099 ],
1100 source: Span {
1101 data: "= Example Title\n\nabc\n\ndef",
1102 line: 1,
1103 col: 1,
1104 offset: 0,
1105 },
1106 warnings: &[],
1107 source_map: SourceMap(&[]),
1108 catalog: Catalog {
1109 refs: HashMap::from([]),
1110 reftext_to_id: HashMap::from([]),
1111 },
1112}"#
1113 );
1114 }
1115}