Skip to main content

config/
xml.rs

1use crate::{pascal_case, path, Error, FileSource, Settings};
2use std::{
3    cell::RefCell,
4    fmt::{self, Display, Formatter},
5    fs::File,
6    io::BufReader,
7    ops::Deref,
8    rc::Rc,
9};
10use tokens::{ChangeToken, FileChangeToken, NeverChangeToken};
11use xml_rs::{
12    attribute::OwnedAttribute,
13    name::OwnedName,
14    reader::{EventReader, XmlEvent::*},
15};
16
17trait LocalNameResolver {
18    fn local_name_or_error(&self, element: &OwnedName, line: usize) -> Result<String, String>;
19}
20
21impl LocalNameResolver for OwnedName {
22    fn local_name_or_error(&self, element: &OwnedName, line: usize) -> Result<String, String> {
23        if self.namespace.is_none() {
24            Ok(self.local_name.clone())
25        } else {
26            Err(format!(
27                "XML namespaces are not supported. ({name}, Line: {line})",
28                name = &element.local_name
29            ))
30        }
31    }
32}
33
34trait VecExt<TKey: PartialEq, TValue> {
35    fn get_or_add(&mut self, key: TKey) -> &mut TValue;
36}
37
38impl VecExt<String, Vec<Rc<RefCell<Element>>>> for Vec<(String, Vec<Rc<RefCell<Element>>>)> {
39    fn get_or_add(&mut self, key: String) -> &mut Vec<Rc<RefCell<Element>>> {
40        let index = self.iter_mut().position(|i| i.0 == key).unwrap_or(self.len());
41
42        if index == self.len() {
43            self.push((key, Vec::new()));
44        }
45
46        &mut self[index].1
47    }
48}
49
50struct Attribute(String, String);
51
52struct Element {
53    line: usize,
54    element_name: String,
55    name: Option<String>,
56    sibling_name: String,
57    children: Vec<(String, Vec<Rc<RefCell<Element>>>)>,
58    text: Option<String>,
59    attributes: Vec<Attribute>,
60}
61
62impl Element {
63    fn new(element_name: OwnedName, attributes: Vec<OwnedAttribute>, line: usize) -> Result<Self, String> {
64        let name = get_name(&element_name, &attributes, line)?;
65        let local_name = element_name.local_name_or_error(&element_name, line)?;
66        let sibling_name = name
67            .as_ref()
68            .map(|n| path::combine(&[&local_name.to_uppercase(), &n.to_uppercase()]))
69            .unwrap_or(local_name.to_uppercase());
70
71        Ok(Self {
72            line,
73            element_name: local_name,
74            name,
75            sibling_name,
76            children: Default::default(),
77            text: None,
78            attributes: attributes
79                .into_iter()
80                .map(|a| Ok(Attribute(a.name.local_name_or_error(&element_name, line)?, a.value)))
81                .collect::<Result<Vec<Attribute>, String>>()?,
82        })
83    }
84}
85
86#[derive(Default)]
87struct Prefix {
88    text: String,
89    lengths: Vec<usize>,
90}
91
92impl Prefix {
93    fn push<S: AsRef<str>>(&mut self, value: S) {
94        if self.text.is_empty() {
95            self.text.push_str(value.as_ref());
96            self.lengths.push(value.as_ref().len());
97        } else {
98            self.text.push(path::delimiter());
99            self.text.push_str(value.as_ref());
100            self.lengths.push(value.as_ref().len() + 1);
101        }
102    }
103
104    fn pop(&mut self) {
105        if let Some(length) = self.lengths.pop() {
106            self.text.truncate(self.text.len() - length);
107        }
108    }
109}
110
111impl Display for Prefix {
112    #[inline]
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        f.write_str(&self.text)
115    }
116}
117
118fn get_name(element: &OwnedName, attributes: &[OwnedAttribute], line: usize) -> Result<Option<String>, String> {
119    for attribute in attributes {
120        let local_name = attribute.name.local_name_or_error(element, line)?;
121
122        if local_name.eq_ignore_ascii_case("name") {
123            return Ok(Some(attribute.value.clone()));
124        }
125    }
126
127    Ok(None)
128}
129
130fn visit_element(prefix: &mut Prefix, element: &Element, settings: &mut Settings) -> Result<(), String> {
131    visit_attributes(prefix, element, settings)?;
132    visit_element_content(prefix, element, settings)?;
133    visit_children(prefix, element, settings)
134}
135
136fn visit_element_content(prefix: &mut Prefix, element: &Element, settings: &mut Settings) -> Result<(), String> {
137    let Some(ref value) = element.text else {
138        return Ok(());
139    };
140
141    add_setting(prefix.to_string(), value.clone(), element, settings)
142}
143
144fn visit_element_child(
145    prefix: &mut Prefix,
146    child: &Element,
147    index: Option<usize>,
148    settings: &mut Settings,
149) -> Result<(), String> {
150    prefix.push(&child.element_name);
151
152    if let Some(ref name) = child.name {
153        prefix.push(name);
154    }
155
156    if let Some(i) = index {
157        prefix.push(i.to_string());
158    }
159
160    visit_element(prefix, child, settings)?;
161
162    if index.is_some() {
163        prefix.pop();
164    }
165
166    if child.name.is_some() {
167        prefix.pop();
168    }
169
170    prefix.pop();
171    Ok(())
172}
173
174fn visit_attributes(prefix: &mut Prefix, element: &Element, settings: &mut Settings) -> Result<(), String> {
175    for attribute in &element.attributes {
176        prefix.push(&attribute.0);
177        add_setting(prefix.to_string(), attribute.1.clone(), element, settings)?;
178        prefix.pop();
179    }
180
181    Ok(())
182}
183
184fn visit_children(prefix: &mut Prefix, element: &Element, settings: &mut Settings) -> Result<(), String> {
185    for children in element.children.iter().map(|i| &i.1) {
186        if children.len() == 1 {
187            visit_element_child(prefix, &children[0].deref().borrow(), None, settings)?;
188        } else {
189            for (i, child) in children.iter().enumerate() {
190                visit_element_child(prefix, &child.deref().borrow(), Some(i), settings)?;
191            }
192        }
193    }
194
195    Ok(())
196}
197
198fn add_setting(key: String, value: String, element: &Element, settings: &mut Settings) -> Result<(), String> {
199    if settings.insert(pascal_case(&key), value).is_some() {
200        Err(format!(
201            "A duplicate key '{key}' was found. ({name}, Line: {line})",
202            name = &element.element_name,
203            line = element.line,
204        ))
205    } else {
206        Ok(())
207    }
208}
209
210fn visit(file: File, settings: &mut Settings) -> Result<(), String> {
211    let content = BufReader::new(file);
212    let events = EventReader::new(content);
213    let mut has_content = false;
214    let mut last_name = None;
215    let mut line = 0;
216    let mut root = None;
217    let mut current = Vec::<(OwnedName, Rc<RefCell<Element>>)>::new();
218
219    for event in events.into_iter() {
220        match event {
221            Ok(StartElement { name, attributes, .. }) => {
222                line += 1;
223                has_content = false;
224                last_name = Some(name.clone());
225                let element = Element::new(name.clone(), attributes, line)?;
226                let key = element.sibling_name.clone();
227                let child = Rc::new(RefCell::new(element));
228
229                if let Some(parent) = current.last() {
230                    parent.1.borrow_mut().children.get_or_add(key).push(child.clone());
231                } else {
232                    root = Some(child.clone());
233                }
234
235                current.push((name, child));
236            }
237            Ok(EndElement { name }) => {
238                if has_content {
239                    if let Some(ref last) = last_name {
240                        if last != &name {
241                            line += 1;
242                        }
243                    }
244                }
245
246                if let Some((current_name, _)) = current.pop() {
247                    last_name = Some(current_name);
248                }
249            }
250            Ok(CData(text)) | Ok(Characters(text)) => {
251                has_content = true;
252                if let Some(parent) = current.last() {
253                    parent.1.borrow_mut().text = Some(text);
254                }
255            }
256            _ => {}
257        };
258    }
259
260    if let Some(cell) = root.take() {
261        let element = &cell.deref().borrow();
262        let mut prefix = Prefix::default();
263
264        if let Some(ref name) = element.name {
265            prefix.push(name);
266        }
267
268        visit_element(&mut prefix, element, settings)?;
269    }
270
271    Ok(())
272}
273
274/// Represents a [configuration provider](crate::Provider) for `*.xml` files.
275pub struct Provider(FileSource);
276
277impl Provider {
278    /// Initializes a new `*.xml` file configuration provider.
279    ///
280    /// # Arguments
281    ///
282    /// * `file` - The `*.xml` [file source](FileSource) information
283    #[inline]
284    pub fn new(file: FileSource) -> Self {
285        Self(file)
286    }
287}
288
289impl crate::Provider for Provider {
290    #[inline]
291    fn name(&self) -> &str {
292        path::provider(&self.0.path, "Xml")
293    }
294
295    fn reload_token(&self) -> Box<dyn ChangeToken> {
296        if self.0.reload_on_change {
297            Box::new(FileChangeToken::new(self.0.path.clone()))
298        } else {
299            Box::new(NeverChangeToken)
300        }
301    }
302
303    fn load(&self, settings: &mut Settings) -> crate::Result {
304        if !self.0.path.is_file() {
305            if self.0.optional {
306                return Ok(());
307            } else {
308                return Err(Error::MissingFile(self.0.path.clone()));
309            }
310        }
311
312        let file = File::open(&self.0.path).map_err(Error::unknown)?;
313
314        visit(file, settings).map_err(|e| Error::InvalidFile {
315            message: e,
316            path: self.0.path.clone(),
317        })?;
318
319        Ok(())
320    }
321}