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
274pub struct Provider(FileSource);
276
277impl Provider {
278 #[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}