config/
xml.rs

1use crate::{
2    util::*, ConfigurationBuilder, ConfigurationPath, ConfigurationProvider, ConfigurationSource,
3    FileSource, LoadError, LoadResult, Value,
4};
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::fs::File;
8use std::io::BufReader;
9use std::ops::Deref;
10use std::rc::Rc;
11use std::sync::{Arc, RwLock};
12use tokens::{ChangeToken, FileChangeToken, SharedChangeToken, SingleChangeToken, Subscription};
13use xml_rs::attribute::OwnedAttribute;
14use xml_rs::name::OwnedName;
15use xml_rs::reader::{EventReader, XmlEvent};
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. ({}, Line: {})",
28                &element.local_name, line
29            ))
30        }
31    }
32}
33
34trait VecExtensions<TKey: PartialEq, TValue> {
35    fn get_or_add(&mut self, key: TKey) -> &mut TValue;
36}
37
38impl VecExtensions<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
41            .iter_mut()
42            .position(|i| &i.0 == &key)
43            .unwrap_or(self.len());
44
45        if index == self.len() {
46            self.push((key, Vec::new()));
47        }
48
49        &mut self[index].1
50    }
51}
52
53struct Attribute(String, String);
54
55struct Element {
56    line: usize,
57    element_name: String,
58    name: Option<String>,
59    sibling_name: String,
60    children: Vec<(String, Vec<Rc<RefCell<Element>>>)>,
61    text: Option<String>,
62    attributes: Vec<Attribute>,
63}
64
65impl Element {
66    fn new(
67        element_name: OwnedName,
68        attributes: Vec<OwnedAttribute>,
69        line: usize,
70    ) -> Result<Self, String> {
71        let name = get_name(&element_name, &attributes, line)?;
72        let local_name = element_name.local_name_or_error(&element_name, line)?;
73        let sibling_name = name
74            .as_ref()
75            .and_then(|n| {
76                Some(ConfigurationPath::combine(&[
77                    &local_name.to_uppercase(),
78                    &n.to_uppercase(),
79                ]))
80            })
81            .unwrap_or(local_name.to_uppercase());
82
83        Ok(Self {
84            line,
85            element_name: local_name,
86            name,
87            sibling_name,
88            children: Default::default(),
89            text: None,
90            attributes: attributes
91                .into_iter()
92                .map(|a| {
93                    Ok(Attribute(
94                        a.name.local_name_or_error(&element_name, line)?,
95                        a.value,
96                    ))
97                })
98                .collect::<Result<Vec<Attribute>, String>>()?,
99        })
100    }
101}
102
103#[derive(Default)]
104struct Prefix {
105    text: String,
106    lengths: Vec<usize>,
107}
108
109impl Prefix {
110    fn push<S: AsRef<str>>(&mut self, value: S) {
111        if self.text.is_empty() {
112            self.text.push_str(&value.as_ref());
113            self.lengths.push(value.as_ref().len());
114        } else {
115            self.text.push_str(ConfigurationPath::key_delimiter());
116            self.text.push_str(&value.as_ref());
117            self.lengths
118                .push(value.as_ref().len() + ConfigurationPath::key_delimiter().len());
119        }
120    }
121
122    fn pop(&mut self) {
123        if let Some(length) = self.lengths.pop() {
124            let idx = self.text.len() - length;
125            for _ in 0..length {
126                let _ = self.text.remove(idx);
127            }
128        }
129    }
130}
131
132impl ToString for Prefix {
133    fn to_string(&self) -> String {
134        self.text.clone()
135    }
136}
137
138fn get_name(
139    element: &OwnedName,
140    attributes: &Vec<OwnedAttribute>,
141    line: usize,
142) -> Result<Option<String>, String> {
143    for attribute in attributes {
144        let local_name = attribute.name.local_name_or_error(element, line)?;
145
146        match local_name.as_str() {
147            "name" | "Name" | "NAME" => {
148                return Ok(Some(attribute.value.clone()));
149            }
150            _ => {}
151        }
152    }
153
154    Ok(None)
155}
156
157fn process_element(
158    prefix: &mut Prefix,
159    element: &Element,
160    config: &mut HashMap<String, (String, Value)>,
161) -> Result<(), String> {
162    process_attributes(prefix, element, config)?;
163    process_element_content(prefix, element, config)?;
164    process_children(prefix, element, config)
165}
166
167fn process_element_content(
168    prefix: &mut Prefix,
169    element: &Element,
170    config: &mut HashMap<String, (String, Value)>,
171) -> Result<(), String> {
172    if let Some(ref value) = element.text {
173        add_to_config(prefix.to_string(), value.clone(), element, config)
174    } else {
175        Ok(())
176    }
177}
178
179fn process_element_child(
180    prefix: &mut Prefix,
181    child: &Element,
182    index: Option<usize>,
183    config: &mut HashMap<String, (String, Value)>,
184) -> Result<(), String> {
185    prefix.push(&child.element_name);
186
187    if let Some(ref name) = child.name {
188        prefix.push(name);
189    }
190
191    if let Some(i) = index {
192        prefix.push(i.to_string());
193    }
194
195    process_element(prefix, child, config)?;
196
197    if index.is_some() {
198        prefix.pop();
199    }
200
201    if child.name.is_some() {
202        prefix.pop();
203    }
204
205    prefix.pop();
206    Ok(())
207}
208
209fn process_attributes(
210    prefix: &mut Prefix,
211    element: &Element,
212    config: &mut HashMap<String, (String, Value)>,
213) -> Result<(), String> {
214    for attribute in &element.attributes {
215        prefix.push(&attribute.0);
216        add_to_config(prefix.to_string(), attribute.1.clone(), element, config)?;
217        prefix.pop();
218    }
219
220    Ok(())
221}
222
223fn process_children(
224    prefix: &mut Prefix,
225    element: &Element,
226    config: &mut HashMap<String, (String, Value)>,
227) -> Result<(), String> {
228    for children in element.children.iter().map(|i| &i.1) {
229        if children.len() == 1 {
230            process_element_child(prefix, &children[0].deref().borrow(), None, config)?;
231        } else {
232            for (i, child) in children.iter().enumerate() {
233                process_element_child(prefix, &child.deref().borrow(), Some(i), config)?;
234            }
235        }
236    }
237
238    Ok(())
239}
240
241fn add_to_config(
242    key: String,
243    value: String,
244    element: &Element,
245    config: &mut HashMap<String, (String, Value)>,
246) -> Result<(), String> {
247    if let Some((dup_key, _)) = config.insert(key.to_uppercase(), (key, value.into())) {
248        Err(format!(
249            "A duplicate key '{}' was found. ({}, Line: {})",
250            &dup_key, &element.element_name, element.line
251        ))
252    } else {
253        Ok(())
254    }
255}
256
257fn to_config(
258    mut root: Option<Rc<RefCell<Element>>>,
259) -> Result<HashMap<String, (String, Value)>, String> {
260    if let Some(cell) = root.take() {
261        let element = &cell.deref().borrow();
262        let mut data = HashMap::new();
263        let mut prefix = Prefix::default();
264
265        if let Some(ref name) = element.name {
266            prefix.push(name);
267        }
268
269        process_element(&mut prefix, &element, &mut data)?;
270        data.shrink_to_fit();
271        Ok(data)
272    } else {
273        Ok(HashMap::with_capacity(0))
274    }
275}
276
277fn visit(file: File) -> Result<HashMap<String, (String, Value)>, String> {
278    let content = BufReader::new(file);
279    let events = EventReader::new(content);
280    let mut has_content = false;
281    let mut last_name = None;
282    let mut line = 0;
283    let mut root = None;
284    let mut current = Vec::<(OwnedName, Rc<RefCell<Element>>)>::new();
285
286    for event in events.into_iter() {
287        match event {
288            Ok(XmlEvent::StartElement {
289                name, attributes, ..
290            }) => {
291                line += 1;
292                has_content = false;
293                last_name = Some(name.clone());
294                let element = Element::new(name.clone(), attributes, line)?;
295                let key = element.sibling_name.clone();
296                let child = Rc::new(RefCell::new(element));
297
298                if let Some(parent) = current.last() {
299                    parent
300                        .1
301                        .borrow_mut()
302                        .children
303                        .get_or_add(key)
304                        .push(child.clone());
305                } else {
306                    root = Some(child.clone());
307                }
308
309                current.push((name, child));
310            }
311            Ok(XmlEvent::EndElement { name }) => {
312                if has_content {
313                    if let Some(ref last) = last_name {
314                        if last != &name {
315                            line += 1;
316                        }
317                    }
318                }
319
320                if let Some((current_name, _)) = current.pop() {
321                    last_name = Some(current_name);
322                }
323            }
324            Ok(XmlEvent::CData(text)) | Ok(XmlEvent::Characters(text)) => {
325                has_content = true;
326                if let Some(parent) = current.last() {
327                    parent.1.borrow_mut().text = Some(text);
328                }
329            }
330            _ => {}
331        };
332    }
333
334    to_config(root)
335}
336
337struct InnerProvider {
338    file: FileSource,
339    data: RwLock<HashMap<String, (String, Value)>>,
340    token: RwLock<SharedChangeToken<SingleChangeToken>>,
341}
342
343impl InnerProvider {
344    fn new(file: FileSource) -> Self {
345        Self {
346            file,
347            data: RwLock::new(HashMap::with_capacity(0)),
348            token: Default::default(),
349        }
350    }
351
352    fn load(&self, reload: bool) -> LoadResult {
353        if !self.file.path.is_file() {
354            if self.file.optional || reload {
355                let mut data = self.data.write().unwrap();
356                if !data.is_empty() {
357                    *data = HashMap::with_capacity(0);
358                }
359
360                return Ok(());
361            } else {
362                return Err(LoadError::File {
363                    message: format!(
364                        "The configuration file '{}' was not found and is not optional.",
365                        self.file.path.display()
366                    ),
367                    path: self.file.path.clone(),
368                });
369            }
370        }
371
372        if let Ok(file) = File::open(&self.file.path) {
373            let data = visit(file).map_err(|e| LoadError::File {
374                message: e,
375                path: self.file.path.clone(),
376            })?;
377            *self.data.write().unwrap() = data;
378        } else {
379            *self.data.write().unwrap() = HashMap::with_capacity(0);
380        }
381
382        let previous = std::mem::replace(
383            &mut *self.token.write().unwrap(),
384            SharedChangeToken::default(),
385        );
386
387        previous.notify();
388        Ok(())
389    }
390
391    fn get(&self, key: &str) -> Option<Value> {
392        self.data
393            .read()
394            .unwrap()
395            .get(&key.to_uppercase())
396            .map(|t| t.1.clone())
397    }
398
399    fn reload_token(&self) -> Box<dyn ChangeToken> {
400        Box::new(self.token.read().unwrap().clone())
401    }
402
403    fn child_keys(&self, earlier_keys: &mut Vec<String>, parent_path: Option<&str>) {
404        let data = self.data.read().unwrap();
405        accumulate_child_keys(&data, earlier_keys, parent_path)
406    }
407}
408
409/// Represents a [`ConfigurationProvider`](crate::ConfigurationProvider) for `*.xml` files.
410pub struct XmlConfigurationProvider {
411    inner: Arc<InnerProvider>,
412    _subscription: Option<Box<dyn Subscription>>,
413}
414
415impl XmlConfigurationProvider {
416    /// Initializes a new `*.xml` file configuration provider.
417    ///
418    /// # Arguments
419    ///
420    /// * `file` - The `*.xml` [`FileSource`](crate::FileSource) information
421    pub fn new(file: FileSource) -> Self {
422        let path = file.path.clone();
423        let inner = Arc::new(InnerProvider::new(file));
424        let subscription: Option<Box<dyn Subscription>> = if inner.file.reload_on_change {
425            Some(Box::new(tokens::on_change(
426                move || FileChangeToken::new(path.clone()),
427                |state| {
428                    let provider = state.unwrap();
429                    std::thread::sleep(provider.file.reload_delay);
430                    provider.load(true).ok();
431                },
432                Some(inner.clone()),
433            )))
434        } else {
435            None
436        };
437
438        Self {
439            inner,
440            _subscription: subscription,
441        }
442    }
443}
444
445impl ConfigurationProvider for XmlConfigurationProvider {
446    fn get(&self, key: &str) -> Option<Value> {
447        self.inner.get(key)
448    }
449
450    fn reload_token(&self) -> Box<dyn ChangeToken> {
451        self.inner.reload_token()
452    }
453
454    fn load(&mut self) -> LoadResult {
455        self.inner.load(false)
456    }
457
458    fn child_keys(&self, earlier_keys: &mut Vec<String>, parent_path: Option<&str>) {
459        self.inner.child_keys(earlier_keys, parent_path)
460    }
461}
462
463/// Represents a [`ConfigurationSource`](crate::ConfigurationSource) for `*.xml` files.
464pub struct XmlConfigurationSource {
465    file: FileSource,
466}
467
468impl XmlConfigurationSource {
469    /// Initializes a new `*.xml` file configuration source.
470    ///
471    /// # Arguments
472    ///
473    /// * `file` - The `*.xml` [`FileSource`](crate::FileSource) information
474    pub fn new(file: FileSource) -> Self {
475        Self { file }
476    }
477}
478
479impl ConfigurationSource for XmlConfigurationSource {
480    fn build(&self, _builder: &dyn ConfigurationBuilder) -> Box<dyn ConfigurationProvider> {
481        Box::new(XmlConfigurationProvider::new(self.file.clone()))
482    }
483}
484
485pub mod ext {
486
487    use super::*;
488
489    /// Defines extension methods for [`ConfigurationBuilder`](crate::ConfigurationBuilder).
490    pub trait XmlConfigurationExtensions {
491        /// Adds a `*.xml` file as a configuration source.
492        ///
493        /// # Arguments
494        ///
495        /// * `file` - The `*.xml` [`FileSource`](crate::FileSource) information
496        fn add_xml_file<T: Into<FileSource>>(&mut self, file: T) -> &mut Self;
497    }
498
499    impl XmlConfigurationExtensions for dyn ConfigurationBuilder + '_ {
500        fn add_xml_file<T: Into<FileSource>>(&mut self, file: T) -> &mut Self {
501            self.add(Box::new(XmlConfigurationSource::new(file.into())));
502            self
503        }
504    }
505
506    impl<T: ConfigurationBuilder> XmlConfigurationExtensions for T {
507        fn add_xml_file<F: Into<FileSource>>(&mut self, file: F) -> &mut Self {
508            self.add(Box::new(XmlConfigurationSource::new(file.into())));
509            self
510        }
511    }
512}