mabel_eno/elements/
document.rs

1use std::cell::RefCell;
2use std::ops::RangeInclusive;
3use std::rc::Rc;
4
5use crate::elements::{Comment, CommentImpl};
6use crate::queries::{
7    EmbedQuery, EmbedQueryImpl, EmbedQueryParent, FieldQuery, FieldQueryImpl, FieldQueryParent,
8    FlagQuery, FlagQueryImpl, FlagQueryParent, Matches, SectionElements, SectionQuery,
9    SectionQueryImpl, SectionQueryParent,
10};
11use crate::{Element, Embed, Error, Field, Flag, Printer, Section, SectionElement};
12
13#[derive(Debug)]
14pub struct Document {
15    document_internals: Rc<DocumentInternals>,
16    elements: Vec<Box<dyn SectionElement>>,
17    number_of_lines: u32,
18}
19
20#[derive(Debug)]
21pub struct DocumentInternals {
22    pub comments: RefCell<Vec<Comment>>,
23    pub content: String,
24    pub default_printer: Box<dyn Printer>,
25}
26
27pub trait DocumentImpl {
28    fn append_comment(&mut self, comment: Comment);
29    fn append_elements(&mut self, elements: &mut Vec<Box<dyn SectionElement>>);
30    fn clone_internals(&self) -> Rc<DocumentInternals>;
31    fn get_elements(&self) -> &[Box<dyn SectionElement>];
32    fn get_elements_mut(&mut self) -> &mut Vec<Box<dyn SectionElement>>;
33    fn get_number_of_lines(&self) -> u32;
34    #[allow(clippy::new_ret_no_self)]
35    fn new(content: &str, default_printer: Box<dyn Printer>) -> Document;
36    fn set_number_of_lines(&mut self, number_of_lines: u32);
37}
38
39impl Document {
40    pub const LINE_NUMBER: u32 = 1;
41
42    pub fn elements(&self) -> &[Box<dyn SectionElement>] {
43        for element in &self.elements {
44            element.touch();
45        }
46        self.elements.as_slice()
47    }
48
49    pub fn embed(&self, key: &str) -> Result<EmbedQuery, Error> {
50        let element_option = match self.elements.single_embed_with_key(key) {
51            Matches::None => None,
52            Matches::One(embed) => Some(embed),
53            Matches::Multiple(_first, second) => {
54                return Err(Error::new(
55                    "Only a single embed was expected".to_owned(),
56                    second.line_number,
57                ))
58            }
59            Matches::WrongType(element) => {
60                return Err(Error::new(
61                    "An embed was expected".to_owned(),
62                    element.line_number(),
63                ))
64            }
65        };
66
67        // TODO: Revisit whether Some(key.to_string()) ever makes sense (as in: does the key need to be an option? Isn't it always present maybe?)
68        Ok(EmbedQuery::new(
69            element_option,
70            Some(key.to_string()),
71            EmbedQueryParent::Document(self),
72        ))
73    }
74
75    pub fn field(&self, key: &str) -> Result<FieldQuery, Error> {
76        let element_option = match self.elements.single_field_with_key(key) {
77            Matches::None => None,
78            Matches::One(field) => Some(field),
79            Matches::Multiple(_first, second) => {
80                return Err(Error::new(
81                    "Only a single field was expected".to_owned(),
82                    second.line_number,
83                ))
84            }
85            Matches::WrongType(element) => {
86                return Err(Error::new(
87                    "A field was expected".to_owned(),
88                    element.line_number(),
89                ))
90            }
91        };
92
93        Ok(FieldQuery::new(
94            element_option,
95            Some(key.to_string()),
96            FieldQueryParent::Document(self),
97        ))
98    }
99
100    pub fn flag(&self, key: &str) -> Result<FlagQuery, Error> {
101        let element_option = match self.elements.single_flag_with_key(key) {
102            Matches::None => None,
103            Matches::One(flag) => Some(flag),
104            Matches::Multiple(_first, second) => {
105                return Err(Error::new(
106                    "Only a single flag was expected".to_string(),
107                    second.line_number,
108                ))
109            }
110            Matches::WrongType(element) => {
111                return Err(Error::new(
112                    "A flag was expected".to_string(),
113                    element.line_number(),
114                ))
115            }
116        };
117
118        Ok(FlagQuery::new(
119            element_option,
120            Some(key.to_string()),
121            FlagQueryParent::Document(self),
122        ))
123    }
124
125    pub fn line_range(&self) -> RangeInclusive<u32> {
126        1..=self.number_of_lines
127    }
128
129    pub fn optional_embed(&self, key: &str) -> Result<Option<&Embed>, Error> {
130        match self.elements.single_embed_with_key(key) {
131            Matches::None => Ok(None),
132            Matches::One(embed) => {
133                embed.touch();
134                Ok(Some(embed))
135            }
136            Matches::Multiple(_first, second) => Err(Error::new(
137                "Only a single embed was expected".to_string(),
138                second.line_number,
139            )),
140            Matches::WrongType(element) => Err(Error::new(
141                "A embed was expected".to_string(),
142                element.line_number(),
143            )),
144        }
145    }
146
147    pub fn optional_field(&self, key: &str) -> Result<Option<&Field>, Error> {
148        match self.elements.single_field_with_key(key) {
149            Matches::None => Ok(None),
150            Matches::One(field) => {
151                field.touch();
152                Ok(Some(field))
153            }
154            Matches::Multiple(_first, second) => Err(Error::new(
155                "Only a single field was expected".to_string(),
156                second.line_number,
157            )),
158            Matches::WrongType(element) => Err(Error::new(
159                "A field was expected".to_string(),
160                element.line_number(),
161            )),
162        }
163    }
164
165    pub fn optional_flag(&self, key: &str) -> Result<Option<&Flag>, Error> {
166        match self.elements.single_flag_with_key(key) {
167            Matches::None => Ok(None),
168            Matches::One(flag) => {
169                flag.touch();
170                Ok(Some(flag))
171            }
172            Matches::Multiple(_first, second) => Err(Error::new(
173                "Only a single flag was expected".to_string(),
174                second.line_number,
175            )),
176            Matches::WrongType(element) => Err(Error::new(
177                "A flag was expected".to_string(),
178                element.line_number(),
179            )),
180        }
181    }
182
183    pub fn optional_section(&self, key: &str) -> Result<Option<&Section>, Error> {
184        match self.elements.single_section_with_key(key) {
185            Matches::None => Ok(None),
186            Matches::One(section) => Ok(Some(section)),
187            Matches::Multiple(_first, second) => Err(Error::new(
188                "Only a single section was expected".to_string(),
189                second.line_number,
190            )),
191            Matches::WrongType(element) => Err(Error::new(
192                "A section was expected".to_string(),
193                element.line_number(),
194            )),
195        }
196    }
197
198    pub fn required_section(&self, key: &str) -> Result<&Section, Error> {
199        match self.elements.single_section_with_key(key) {
200            Matches::None => Err(Error::new("Not found".to_string(), Document::LINE_NUMBER)),
201            Matches::One(section) => Ok(section),
202            Matches::Multiple(_first, second) => Err(Error::new(
203                "Only a single section was expected".to_string(),
204                second.line_number,
205            )),
206            Matches::WrongType(element) => Err(Error::new(
207                "A section was expected".to_string(),
208                element.line_number(),
209            )),
210        }
211    }
212
213    pub fn section(&self, key: &str) -> Result<SectionQuery, Error> {
214        let element_option = match self.elements.single_section_with_key(key) {
215            Matches::None => None,
216            Matches::One(section) => Some(section),
217            Matches::Multiple(_first, second) => {
218                return Err(Error::new(
219                    "Only a single section was expected".to_string(),
220                    second.line_number,
221                ))
222            }
223            Matches::WrongType(element) => {
224                return Err(Error::new(
225                    "A section was expected".to_string(),
226                    element.line_number(),
227                ))
228            }
229        };
230
231        Ok(SectionQuery::new(
232            element_option,
233            Some(key.to_string()),
234            SectionQueryParent::Document(self),
235        ))
236    }
237
238    pub fn snippet(&self) -> String {
239        self.snippet_with_options(&*self.document_internals.default_printer, true)
240    }
241
242    pub fn snippet_with_options(&self, printer: &dyn Printer, gutter: bool) -> String {
243        let mut out = String::new();
244        let mut line_number = 1;
245
246        for element in &self.elements {
247            if line_number > 1 {
248                out.push('\n');
249            }
250
251            let element_line_range = element.line_range();
252
253            while line_number < *element_line_range.start() {
254                if let Some(comment) = self
255                    .document_internals
256                    .comments
257                    .borrow()
258                    .iter()
259                    .find(|comment| comment.line_number == line_number)
260                {
261                    out.push_str(&comment.snippet_with_options(printer, gutter));
262                } else if gutter {
263                    out.push_str(&printer.gutter(line_number));
264                }
265
266                out.push('\n');
267
268                line_number += 1;
269            }
270
271            out.push_str(&element.snippet_with_options(printer, gutter));
272
273            line_number = *element_line_range.end() + 1;
274        }
275
276        while line_number <= self.number_of_lines {
277            if line_number > 1 {
278                out.push('\n');
279            }
280
281            if let Some(comment) = self
282                .document_internals
283                .comments
284                .borrow()
285                .iter()
286                .find(|comment| comment.line_number == line_number)
287            {
288                out.push_str(&comment.snippet_with_options(printer, gutter));
289            } else if gutter {
290                out.push_str(&printer.gutter(line_number));
291            }
292
293            line_number += 1;
294        }
295
296        out
297    }
298
299    pub fn untouched_elements(&self) -> Vec<&dyn Element> {
300        self.elements.untouched()
301    }
302}
303
304impl DocumentImpl for Document {
305    fn append_comment(&mut self, comment: Comment) {
306        self.document_internals.comments.borrow_mut().push(comment);
307    }
308
309    fn append_elements(&mut self, elements: &mut Vec<Box<dyn SectionElement>>) {
310        self.elements.append(elements);
311    }
312
313    fn clone_internals(&self) -> Rc<DocumentInternals> {
314        self.document_internals.clone()
315    }
316
317    fn get_elements(&self) -> &[Box<dyn SectionElement>] {
318        self.elements.as_slice()
319    }
320
321    fn get_elements_mut(&mut self) -> &mut Vec<Box<dyn SectionElement>> {
322        &mut self.elements
323    }
324
325    fn get_number_of_lines(&self) -> u32 {
326        self.number_of_lines
327    }
328
329    fn new(content: &str, default_printer: Box<dyn Printer>) -> Document {
330        Document {
331            document_internals: Rc::new(DocumentInternals::new(content, default_printer)),
332            elements: Vec::new(),
333            number_of_lines: 1,
334        }
335    }
336
337    fn set_number_of_lines(&mut self, number_of_lines: u32) {
338        self.number_of_lines = number_of_lines;
339    }
340}
341
342impl DocumentInternals {
343    fn new(content: &str, default_printer: Box<dyn Printer>) -> DocumentInternals {
344        DocumentInternals {
345            comments: RefCell::new(Vec::new()),
346            content: content.to_owned(),
347            default_printer,
348        }
349    }
350}