ucglib/convert/
xml.rs

1// Copyright 2018 Jeremy Wall <jeremy@marzhillstudios.com>
2//
3//  Licensed under the Apache License, Version 2.0 (the "License");
4//  you may not use this file except in compliance with the License.
5//  You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14
15use std;
16use std::error::Error;
17use std::io::Write;
18use std::rc::Rc;
19
20use super::traits::{ConvertResult, Converter};
21use crate::build::Val;
22use crate::error::BuildError;
23use crate::error::ErrorType;
24
25use xml::common::XmlVersion;
26use xml::writer::events::XmlEvent;
27use xml::writer::EventWriter;
28use xml::EmitterConfig;
29
30pub struct XmlConverter {}
31
32impl XmlConverter {
33    fn get_str_val(v: &Val) -> std::result::Result<&str, Box<dyn Error>> {
34        if let Val::Str(ref s) = v {
35            Ok(s)
36        } else {
37            Err(BuildError::new("Not a String value", ErrorType::TypeFail).to_boxed())
38        }
39    }
40
41    fn get_tuple_val(v: &Val) -> std::result::Result<&Vec<(String, Rc<Val>)>, Box<dyn Error>> {
42        if let Val::Tuple(ref fs) = v {
43            Ok(fs)
44        } else {
45            Err(BuildError::new("Not a tuple value", ErrorType::TypeFail).to_boxed())
46        }
47    }
48
49    fn get_list_val(v: &Val) -> std::result::Result<&Vec<Rc<Val>>, Box<dyn Error>> {
50        if let Val::List(ref fs) = v {
51            Ok(fs)
52        } else {
53            Err(BuildError::new("Not a List value", ErrorType::TypeFail).to_boxed())
54        }
55    }
56
57    fn write_node<W: std::io::Write>(&self, v: &Val, w: &mut EventWriter<W>) -> ConvertResult {
58        // First we determine if this is a tag or text node
59        if let Val::Tuple(ref fs) = v {
60            let mut name: Option<&str> = None;
61            let mut attrs: Option<&Vec<(String, Rc<Val>)>> = None;
62            let mut children: Option<&Vec<Rc<Val>>> = None;
63            let mut text: Option<&str> = None;
64            let mut ns: Option<(&str, &str)> = None;
65            for (ref field, ref val) in fs.iter() {
66                if field == "name" {
67                    name = Some(Self::get_str_val(val.as_ref())?);
68                }
69                if field == "ns" {
70                    if let Val::Tuple(ref fs) = val.as_ref() {
71                        let mut prefix = "";
72                        let mut uri = "";
73                        for (ref name, ref val) in fs.iter() {
74                            if val.is_empty() {
75                                continue;
76                            }
77                            if name == "uri" {
78                                uri = Self::get_str_val(val.as_ref())?;
79                            }
80                            if name == "prefix" {
81                                prefix = Self::get_str_val(val.as_ref())?;
82                            }
83                        }
84                        if uri != "" && prefix != "" {
85                            ns = Some((prefix, uri));
86                        }
87                    } else if let Val::Str(ref s) = val.as_ref() {
88                        ns = Some(("", s));
89                    }
90                }
91                if field == "attrs" {
92                    // This should be a tuple.
93                    if !val.is_empty() {
94                        attrs = Some(Self::get_tuple_val(val.as_ref())?);
95                    }
96                }
97                if field == "children" {
98                    // This should be a list of tuples.
99                    if !val.is_empty() {
100                        children = Some(Self::get_list_val(val.as_ref())?);
101                    }
102                }
103                if field == "text" {
104                    if !val.is_empty() {
105                        text = Some(Self::get_str_val(val.as_ref())?);
106                    }
107                }
108            }
109            if name.is_some() && text.is_some() {
110                return Err(BuildError::new(
111                    "XML nodes can not have both text and name fields",
112                    ErrorType::TypeFail,
113                )
114                .to_boxed());
115            }
116            if name.is_some() {
117                let mut start = XmlEvent::start_element(name.unwrap());
118                if attrs.is_some() {
119                    for (ref name, ref val) in attrs.unwrap().iter() {
120                        if val.is_empty() {
121                            continue;
122                        }
123                        start = start.attr(name.as_ref(), Self::get_str_val(val.as_ref())?);
124                    }
125                }
126                if let Some((prefix, uri)) = ns {
127                    if prefix == "" {
128                        start = start.default_ns(uri);
129                    } else {
130                        start = start.ns(prefix, uri);
131                    }
132                }
133                w.write(start)?;
134                if children.is_some() {
135                    for child in children.unwrap().iter() {
136                        self.write_node(child.as_ref(), w)?;
137                    }
138                }
139                w.write(XmlEvent::end_element())?;
140            }
141            if text.is_some() {
142                w.write(XmlEvent::characters(text.unwrap()))?;
143            }
144        } else if let Val::Str(ref s) = v {
145            w.write(XmlEvent::characters(s.as_ref()))?;
146        } else {
147            return Err(BuildError::new(
148                "XML nodes must be a Tuple or a string",
149                ErrorType::TypeFail,
150            )
151            .to_boxed());
152        }
153        Ok(())
154    }
155
156    fn write(&self, v: &Val, w: &mut dyn Write) -> ConvertResult {
157        if let Val::Tuple(ref fs) = v {
158            let mut version: Option<&str> = None;
159            let mut encoding: Option<&str> = None;
160            let mut standalone: Option<bool> = None;
161            let mut root: Option<Rc<Val>> = None;
162            for &(ref name, ref val) in fs.iter() {
163                if name == "version" {
164                    version = Some(Self::get_str_val(val)?);
165                }
166                if name == "encoding" {
167                    encoding = Some(Self::get_str_val(val)?);
168                }
169                if name == "standalone" {
170                    standalone = match val.as_ref() {
171                        Val::Boolean(b) => Some(*b),
172                        _ => None,
173                    };
174                }
175                if name == "root" {
176                    root = Some(val.clone());
177                }
178            }
179            match root {
180                Some(n) => {
181                    let mut writer = EmitterConfig::new()
182                        .perform_indent(true)
183                        .normalize_empty_elements(false)
184                        .create_writer(w);
185                    // first we see if we need to emit a document
186                    // declaration event.
187                    let version = match version {
188                        Some(s) => {
189                            if s == "1.0" {
190                                Some(XmlVersion::Version10)
191                            } else if s == "1.1" {
192                                Some(XmlVersion::Version11)
193                            } else {
194                                // If they specified the wrong version then
195                                // error out.
196                                return Err(BuildError::new(
197                                    "XML version must be either 1.0 or 1.1",
198                                    ErrorType::TypeFail,
199                                )
200                                .to_boxed());
201                            }
202                        }
203                        None => None,
204                    };
205                    writer.write(XmlEvent::StartDocument {
206                        // We default to version 1.1 documents if not specified.
207                        version: version.unwrap_or(XmlVersion::Version10),
208                        encoding: encoding,
209                        standalone: standalone,
210                    })?;
211                    self.write_node(n.as_ref(), &mut writer)
212                }
213                None => Err(BuildError::new(
214                    "XML doc tuples must have a root field",
215                    ErrorType::TypeFail,
216                )
217                .to_boxed()),
218            }
219        } else {
220            Err(BuildError::new("XML outputs must be a Tuple", ErrorType::TypeFail).to_boxed())
221        }
222    }
223}
224
225impl Converter for XmlConverter {
226    fn convert(&self, v: Rc<Val>, mut w: &mut dyn Write) -> ConvertResult {
227        self.write(&v, &mut w)
228    }
229
230    fn file_ext(&self) -> String {
231        String::from("xml")
232    }
233
234    fn description(&self) -> String {
235        String::from("Convert a ucg DSL into xml.")
236    }
237
238    #[allow(unused_must_use)]
239    fn help(&self) -> String {
240        include_str!("xml_help.txt").to_string()
241    }
242}