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 anchor: None,
155 anchor_reftext: None,
156 attrlist: None,
157 },
158 ),
159 ],
160 source: Span {
161 data: "Some early words go here.",
162 line: 3,
163 col: 1,
164 offset: 18,
165 },
166}"#
167 );
168 }
169
170 #[test]
171 fn impl_is_block() {
172 let doc = doc_fixture();
173 let preamble = fixture_preamble(&doc);
174
175 assert_eq!(
176 preamble,
177 &Block::Preamble(Preamble {
178 blocks: &[Block::Simple(SimpleBlock {
179 content: Content {
180 original: Span {
181 data: "Some early words go here.",
182 line: 3,
183 col: 1,
184 offset: 18,
185 },
186 rendered: "Some early words go here.",
187 },
188 source: Span {
189 data: "Some early words go here.",
190 line: 3,
191 col: 1,
192 offset: 18,
193 },
194 style: SimpleBlockStyle::Paragraph,
195 title_source: None,
196 title: None,
197 anchor: None,
198 anchor_reftext: None,
199 attrlist: None,
200 },),],
201 source: Span {
202 data: "Some early words go here.",
203 line: 3,
204 col: 1,
205 offset: 18,
206 },
207 },)
208 );
209
210 assert_eq!(preamble.content_model(), ContentModel::Compound);
211 assert!(preamble.rendered_content().is_none());
212 assert_eq!(preamble.raw_context().as_ref(), "preamble");
213 assert_eq!(preamble.resolved_context().as_ref(), "preamble");
214 assert!(preamble.declared_style().is_none());
215
216 let mut blocks = preamble.nested_blocks();
217 assert_eq!(
218 blocks.next().unwrap(),
219 &Block::Simple(SimpleBlock {
220 content: Content {
221 original: Span {
222 data: "Some early words go here.",
223 line: 3,
224 col: 1,
225 offset: 18,
226 },
227 rendered: "Some early words go here.",
228 },
229 source: Span {
230 data: "Some early words go here.",
231 line: 3,
232 col: 1,
233 offset: 18,
234 },
235 style: SimpleBlockStyle::Paragraph,
236 title_source: None,
237 title: None,
238 anchor: None,
239 anchor_reftext: None,
240 attrlist: None,
241 })
242 );
243
244 assert!(blocks.next().is_none());
245
246 assert!(preamble.id().is_none());
247 assert!(preamble.roles().is_empty());
248 assert!(preamble.options().is_empty());
249 assert!(preamble.title_source().is_none());
250 assert!(preamble.title().is_none());
251 assert!(preamble.anchor().is_none());
252 assert!(preamble.anchor_reftext().is_none());
253 assert!(preamble.attrlist().is_none());
254 assert_eq!(preamble.substitution_group(), SubstitutionGroup::Normal);
255
256 assert_eq!(
257 format!("{preamble:#?}"),
258 "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)"
259 );
260
261 assert_eq!(
262 preamble.span(),
263 Span {
264 data: "Some early words go here.",
265 line: 3,
266 col: 1,
267 offset: 18,
268 }
269 );
270 }
271}