parse/xml/
attribute.rs

1use roxmltree::{
2    Attribute,
3    Node,
4};
5use std::ops::Range;
6
7use crate::{
8    DottedPath,
9    LowerName,
10    NameError,
11    PathError,
12    UpperName,
13};
14
15use super::{
16    Once,
17    ParseError,
18};
19
20impl Once<bool> {
21    /// Parse a single boolean (yes/no) attribute
22    pub fn boolean_attribute<'input, E>(
23        &mut self,
24        attribute: Attribute<'input, 'input>,
25        errors: &mut Vec<E>,
26    )
27    where
28        E: From<ParseError> + From<NameError>,
29    {
30        if self.is_duplicate_attribute(&attribute, errors) {
31            return;
32        }
33
34        match attribute.value() {
35            "yes" | "true" =>
36                self.attribute_success(attribute, true),
37
38            "no" | "false" =>
39                self.attribute_success(attribute, false),
40
41            _ => {
42                errors.push(ParseError::BooleanInvalid {
43                    value: attribute.range_value().into(),
44                }.into());
45
46                self.attribute_failure(attribute);
47            }
48        }
49    }
50}
51
52impl Once<DottedPath> {
53    /// Parse a single dotted path attribute
54    pub fn dotted_path_attribute<'input, E>(
55        &mut self,
56        attribute: Attribute<'input, 'input>,
57        errors: &mut Vec<E>,
58    )
59    where
60        E: From<ParseError> + From<PathError>,
61    {
62        if self.is_duplicate_attribute(&attribute, errors) {
63            return;
64        }
65
66        match DottedPath::try_from(attribute) {
67            Ok(value) =>
68                self.attribute_success(attribute, value),
69
70            Err(error) => {
71                errors.push(error.into());
72
73                self.attribute_failure(attribute);
74            }
75        }
76    }
77}
78
79/// Raise a `Duplicate attribute` error
80pub fn duplicate_attribute<E: From<ParseError>>(
81    attribute_name: &str,
82    duplicate_range: Range<usize>,
83    existing_range: Range<usize>,
84    errors: &mut Vec<E>,
85) {
86    errors.push(ParseError::DuplicateAttribute {
87        attribute: attribute_name.to_owned(),
88        duplicate: duplicate_range.into(),
89        existing: existing_range.into(),
90    }.into());
91}
92
93impl<T: Clone> Once<T> {
94    /// Parse a single enum attribute
95    pub fn enum_attribute<'input, E>(
96        &mut self,
97        values: &'static [(&'static str, T)],
98        attribute: Attribute<'input, 'input>,
99        errors: &mut Vec<E>,
100    )
101    where
102        E: From<ParseError> + From<NameError>,
103    {
104        if self.is_duplicate_attribute(&attribute, errors) {
105            return;
106        }
107
108        for (name, value) in values {
109            if *name == attribute.value() {
110                self.attribute_success(attribute, value.clone());
111
112                return;
113            }
114        }
115
116        let last = values.len() - 1;
117        let mut message = format!(
118            "Expecting one of `{}",
119            values[0].0,
120        );
121        for (name, _) in &values[1..last] {
122            message.push_str("`, `");
123            message.push_str(name);
124        }
125        message.push_str("` or `");
126        message.push_str(values[last].0);
127        message.push_str("`");
128        errors.push(ParseError::EnumValue {
129            message,
130            value: attribute.range_value().into(),
131        }.into());
132
133        self.attribute_failure(attribute);
134    }
135}
136
137impl<T> Once<T> {
138    /// Is this a duplicate attribute?
139    ///
140    /// If yes, then raise an error.
141    pub fn is_duplicate_attribute<'input, E>(
142        &self,
143        attribute: &Attribute<'input, 'input>,
144        errors: &mut Vec<E>,
145    ) -> bool
146    where
147        E: From<ParseError>,
148    {
149        match self {
150            Once::Success { name_range, .. } |
151            Once::Failure { name_range, .. } => {
152                duplicate_attribute(
153                    attribute.name(),
154                    attribute.range_qname(),
155                    name_range.clone(),
156                    errors,
157                );
158
159                true
160            }
161
162            Once::Missing { .. } =>
163                false,
164        }
165    }
166}
167
168impl Once<usize> {
169    /// Parse a single length attribute,
170    /// a `usize` with bounded range.
171    pub fn length_attribute<'input, E>(
172        &mut self,
173        range: std::ops::RangeInclusive<usize>,
174        attribute: Attribute<'input, 'input>,
175        errors: &mut Vec<E>,
176    )
177    where
178        E: From<ParseError>,
179    {
180        if self.is_duplicate_attribute(&attribute, errors) {
181            return;
182        }
183
184        if let Ok(value) = usize::from_str_radix(attribute.value(), 10) {
185            if range.contains(&value) {
186                self.attribute_success(attribute, value);
187            } else {
188                errors.push(ParseError::LengthRange {
189                    min: *range.start(),
190                    max: *range.end(),
191                    value: attribute.range_value().into(),
192                }.into());
193
194                self.attribute_failure(attribute);
195            }
196        } else {
197            errors.push(ParseError::LengthInvalid {
198                value: attribute.range_value().into(),
199            }.into());
200
201            self.attribute_failure(attribute);
202        }
203    }
204}
205
206impl Once<LowerName> {
207    /// Parse a single lowercase name attribute
208    pub fn lower_name_attribute<'input, E>(
209        &mut self,
210        attribute: Attribute<'input, 'input>,
211        errors: &mut Vec<E>,
212    )
213    where
214        E: From<ParseError> + From<NameError>,
215    {
216        if self.is_duplicate_attribute(&attribute, errors) {
217            return;
218        }
219
220        match LowerName::try_from(attribute) {
221            Ok(value) =>
222                self.attribute_success(attribute, value),
223
224            Err(error) => {
225                errors.push(error.into());
226
227                self.attribute_failure(attribute);
228            }
229        }
230    }
231}
232
233/// Raise a `Missing attribute` error
234pub fn missing_attribute<'input, E>(
235    name: &str,
236    parent: &Node<'input, 'input>,
237    errors: &mut Vec<E>,
238)
239where
240    E: From<ParseError>,
241{
242    errors.push(ParseError::MissingAttribute {
243        element: parent.tag_name().name().to_owned(),
244        attribute: name.to_owned(),
245        parent: parent.range().into(),
246    }.into());
247}
248
249impl Once<usize> {
250    /// Parse a single number attribute,
251    /// a usize with no bound requirements
252    pub fn number_attribute<'input, E>(
253        &mut self,
254        attribute: Attribute<'input, 'input>,
255        errors: &mut Vec<E>,
256    )
257    where
258        E: From<ParseError>,
259    {
260        if self.is_duplicate_attribute(&attribute, errors) {
261            return;
262        }
263
264        if let Ok(value) = usize::from_str_radix(attribute.value(), 10) {
265            self.attribute_success(attribute, value);
266        } else {
267            errors.push(ParseError::NumberValue {
268                value: attribute.range_value().into(),
269            }.into());
270
271            self.attribute_failure(attribute);
272        }
273    }
274}
275
276impl Once<String> {
277    /// Parse a string attribute
278    pub fn string_attribute<'input, E>(
279        &mut self,
280        attribute: Attribute<'input, 'input>,
281        errors: &mut Vec<E>,
282    )
283    where
284        E: From<ParseError>,
285    {
286        if self.is_duplicate_attribute(&attribute, errors) {
287            return;
288        }
289
290        let value = attribute.value().trim_ascii();
291
292        if value.is_empty() {
293            errors.push(ParseError::StringEmpty {
294                value: attribute.range().into(),
295            }.into());
296
297            self.attribute_failure(attribute);
298        } else {
299            self.attribute_success(attribute, value.to_owned());
300        }
301    }
302}
303
304impl Once<UpperName> {
305    /// Parse a single uppercase name attribute
306    pub fn upper_name_attribute<'input, E>(
307        &mut self,
308        attribute: Attribute<'input, 'input>,
309        errors: &mut Vec<E>,
310    )
311    where
312        E: From<ParseError> + From<NameError>,
313    {
314        if self.is_duplicate_attribute(&attribute, errors) {
315            return;
316        }
317
318        match UpperName::try_from(attribute) {
319            Ok(value) =>
320                self.attribute_success(attribute, value),
321
322            Err(error) => {
323                errors.push(error.into());
324
325                self.attribute_failure(attribute);
326            }
327        }
328    }
329}
330
331/// Raise an 'Unexpected attribute' error
332pub fn unexpected_attribute<'input, E>(
333    attribute: Attribute<'input, 'input>,
334    errors: &mut Vec<E>,
335)
336where
337    E: From<ParseError>,
338{
339    errors.push(ParseError::UnexpectedAttribute {
340        unexpected: attribute.range_qname().into(),
341    }.into());
342}