mabel_eno/queries/
field_query.rs

1use std::str::FromStr;
2
3use crate::elements::{Document, Element, Field, FieldContent, FieldImpl, Item, Section};
4use crate::queries::{AttributeQuery, AttributeQueryImpl, SectionQuery, SectionQueryImpl};
5use crate::{Error, Printer};
6
7pub struct FieldQuery<'a> {
8    element_option: Option<&'a Field>,
9    key: Option<String>,
10    parent: FieldQueryParent<'a>,
11}
12
13pub trait FieldQueryImpl<'a> {
14    fn element(&self) -> Option<&Field>;
15    #[allow(clippy::new_ret_no_self)]
16    fn new(
17        element_option: Option<&'a Field>,
18        key: Option<String>,
19        parent: FieldQueryParent<'a>,
20    ) -> FieldQuery<'a>;
21}
22
23pub enum FieldQueryParent<'a> {
24    Document(&'a Document),
25    Section(&'a Section),
26    SectionQuery(&'a SectionQuery<'a>),
27}
28
29impl<'a> FieldQuery<'a> {
30    pub fn attribute(&self, key: &str) -> Result<AttributeQuery, Error> {
31        let element_option = match self.element_option {
32            Some(field) => match &field.get_content() {
33                FieldContent::Attributes(attributes) => {
34                    let mut attribute_option = None;
35                    for attribute in attributes {
36                        if attribute.key() == key {
37                            match attribute_option {
38                                Some(_) => {
39                                    return Err(Error::new(
40                                        format!("Multiple attributes with key {} found", key),
41                                        attribute.line_number,
42                                    ))
43                                }
44                                None => {
45                                    attribute.touch();
46                                    attribute_option = Some(attribute);
47                                }
48                            }
49                        }
50                    }
51                    attribute_option
52                }
53                FieldContent::Items(items) => {
54                    return Err(Error::new(
55                        "Attributes expected, Items found".to_string(),
56                        items[0].line_number,
57                    ))
58                }
59                FieldContent::Value { .. } => {
60                    return Err(Error::new(
61                        "Attributes expected, Value found".to_string(),
62                        field.line_number,
63                    ))
64                }
65                FieldContent::None => None,
66            },
67            None => None,
68        };
69
70        if let Some(element) = element_option {
71            element.touch();
72        }
73
74        Ok(AttributeQuery::new(
75            element_option,
76            Some(key.to_string()),
77            self,
78        ))
79    }
80
81    pub fn attributes(&self) -> Result<Vec<crate::Attribute>, Error> {
82        match self.element_option {
83            Some(field) => match field.get_content() {
84                FieldContent::Attributes(attributes) => Ok(attributes.to_vec()),
85                FieldContent::None => Ok(vec![]),
86                FieldContent::Items(items) => Err(Error::new(
87                    "Attributes expected, Items found".to_owned(),
88                    items[0].line_number,
89                )),
90                FieldContent::Value { .. } => Err(Error::new(
91                    "Attributes expected, Value found".to_owned(),
92                    field.line_number,
93                )),
94            },
95            None => Ok(vec![]),
96        }
97    }
98
99    pub fn items(&self) -> Result<&[Item], Error> {
100        match self.element_option {
101            Some(field) => match &field.get_content() {
102                FieldContent::Attributes(attributes) => Err(Error::new(
103                    "Items expected, Attributes found".to_string(),
104                    attributes[0].line_number,
105                )),
106                FieldContent::Items(items) => {
107                    for item in items.iter() {
108                        item.touch();
109                    }
110                    Ok(items.as_slice())
111                }
112                FieldContent::Value { .. } => Err(Error::new(
113                    "Items expected, Value found".to_string(),
114                    field.line_number,
115                )),
116                FieldContent::None => Ok(&[]),
117            },
118            None => Ok(&[]),
119        }
120    }
121
122    pub fn missing_error(&self) -> Error {
123        match self.parent {
124            FieldQueryParent::Document(_) => Error::new(
125                format!(
126                    "Field {} not found",
127                    self.key.as_deref().unwrap_or("(can have any key)")
128                ),
129                Document::LINE_NUMBER,
130            ),
131            FieldQueryParent::Section(section) => Error::new(
132                format!(
133                    "Field {} not found",
134                    self.key.as_deref().unwrap_or("(can have any key)")
135                ),
136                section.line_number,
137            ),
138            FieldQueryParent::SectionQuery(section_query) => match section_query.element() {
139                Some(section) => Error::new(
140                    format!(
141                        "Field {} not found",
142                        self.key.as_deref().unwrap_or("(can have any key)")
143                    ),
144                    section.line_number,
145                ),
146                None => section_query.missing_error(),
147            },
148        }
149    }
150
151    pub fn optional_value(&self) -> Result<Option<String>, Error> {
152        match &self.element_option {
153            Some(field) => match &field.get_content() {
154                FieldContent::Attributes(attributes) => Err(Error::new(
155                    "Value expected, Attributes found".to_string(),
156                    attributes[0].line_number,
157                )),
158                FieldContent::Items(items) => Err(Error::new(
159                    "Value expected, Items found".to_string(),
160                    items[0].line_number,
161                )),
162                FieldContent::Value(range) => {
163                    Ok(Some(field.get_document_content()[range.clone()].to_owned()))
164                }
165                FieldContent::None => Ok(None),
166            },
167            None => Ok(None),
168        }
169    }
170
171    pub fn required_value<T: FromStr>(&self) -> Result<T, Error>
172    where
173        <T as FromStr>::Err: std::fmt::Display,
174    {
175        match &self.element_option {
176            Some(field) => match &field.get_content() {
177                FieldContent::Attributes(attributes) => Err(Error::new(
178                    "Value expected, Attributes found".to_string(),
179                    attributes[0].line_number,
180                )),
181                FieldContent::Items(items) => Err(Error::new(
182                    "Value expected, Items found".to_string(),
183                    items[0].line_number,
184                )),
185                FieldContent::Value(range) => {
186                    let value = field.get_document_content()[range.clone()].to_owned();
187                    match value.parse::<T>() {
188                        Ok(converted) => Ok(converted),
189                        Err(err) => Err(Error::new(format!("{}", err), field.line_number)),
190                    }
191                }
192                FieldContent::None => {
193                    Err(Error::new("Missing value".to_string(), field.line_number))
194                }
195            },
196            None => Err(self.missing_error()),
197        }
198    }
199
200    pub fn snippet(&self) -> Result<String, Error> {
201        match self.element_option {
202            Some(field) => Ok(field.snippet()),
203            None => Err(self.missing_error()),
204        }
205    }
206
207    pub fn snippet_with_options(
208        &self,
209        printer: &dyn Printer,
210        gutter: bool,
211    ) -> Result<String, Error> {
212        match self.element_option {
213            Some(field) => Ok(field.snippet_with_options(printer, gutter)),
214            None => Err(self.missing_error()),
215        }
216    }
217}
218
219impl<'a> FieldQueryImpl<'a> for FieldQuery<'a> {
220    fn element(&self) -> Option<&Field> {
221        self.element_option
222    }
223
224    fn new(
225        element_option: Option<&'a Field>,
226        key: Option<String>,
227        parent: FieldQueryParent<'a>,
228    ) -> FieldQuery<'a> {
229        FieldQuery {
230            element_option,
231            key,
232            parent,
233        }
234    }
235}