mabel_eno/queries/
section_query.rs

1use crate::elements::{
2    Document, Element, Embed, Field, Flag, Section, SectionElement, SectionImpl,
3};
4use crate::queries::{
5    EmbedQuery, EmbedQueryImpl, EmbedQueryParent, FieldQuery, FieldQueryImpl, FieldQueryParent,
6    FlagQuery, FlagQueryImpl, FlagQueryParent,
7};
8use crate::{Error, Printer};
9
10pub enum Matches<'a, T> {
11    Multiple(&'a T, &'a T),
12    One(&'a T),
13    None,
14    WrongType(&'a dyn SectionElement),
15}
16
17pub trait SectionElements {
18    fn single_embed_with_key(&self, key: &str) -> Matches<Embed>;
19    fn single_field_with_key(&self, key: &str) -> Matches<Field>;
20    fn single_flag_with_key(&self, key: &str) -> Matches<Flag>;
21    fn single_section_with_key(&self, key: &str) -> Matches<Section>;
22    fn untouched(&self) -> Vec<&dyn Element>;
23}
24
25/// Allows chained queries deep down into a document hierarchy
26/// without having to manually handle possibly missing sections
27/// in between. If the terminal/leaf element/value of a query chain
28/// is required, the first missing element in the chain will be
29/// "bubbled" up as the cause of the error.
30///
31/// Note that chaining still returns a `Result<*, enolib::Error>`
32/// at every step, due to the fact that a query step may not only
33/// fail due to a (potentially graceful) missing element, but also
34/// due to ambiguous results (e.g. two sections with the same key),
35/// which is an immediate hard error. Hint: Use the idiomatic approach
36/// with the ? operator to create nicely readable code with this API.
37pub struct SectionQuery<'a> {
38    element_option: Option<&'a Section>,
39    key: Option<String>,
40    parent: SectionQueryParent<'a>,
41}
42
43pub trait SectionQueryImpl<'a> {
44    fn element(&self) -> Option<&Section>;
45    #[allow(clippy::new_ret_no_self)]
46    fn new(
47        element_option: Option<&'a Section>,
48        key: Option<String>,
49        parent: SectionQueryParent<'a>,
50    ) -> SectionQuery<'a>;
51}
52
53pub enum SectionQueryParent<'a> {
54    Document(&'a Document),
55    Section(&'a Section),
56    SectionQuery(&'a SectionQuery<'a>),
57}
58
59impl SectionElements for Vec<Box<dyn SectionElement>> {
60    fn single_embed_with_key(&self, key: &str) -> Matches<Embed> {
61        let mut embed_option: Option<&Embed> = None;
62
63        for element in self.iter().filter(|element| element.key() == key) {
64            match element.as_embed() {
65                Some(embed) => match embed_option {
66                    Some(prev_embed) => return Matches::Multiple(prev_embed, embed),
67                    None => embed_option = Some(embed),
68                },
69                None => return Matches::WrongType(element.as_ref()),
70            }
71        }
72
73        match embed_option {
74            Some(embed) => {
75                embed.touch();
76                Matches::One(embed)
77            }
78            None => Matches::None,
79        }
80    }
81
82    fn single_field_with_key(&self, key: &str) -> Matches<Field> {
83        let mut field_option: Option<&Field> = None;
84
85        for element in self.iter().filter(|element| element.key() == key) {
86            match element.as_field() {
87                Some(field) => match field_option {
88                    Some(prev_field) => return Matches::Multiple(prev_field, field),
89                    None => field_option = Some(field),
90                },
91                None => return Matches::WrongType(element.as_ref()),
92            }
93        }
94
95        match field_option {
96            Some(field) => {
97                field.touch();
98                Matches::One(field)
99            }
100            None => Matches::None,
101        }
102    }
103
104    fn single_flag_with_key(&self, key: &str) -> Matches<Flag> {
105        let mut flag_option: Option<&Flag> = None;
106
107        for element in self.iter().filter(|element| element.key() == key) {
108            match element.as_flag() {
109                Some(flag) => match flag_option {
110                    Some(prev_flag) => return Matches::Multiple(prev_flag, flag),
111                    None => flag_option = Some(flag),
112                },
113                None => return Matches::WrongType(element.as_ref()),
114            }
115        }
116
117        match flag_option {
118            Some(flag) => {
119                flag.touch();
120                Matches::One(flag)
121            }
122            None => Matches::None,
123        }
124    }
125
126    fn single_section_with_key(&self, key: &str) -> Matches<Section> {
127        let mut section_option: Option<&Section> = None;
128
129        for element in self.iter().filter(|element| element.key() == key) {
130            match element.as_section() {
131                Some(section) => match section_option {
132                    Some(prev_section) => return Matches::Multiple(prev_section, section),
133                    None => section_option = Some(section),
134                },
135                None => return Matches::WrongType(element.as_ref()),
136            }
137        }
138
139        match section_option {
140            Some(section) => {
141                section.touch();
142                Matches::One(section)
143            }
144            None => Matches::None,
145        }
146    }
147
148    fn untouched(&self) -> Vec<&dyn Element> {
149        let mut result = Vec::new();
150
151        for element in self {
152            if !element.touched() {
153                result.push(element.as_element());
154            } else if let Some(field) = element.as_field() {
155                result.append(&mut field.untouched_elements());
156            } else if let Some(section) = element.as_section() {
157                result.append(&mut section.untouched_elements());
158            }
159        }
160
161        result
162    }
163}
164
165impl<'a> SectionQuery<'a> {
166    pub fn elements(&self) -> &[Box<dyn SectionElement>] {
167        match self.element_option {
168            Some(section) => section.get_elements(),
169            None => &[],
170        }
171    }
172
173    pub fn embed(&self, key: &str) -> Result<EmbedQuery, Error> {
174        let element_option = match self.element_option {
175            Some(section) => match section.get_elements_ref().single_embed_with_key(key) {
176                Matches::None => None,
177                Matches::One(embed) => Some(embed),
178                Matches::Multiple(_first, second) => {
179                    return Err(Error::new(
180                        "Only a single embed was expected".to_string(),
181                        second.line_number,
182                    ))
183                }
184                Matches::WrongType(element) => {
185                    return Err(Error::new(
186                        "An embed was expected".to_string(),
187                        element.line_number(),
188                    ))
189                }
190            },
191            None => None,
192        };
193
194        Ok(EmbedQuery::new(
195            element_option,
196            Some(key.to_string()),
197            EmbedQueryParent::SectionQuery(self),
198        ))
199    }
200
201    pub fn field(&self, key: &str) -> Result<FieldQuery, Error> {
202        let element_option = match self.element_option {
203            Some(section) => match section.get_elements_ref().single_field_with_key(key) {
204                Matches::None => None,
205                Matches::One(field) => Some(field),
206                Matches::Multiple(_first, second) => {
207                    return Err(Error::new(
208                        "Only a single field was expected".to_string(),
209                        second.line_number,
210                    ))
211                }
212                Matches::WrongType(element) => {
213                    return Err(Error::new(
214                        "A field was expected".to_string(),
215                        element.line_number(),
216                    ))
217                }
218            },
219            None => None,
220        };
221
222        Ok(FieldQuery::new(
223            element_option,
224            Some(key.to_string()),
225            FieldQueryParent::SectionQuery(self),
226        ))
227    }
228
229    pub fn flag(&self, key: &str) -> Result<FlagQuery, Error> {
230        let element_option = match self.element_option {
231            Some(section) => match section.get_elements_ref().single_flag_with_key(key) {
232                Matches::None => None,
233                Matches::One(flag) => Some(flag),
234                Matches::Multiple(_first, second) => {
235                    return Err(Error::new(
236                        "Only a single flag was expected".to_string(),
237                        second.line_number,
238                    ))
239                }
240                Matches::WrongType(element) => {
241                    return Err(Error::new(
242                        "A flag was expected".to_string(),
243                        element.line_number(),
244                    ))
245                }
246            },
247            None => None,
248        };
249
250        Ok(FlagQuery::new(
251            element_option,
252            Some(key.to_string()),
253            FlagQueryParent::SectionQuery(self),
254        ))
255    }
256
257    pub fn missing_error(&self) -> Error {
258        match self.parent {
259            SectionQueryParent::Document(_) => Error::new(
260                format!(
261                    "Section {} not found",
262                    self.key.as_deref().unwrap_or("(can have any key)")
263                ),
264                Document::LINE_NUMBER,
265            ),
266            SectionQueryParent::Section(section) => Error::new(
267                format!(
268                    "Section {} not found",
269                    self.key.as_deref().unwrap_or("(can have any key)")
270                ),
271                section.line_number,
272            ),
273            SectionQueryParent::SectionQuery(section_query) => match section_query.element_option {
274                Some(section) => Error::new(
275                    format!(
276                        "Section {} not found",
277                        self.key.as_deref().unwrap_or("(can have any key)")
278                    ),
279                    section.line_number,
280                ),
281                None => section_query.missing_error(),
282            },
283        }
284    }
285
286    pub fn section(&self, key: &str) -> Result<SectionQuery, Error> {
287        let element_option = match self.element_option {
288            Some(section) => match section.get_elements_ref().single_section_with_key(key) {
289                Matches::None => None,
290                Matches::One(subsection) => Some(subsection),
291                Matches::Multiple(_first, second) => {
292                    return Err(Error::new(
293                        "Only a single section was expected".to_string(),
294                        second.line_number,
295                    ))
296                }
297                Matches::WrongType(element) => {
298                    return Err(Error::new(
299                        "A section was expected".to_string(),
300                        element.line_number(),
301                    ))
302                }
303            },
304            None => None,
305        };
306
307        Ok(SectionQuery::new(
308            element_option,
309            Some(key.to_string()),
310            SectionQueryParent::SectionQuery(self),
311        ))
312    }
313
314    pub fn snippet(&self) -> Result<String, Error> {
315        match self.element_option {
316            Some(section) => Ok(section.snippet()),
317            None => Err(self.missing_error()),
318        }
319    }
320
321    pub fn snippet_with_options(
322        &self,
323        printer: &dyn Printer,
324        gutter: bool,
325    ) -> Result<String, Error> {
326        match self.element_option {
327            Some(section) => Ok(section.snippet_with_options(printer, gutter)),
328            None => Err(self.missing_error()),
329        }
330    }
331}
332
333impl<'a> SectionQueryImpl<'a> for SectionQuery<'a> {
334    fn element(&self) -> Option<&Section> {
335        self.element_option
336    }
337
338    fn new(
339        element_option: Option<&'a Section>,
340        key: Option<String>,
341        parent: SectionQueryParent<'a>,
342    ) -> SectionQuery<'a> {
343        SectionQuery {
344            element_option,
345            key,
346            parent,
347        }
348    }
349}