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