1use super::types::Tag;
2use super::types::TagError;
3use super::types::TagEvent;
4use super::types::TagParser;
5
6use std::collections::HashMap;
7use std::error::Error;
8use std::fs::File;
9use std::io::{BufReader, Read};
10use xml::attribute::OwnedAttribute;
11use xml::reader::{EventReader, Events, XmlEvent};
12
13pub struct XmlTagParser {
14 tag_parser: Events<BufReader<Box<dyn Read>>>,
15}
16
17impl Iterator for XmlTagParser {
18 type Item = Result<TagEvent, TagError>;
19
20 fn next(&mut self) -> Option<Result<TagEvent, TagError>> {
21 loop {
22 match self.tag_parser.next() {
23 Some(Ok(XmlEvent::StartElement {
24 name, attributes, ..
25 }))
26 if Self::is_supported_tag(&name.local_name) =>
27 {
28 return Some(Ok(Self::build_open_tag_event(name.local_name, &attributes)))
29 }
30 Some(Ok(XmlEvent::EndElement { name })) if Self::is_supported_tag(&name.local_name) => {
31 return Some(Ok(Self::build_close_tag_event(name.local_name)))
32 }
33 Some(Err(e)) => return Some(Err(TagError::new(e.msg()))),
34 None => return None,
35 _ => continue,
36 }
37 }
38 }
39}
40
41impl TagParser for XmlTagParser {}
42
43impl XmlTagParser {
44 pub fn new(reader: Box<dyn Read>) -> Self {
45 let file = BufReader::new(reader);
46 let tag_parser = EventReader::new(file);
47 let tag_parser = tag_parser.into_iter();
48 XmlTagParser { tag_parser }
49 }
50
51 pub fn from_file(path: String) -> Result<Self, Box<dyn Error>> {
52 let file = Box::new(File::open(path)?) as Box<dyn Read>;
53 Ok(Self::new(file))
54 }
55
56 fn build_open_tag_event(name: String, attributes: &[OwnedAttribute]) -> TagEvent {
57 let tag = Self::build_tag(name);
58 let attributes = Self::parse_attributes(attributes);
59 TagEvent::Open { tag, attributes }
60 }
61
62 fn parse_attributes(attributes: &[OwnedAttribute]) -> HashMap<String, String> {
63 let mut attributes_map = HashMap::new();
64 for attribute in attributes.iter() {
65 let key = attribute.name.local_name.clone();
66 let value = attribute.value.clone();
67 attributes_map.insert(key, value);
68 }
69 attributes_map
70 }
71
72 fn build_close_tag_event(name: String) -> TagEvent {
73 let tag = Self::build_tag(name);
74 TagEvent::Close { tag }
75 }
76
77 fn build_tag(name: String) -> Tag {
78 match name.as_str() {
79 "Schema" => Tag::Schema,
80 "EntityType" => Tag::EntityType,
81 "Property" => Tag::Property,
82 "NavigationProperty" => Tag::NavigationProperty,
83 _ => Tag::PropertyRef,
84 }
85 }
86
87 fn is_supported_tag(name: &str) -> bool {
88 name == "Schema"
89 || name == "EntityType"
90 || name == "Property"
91 || name == "PropertyRef"
92 || name == "NavigationProperty"
93 }
94}