mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: XML output using a dsl to describe the DOM
This commit is contained in:
parent
8b889ad8ec
commit
b91fe1550b
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -244,6 +244,7 @@ dependencies = [
|
|||||||
"serde_yaml 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_yaml 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"simple-error 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"simple-error 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -285,6 +286,11 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xml-rs"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -333,4 +339,5 @@ dependencies = [
|
|||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
|
||||||
"checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"
|
"checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"
|
||||||
|
@ -17,6 +17,7 @@ serde_json = "~1.0.9"
|
|||||||
simple-error = "0.1"
|
simple-error = "0.1"
|
||||||
serde_yaml = "~0.8.1"
|
serde_yaml = "~0.8.1"
|
||||||
toml = "~0.4.8"
|
toml = "~0.4.8"
|
||||||
|
xml-rs = "0.8.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bencher = "~0.1.5"
|
bencher = "~0.1.5"
|
||||||
|
@ -19,6 +19,7 @@ pub mod flags;
|
|||||||
pub mod json;
|
pub mod json;
|
||||||
pub mod toml;
|
pub mod toml;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
pub mod xml;
|
||||||
pub mod yaml;
|
pub mod yaml;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -49,6 +50,7 @@ impl ConverterRegistry {
|
|||||||
registry.register("exec", Box::new(exec::ExecConverter::new()));
|
registry.register("exec", Box::new(exec::ExecConverter::new()));
|
||||||
registry.register("yaml", Box::new(yaml::YamlConverter::new()));
|
registry.register("yaml", Box::new(yaml::YamlConverter::new()));
|
||||||
registry.register("toml", Box::new(toml::TomlConverter::new()));
|
registry.register("toml", Box::new(toml::TomlConverter::new()));
|
||||||
|
registry.register("xml", Box::new(xml::XmlConverter {}));
|
||||||
registry
|
registry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
208
src/convert/xml.rs
Normal file
208
src/convert/xml.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use std;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::traits::{Converter, Result};
|
||||||
|
use crate::ast::{Position, PositionedItem};
|
||||||
|
use crate::build::Val;
|
||||||
|
use crate::error::BuildError;
|
||||||
|
use crate::error::ErrorType;
|
||||||
|
|
||||||
|
use xml::common::XmlVersion;
|
||||||
|
use xml::writer::events::XmlEvent;
|
||||||
|
use xml::writer::EventWriter;
|
||||||
|
use xml::EmitterConfig;
|
||||||
|
|
||||||
|
pub struct XmlConverter {}
|
||||||
|
|
||||||
|
impl XmlConverter {
|
||||||
|
fn get_str_val(v: &Val) -> std::result::Result<&str, Box<Error>> {
|
||||||
|
if let Val::Str(ref s) = v {
|
||||||
|
Ok(s)
|
||||||
|
} else {
|
||||||
|
Err(Box::new(BuildError::new(
|
||||||
|
"Not a String value",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tuple_val(
|
||||||
|
v: &Val,
|
||||||
|
) -> std::result::Result<&Vec<(PositionedItem<String>, Rc<Val>)>, Box<Error>> {
|
||||||
|
if let Val::Tuple(ref fs) = v {
|
||||||
|
Ok(fs)
|
||||||
|
} else {
|
||||||
|
Err(Box::new(BuildError::new(
|
||||||
|
"Not a tuple value",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_list_val(v: &Val) -> std::result::Result<&Vec<Rc<Val>>, Box<Error>> {
|
||||||
|
if let Val::List(ref fs) = v {
|
||||||
|
Ok(fs)
|
||||||
|
} else {
|
||||||
|
Err(Box::new(BuildError::new(
|
||||||
|
"Not a List value",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_node<W: std::io::Write>(&self, v: &Val, w: &mut EventWriter<W>) -> Result {
|
||||||
|
// First we determine if this is a tag or text node
|
||||||
|
if let Val::Tuple(ref fs) = v {
|
||||||
|
let mut name: Option<&str> = None;
|
||||||
|
// TODO let mut namespace: Option<&str> = None;
|
||||||
|
let mut attrs: Option<&Vec<(PositionedItem<String>, Rc<Val>)>> = None;
|
||||||
|
let mut children: Option<&Vec<Rc<Val>>> = None;
|
||||||
|
let mut text: Option<&str> = None;
|
||||||
|
for (ref field, ref val) in fs.iter() {
|
||||||
|
if field.val == "name" {
|
||||||
|
name = Some(Self::get_str_val(val.as_ref())?);
|
||||||
|
}
|
||||||
|
//if field.val == "namespace" {
|
||||||
|
// namespace = Some(Self::get_str_val(val.as_ref())?);
|
||||||
|
//}
|
||||||
|
if field.val == "attrs" {
|
||||||
|
// This should be a tuple.
|
||||||
|
attrs = Some(Self::get_tuple_val(val.as_ref())?);
|
||||||
|
}
|
||||||
|
if field.val == "children" {
|
||||||
|
// This should be a list of tuples.
|
||||||
|
children = Some(Self::get_list_val(val.as_ref())?);
|
||||||
|
}
|
||||||
|
if field.val == "text" {
|
||||||
|
text = Some(Self::get_str_val(val.as_ref())?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name.is_some() && text.is_some() {
|
||||||
|
return Err(Box::new(BuildError::new(
|
||||||
|
"XML nodes can not have both text and name fields",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if name.is_some() {
|
||||||
|
let mut start = XmlEvent::start_element(name.unwrap());
|
||||||
|
if attrs.is_some() {
|
||||||
|
for (ref name, ref val) in attrs.unwrap().iter() {
|
||||||
|
start = start.attr(name.val.as_ref(), Self::get_str_val(val.as_ref())?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.write(start)?;
|
||||||
|
if children.is_some() {
|
||||||
|
for child in children.unwrap().iter() {
|
||||||
|
self.write_node(child.as_ref(), w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.write(XmlEvent::end_element())?;
|
||||||
|
}
|
||||||
|
if text.is_some() {
|
||||||
|
w.write(XmlEvent::characters(text.unwrap()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Val::Str(ref s) = v {
|
||||||
|
w.write(XmlEvent::characters(s.as_ref()))?;
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(BuildError::new(
|
||||||
|
"XML nodes must be a Tuple or a string",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, v: &Val, w: &mut Write) -> Result {
|
||||||
|
if let Val::Tuple(ref fs) = v {
|
||||||
|
let mut version: Option<&str> = None;
|
||||||
|
let mut encoding: Option<&str> = None;
|
||||||
|
let mut standalone: Option<bool> = None;
|
||||||
|
let mut root: Option<Rc<Val>> = None;
|
||||||
|
for &(ref name, ref val) in fs.iter() {
|
||||||
|
if name.val == "versin" {
|
||||||
|
version = Some(Self::get_str_val(val)?);
|
||||||
|
}
|
||||||
|
if name.val == "encoding" {
|
||||||
|
encoding = Some(Self::get_str_val(val)?);
|
||||||
|
}
|
||||||
|
if name.val == "standalone" {
|
||||||
|
standalone = match val.as_ref() {
|
||||||
|
Val::Boolean(b) => Some(*b),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if name.val == "root" {
|
||||||
|
root = Some(val.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match root {
|
||||||
|
Some(n) => {
|
||||||
|
let mut writer = EmitterConfig::new()
|
||||||
|
.perform_indent(true)
|
||||||
|
.normalize_empty_elements(false)
|
||||||
|
.create_writer(w);
|
||||||
|
// first we see if we need to emit a document
|
||||||
|
// declaration event.
|
||||||
|
let version = match version {
|
||||||
|
Some(s) => {
|
||||||
|
if s == "1.0" {
|
||||||
|
Some(XmlVersion::Version10)
|
||||||
|
} else if s == "1.1" {
|
||||||
|
Some(XmlVersion::Version11)
|
||||||
|
} else {
|
||||||
|
// If they specified the wrong version then
|
||||||
|
// error out.
|
||||||
|
return Err(Box::new(BuildError::new(
|
||||||
|
"XML version must be either 1.0 or 1.1",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
writer.write(XmlEvent::StartDocument {
|
||||||
|
// We default to version 1.1 documents if not specified.
|
||||||
|
version: version.unwrap_or(XmlVersion::Version10),
|
||||||
|
encoding: encoding,
|
||||||
|
standalone: standalone,
|
||||||
|
})?;
|
||||||
|
self.write_node(n.as_ref(), &mut writer)
|
||||||
|
}
|
||||||
|
None => Err(Box::new(BuildError::new(
|
||||||
|
"XML doc tuples must have a root field",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Box::new(BuildError::new(
|
||||||
|
"XML outputs must be a Tuple",
|
||||||
|
ErrorType::TypeFail,
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Converter for XmlConverter {
|
||||||
|
fn convert(&self, v: Rc<Val>, mut w: &mut Write) -> Result {
|
||||||
|
self.write(&v, &mut w)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_ext(&self) -> String {
|
||||||
|
String::from("xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
String::from("Convert a ucg DSL into xml.")
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ extern crate serde_json;
|
|||||||
extern crate serde_yaml;
|
extern crate serde_yaml;
|
||||||
extern crate simple_error;
|
extern crate simple_error;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
|
extern crate xml;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user