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