1use std::slice::Iter;
2
3use crate::{
4 HasSpan, Span,
5 attributes::Attrlist,
6 blocks::{Block, ContentModel, IsBlock},
7 internal::debug::DebugSliceReference,
8 strings::CowStr,
9};
10
11#[derive(Clone, Eq, PartialEq)]
14pub struct Preamble<'src> {
15 blocks: Vec<Block<'src>>,
16 source: Span<'src>,
17}
18
19impl<'src> Preamble<'src> {
20 pub(crate) fn from_blocks(blocks: Vec<Block<'src>>, source: Span<'src>) -> Self {
21 let preamble_source = if let Some(last_block) = blocks.last() {
22 let after_last = last_block.span().discard_all();
23 source.trim_remainder(after_last)
24 } else {
25 source.trim_remainder(source)
29 };
30
31 Self {
32 blocks,
33 source: preamble_source,
34 }
35 }
36}
37
38impl<'src> IsBlock<'src> for Preamble<'src> {
39 fn content_model(&self) -> ContentModel {
40 ContentModel::Compound
41 }
42
43 fn raw_context(&self) -> CowStr<'src> {
44 "preamble".into()
45 }
46
47 fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
48 self.blocks.iter()
49 }
50
51 fn title_source(&'src self) -> Option<Span<'src>> {
52 None
53 }
54
55 fn title(&self) -> Option<&str> {
56 None
57 }
58
59 fn anchor(&'src self) -> Option<Span<'src>> {
60 None
61 }
62
63 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
64 None
65 }
66
67 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
68 None
69 }
70}
71
72impl<'src> HasSpan<'src> for Preamble<'src> {
73 fn span(&self) -> Span<'src> {
74 self.source
75 }
76}
77
78impl std::fmt::Debug for Preamble<'_> {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.debug_struct("Preamble")
81 .field("blocks", &DebugSliceReference(&self.blocks))
82 .field("source", &self.source)
83 .finish()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 #![allow(clippy::panic)]
90 #![allow(clippy::unwrap_used)]
91
92 use pretty_assertions_sorted::assert_eq;
93
94 use crate::{
95 HasSpan, Parser,
96 blocks::{ContentModel, IsBlock, SimpleBlockStyle},
97 content::SubstitutionGroup,
98 tests::prelude::*,
99 };
100
101 fn doc_fixture() -> crate::Document<'static> {
102 Parser::default().parse("= Document Title\n\nSome early words go here.\n\n== First Section")
103 }
104
105 fn fixture_preamble<'src>(
106 doc: &'src crate::Document<'src>,
107 ) -> &'src crate::blocks::Block<'src> {
108 doc.nested_blocks().next().unwrap()
109 }
110
111 #[test]
112 fn impl_clone() {
113 let doc = doc_fixture();
115
116 let b1 = fixture_preamble(&doc);
117 let b2 = b1.clone();
118
119 assert_eq!(b1, &b2);
120 }
121
122 #[test]
123 fn impl_debug() {
124 let doc = doc_fixture();
125 let preamble = fixture_preamble(&doc);
126
127 let crate::blocks::Block::Preamble(preamble) = preamble else {
128 panic!("Unexpected block: {preamble:#?}");
129 };
130
131 dbg!(&preamble);
132
133 assert_eq!(
134 format!("{preamble:#?}"),
135 r#"Preamble {
136 blocks: &[
137 Block::Simple(
138 SimpleBlock {
139 content: Content {
140 original: Span {
141 data: "Some early words go here.",
142 line: 3,
143 col: 1,
144 offset: 18,
145 },
146 rendered: "Some early words go here.",
147 },
148 source: Span {
149 data: "Some early words go here.",
150 line: 3,
151 col: 1,
152 offset: 18,
153 },
154 style: SimpleBlockStyle::Paragraph,
155 title_source: None,
156 title: None,
157 anchor: None,
158 anchor_reftext: None,
159 attrlist: None,
160 },
161 ),
162 ],
163 source: Span {
164 data: "Some early words go here.",
165 line: 3,
166 col: 1,
167 offset: 18,
168 },
169}"#
170 );
171 }
172
173 #[test]
174 fn impl_is_block() {
175 let doc = doc_fixture();
176 let preamble = fixture_preamble(&doc);
177
178 assert_eq!(
179 preamble,
180 &Block::Preamble(Preamble {
181 blocks: &[Block::Simple(SimpleBlock {
182 content: Content {
183 original: Span {
184 data: "Some early words go here.",
185 line: 3,
186 col: 1,
187 offset: 18,
188 },
189 rendered: "Some early words go here.",
190 },
191 source: Span {
192 data: "Some early words go here.",
193 line: 3,
194 col: 1,
195 offset: 18,
196 },
197 style: SimpleBlockStyle::Paragraph,
198 title_source: None,
199 title: None,
200 anchor: None,
201 anchor_reftext: None,
202 attrlist: None,
203 },),],
204 source: Span {
205 data: "Some early words go here.",
206 line: 3,
207 col: 1,
208 offset: 18,
209 },
210 },)
211 );
212
213 assert_eq!(preamble.content_model(), ContentModel::Compound);
214 assert_eq!(preamble.raw_context().as_ref(), "preamble");
215 assert_eq!(preamble.resolved_context().as_ref(), "preamble");
216 assert!(preamble.declared_style().is_none());
217
218 let mut blocks = preamble.nested_blocks();
219 assert_eq!(
220 blocks.next().unwrap(),
221 &Block::Simple(SimpleBlock {
222 content: Content {
223 original: Span {
224 data: "Some early words go here.",
225 line: 3,
226 col: 1,
227 offset: 18,
228 },
229 rendered: "Some early words go here.",
230 },
231 source: Span {
232 data: "Some early words go here.",
233 line: 3,
234 col: 1,
235 offset: 18,
236 },
237 style: SimpleBlockStyle::Paragraph,
238 title_source: None,
239 title: None,
240 anchor: None,
241 anchor_reftext: None,
242 attrlist: None,
243 })
244 );
245
246 assert!(blocks.next().is_none());
247
248 assert!(preamble.id().is_none());
249 assert!(preamble.roles().is_empty());
250 assert!(preamble.options().is_empty());
251 assert!(preamble.title_source().is_none());
252 assert!(preamble.title().is_none());
253 assert!(preamble.anchor().is_none());
254 assert!(preamble.anchor_reftext().is_none());
255 assert!(preamble.attrlist().is_none());
256 assert_eq!(preamble.substitution_group(), SubstitutionGroup::Normal);
257
258 assert_eq!(
259 format!("{preamble:#?}"),
260 "Block::Preamble(\n Preamble {\n blocks: &[\n Block::Simple(\n SimpleBlock {\n content: Content {\n original: Span {\n data: \"Some early words go here.\",\n line: 3,\n col: 1,\n offset: 18,\n },\n rendered: \"Some early words go here.\",\n },\n source: Span {\n data: \"Some early words go here.\",\n line: 3,\n col: 1,\n offset: 18,\n },\n style: SimpleBlockStyle::Paragraph,\n title_source: None,\n title: None,\n anchor: None,\n anchor_reftext: None,\n attrlist: None,\n },\n ),\n ],\n source: Span {\n data: \"Some early words go here.\",\n line: 3,\n col: 1,\n offset: 18,\n },\n },\n)"
261 );
262
263 assert_eq!(
264 preamble.span(),
265 Span {
266 data: "Some early words go here.",
267 line: 3,
268 col: 1,
269 offset: 18,
270 }
271 );
272 }
273}