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