Skip to content

Commit cbd6fed

Browse files
committed
Refactoring parse/encode/types
Also added unproven feature to encoding
1 parent 61eff0c commit cbd6fed

26 files changed

+305
-248
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ name = "svd-parser"
88
repository = "https://github.com/japaric/svd"
99
version = "0.6.0"
1010

11+
[features]
12+
unproven = []
13+
1114
[dependencies]
1215
either = "1.1.0"
1316
xmltree = "0.3.2"

src/elementext.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,99 @@
1+
/// SVD Element Extensions
12
23
use xmltree::Element;
4+
5+
use types::{Parse, BoolParse};
36
use failure::ResultExt;
47

58
use error::*;
69

10+
pub trait ElementExt {
11+
fn get_child_text_opt<K>(&self, k: K) -> Result<Option<String>, SVDError>
12+
where
13+
String: PartialEq<K>;
14+
fn get_child_text<K>(&self, k: K) -> Result<String, SVDError>
15+
where
16+
String: PartialEq<K>,
17+
K: ::std::fmt::Display + Clone;
18+
19+
fn get_text(&self) -> Result<String, SVDError>;
20+
21+
fn get_child_elem<'a>(&'a self, n: &str) -> Result<&'a Element, SVDError>;
22+
fn get_child_u32(&self, n: &str) -> Result<u32, SVDError>;
23+
fn get_child_bool(&self, n: &str) -> Result<bool, SVDError>;
24+
25+
fn merge(&self, n: &Self) -> Self;
26+
27+
fn debug(&self);
28+
}
29+
30+
impl ElementExt for Element {
31+
fn get_child_text_opt<K>(&self, k: K) -> Result<Option<String>, SVDError>
32+
where
33+
String: PartialEq<K>,
34+
{
35+
if let Some(child) = self.get_child(k) {
36+
Ok(Some(child.get_text().map(|s| s.to_owned())?))
37+
} else {
38+
Ok(None)
39+
}
40+
}
41+
fn get_child_text<K>(&self, k: K) -> Result<String, SVDError>
42+
where
43+
String: PartialEq<K>,
44+
K: ::std::fmt::Display + Clone,
45+
{
46+
self.get_child_text_opt(k.clone())?.ok_or(SVDErrorKind::MissingTag(self.clone(), format!("{}", k)).into(),)
47+
}
48+
49+
/// Get text contained by an XML Element
50+
fn get_text(&self) -> Result<String, SVDError> {
51+
match self.text.as_ref() {
52+
Some(s) => Ok(s.clone()),
53+
// FIXME: Doesn't look good because SVDErrorKind doesn't format by itself. We already
54+
// capture the element and this information can be used for getting the name
55+
// This would fix ParseError
56+
None => {
57+
Err(SVDErrorKind::EmptyTag(self.clone(), self.name.clone())
58+
.into())
59+
}
60+
}
61+
}
62+
63+
/// Get a named child element from an XML Element
64+
fn get_child_elem<'a>(&'a self, n: &str) -> Result<&'a Element, SVDError> {
65+
match self.get_child(n) {
66+
Some(s) => Ok(s),
67+
None => Err(SVDErrorKind::MissingTag(self.clone(), n.to_string()).into()),
68+
}
69+
}
70+
71+
/// Get a u32 value from a named child element
72+
fn get_child_u32(&self, n: &str) -> Result<u32, SVDError> {
73+
let s = self.get_child_elem(n)?;
74+
u32::parse(&s).context(SVDErrorKind::ParseError(self.clone())).map_err(|e| e.into())
75+
}
76+
77+
/// Get a bool value from a named child element
78+
fn get_child_bool(&self, n: &str) -> Result<bool, SVDError> {
79+
let s = self.get_child_elem(n)?;
80+
BoolParse::parse(s)
81+
}
82+
83+
// Merges the children of two elements, maintaining the name and description of the first
84+
fn merge(&self, r: &Self) -> Self {
85+
let mut n = self.clone();
86+
for c in &r.children {
87+
n.children.push(c.clone());
88+
}
89+
n
90+
}
791

92+
fn debug(&self) {
93+
println!("<{}>", self.name);
94+
for c in &self.children {
95+
println!("{}: {:?}", c.name, c.text)
96+
}
97+
println!("</{}>", self.name);
98+
}
99+
}

src/encode.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
use xmltree::Element;
3+
4+
/// Encode trait allows SVD objects to be encoded into XML elements.
5+
pub trait Encode {
6+
/// Encoding error
7+
type Error;
8+
/// Encode into an XML/SVD element
9+
fn encode(&self) -> Result<Element, Self::Error>;
10+
}
11+
12+
/// EncodeChildren allows SVD objects to be encoded as a list of XML elements
13+
/// This is typically used to merge with an existing element.
14+
pub trait EncodeChildren {
15+
/// Encoding error
16+
type Error;
17+
/// Encode into XML/SVD children to merge with existing object
18+
fn encode(&self) -> Result<Vec<Element>, Self::Error>;
19+
}

src/lib.rs

Lines changed: 50 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -28,116 +28,43 @@ extern crate either;
2828
extern crate xmltree;
2929
#[macro_use]
3030
extern crate failure;
31-
use failure::ResultExt;
31+
32+
use std::collections::HashMap;
3233

3334
use xmltree::Element;
3435

36+
// ElementExt extends XML elements with useful methods
37+
pub mod elementext;
38+
// SVD contains svd primitives
3539
pub mod svd;
3640
use svd::device::Device;
41+
// Error defines SVD error types
3742
pub mod error;
38-
use error::{SVDError, SVDErrorKind};
43+
use error::{SVDError};
3944

4045
pub mod parse;
46+
use parse::Parse;
47+
48+
pub mod encode;
49+
#[cfg(feature = "unproven")]
50+
use encode::Encode;
51+
4152
pub mod types;
42-
use parse::BoolParse;
43-
use types::Parse;
4453

45-
/// Parses the contents of a SVD file (XML)
54+
/// Parses the contents of an SVD (XML) string
4655
pub fn parse(xml: &str) -> Result<Device, SVDError> {
4756
let xml = trim_utf8_bom(xml);
4857
let tree = Element::parse(xml.as_bytes())?;
4958
Device::parse(&tree)
5059
}
5160

52-
trait ElementExt {
53-
fn get_child_text_opt<K>(&self, k: K) -> Result<Option<String>, SVDError>
54-
where
55-
String: PartialEq<K>;
56-
fn get_child_text<K>(&self, k: K) -> Result<String, SVDError>
57-
where
58-
String: PartialEq<K>,
59-
K: ::std::fmt::Display + Clone;
60-
61-
fn get_text(&self) -> Result<String, SVDError>;
62-
63-
fn get_child_elem<'a>(&'a self, n: &str) -> Result<&'a Element, SVDError>;
64-
fn get_child_u32(&self, n: &str) -> Result<u32, SVDError>;
65-
fn get_child_bool(&self, n: &str) -> Result<bool, SVDError>;
66-
67-
fn merge(&self, n: &Self) -> Self;
68-
69-
fn debug(&self);
70-
}
71-
72-
impl ElementExt for Element {
73-
fn get_child_text_opt<K>(&self, k: K) -> Result<Option<String>, SVDError>
74-
where
75-
String: PartialEq<K>,
76-
{
77-
if let Some(child) = self.get_child(k) {
78-
Ok(Some(child.get_text().map(|s| s.to_owned())?))
79-
} else {
80-
Ok(None)
81-
}
82-
}
83-
fn get_child_text<K>(&self, k: K) -> Result<String, SVDError>
84-
where
85-
String: PartialEq<K>,
86-
K: ::std::fmt::Display + Clone,
87-
{
88-
self.get_child_text_opt(k.clone())?.ok_or(SVDErrorKind::MissingTag(self.clone(), format!("{}", k)).into(),)
89-
}
90-
91-
/// Get text contained by an XML Element
92-
fn get_text(&self) -> Result<String, SVDError> {
93-
match self.text.as_ref() {
94-
Some(s) => Ok(s.clone()),
95-
// FIXME: Doesn't look good because SVDErrorKind doesn't format by itself. We already
96-
// capture the element and this information can be used for getting the name
97-
// This would fix ParseError
98-
None => {
99-
Err(SVDErrorKind::EmptyTag(self.clone(), self.name.clone())
100-
.into())
101-
}
102-
}
103-
}
104-
105-
/// Get a named child element from an XML Element
106-
fn get_child_elem<'a>(&'a self, n: &str) -> Result<&'a Element, SVDError> {
107-
match self.get_child(n) {
108-
Some(s) => Ok(s),
109-
None => Err(SVDErrorKind::MissingTag(self.clone(), n.to_string()).into()),
110-
}
111-
}
112-
113-
/// Get a u32 value from a named child element
114-
fn get_child_u32(&self, n: &str) -> Result<u32, SVDError> {
115-
let s = self.get_child_elem(n)?;
116-
u32::parse(&s).context(SVDErrorKind::ParseError(self.clone())).map_err(|e| e.into())
117-
}
118-
119-
/// Get a bool value from a named child element
120-
fn get_child_bool(&self, n: &str) -> Result<bool, SVDError> {
121-
let s = self.get_child_elem(n)?;
122-
BoolParse::parse(s)
123-
}
124-
125-
// Merges the children of two elements, maintaining the name and description of the first
126-
fn merge(&self, r: &Self) -> Self {
127-
let mut n = self.clone();
128-
for c in &r.children {
129-
n.children.push(c.clone());
130-
}
131-
n
132-
}
133-
134-
fn debug(&self) {
135-
println!("<{}>", self.name);
136-
for c in &self.children {
137-
println!("{}: {:?}", c.name, c.text)
138-
}
139-
println!("</{}>", self.name);
140-
}
61+
/// Encodes a device object to an SVD (XML) string
62+
#[cfg(feature = "unproven")]
63+
pub fn encode(d: &Device) -> Result<String, SVDError> {
64+
let root = d.encode()?;
65+
let mut wr = Vec::new();
66+
root.write(&mut wr);
67+
Ok(String::from_utf8(wr).unwrap())
14168
}
14269

14370
/// Return the &str trimmed UTF-8 BOM if the input &str contains the BOM.
@@ -149,6 +76,35 @@ fn trim_utf8_bom(s: &str) -> &str {
14976
}
15077
}
15178

79+
/// Helper to create new base xml elements
80+
pub (crate) fn new_element(name: &str, text: Option<String>) -> Element {
81+
Element {
82+
name: String::from(name),
83+
attributes: HashMap::new(),
84+
children: Vec::new(),
85+
text: text,
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
use std::fmt::Debug;
91+
#[cfg(test)]
92+
use types::{Encode};
93+
94+
/// Generic test helper function
95+
/// Takes an array of (item, xml) pairs where the item implements
96+
/// Parse and Encode and tests object encoding and decoding
97+
#[cfg(test)]
98+
pub fn run_test<T: Parse<Error=SVDError, Object=T> + Encode<Error=SVDError> + Debug + PartialEq>(tests: &[(T, &str)]) {
99+
for t in tests {
100+
let tree1 = Element::parse(t.1.as_bytes()).unwrap();
101+
let elem = T::parse(&tree1).unwrap();
102+
assert_eq!(elem, t.0, "Error parsing xml` (mismatch between parsed and expected)");
103+
let tree2 = elem.encode().unwrap();
104+
assert_eq!(tree1, tree2, "Error encoding xml (mismatch between encoded and original)");
105+
};
106+
}
107+
152108
#[cfg(test)]
153109
mod tests {
154110
use super::*;

src/parse.rs

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,17 @@
11
use xmltree::Element;
22

33
use error::*;
4-
use types::Parse;
5-
use ElementExt;
64

7-
macro_rules! try {
8-
($e:expr) => {
9-
$e.expect(concat!(file!(), ":", line!(), " ", stringify!($e)))
10-
}
11-
}
12-
13-
pub struct BoolParse;
14-
15-
impl Parse for BoolParse {
16-
type Object = bool;
17-
type Error = SVDError;
18-
fn parse(tree: &Element) -> Result<bool, SVDError> {
19-
let text = try!(tree.text.as_ref());
20-
Ok(match text.as_ref() {
21-
"0" => false,
22-
"1" => true,
23-
_ => match text.parse() {
24-
Ok(b) => b,
25-
Err(e) => {
26-
return Err(SVDErrorKind::InvalidBooleanValue(
27-
tree.clone(),
28-
text.clone(),
29-
e,
30-
).into())
31-
}
32-
},
33-
})
34-
}
35-
}
36-
37-
pub struct DimIndex;
38-
39-
impl Parse for DimIndex {
40-
type Object = Vec<String>;
41-
type Error = SVDError;
42-
43-
fn parse(tree: &Element) -> Result<Vec<String>, SVDError> {
44-
let text = tree.get_text()?;
45-
if text.contains('-') {
46-
let mut parts = text.splitn(2, '-');
47-
let start = try!(try!(parts.next()).parse::<u32>());
48-
let end = try!(try!(parts.next()).parse::<u32>()) + 1;
49-
50-
Ok((start..end).map(|i| i.to_string()).collect())
51-
} else if text.contains(',') {
52-
Ok(text.split(',').map(|s| s.to_string()).collect())
53-
} else {
54-
unreachable!()
55-
}
56-
}
5+
/// Parse trait allows SVD objects to be parsed from XML elements.
6+
pub trait Parse {
7+
/// Object returned by parse method
8+
type Object;
9+
/// Parsing error
10+
type Error;
11+
/// Parse an XML/SVD element into it's corresponding `Object`.
12+
fn parse(&Element) -> Result<Self::Object, Self::Error>;
5713
}
5814

59-
//TODO: encode for DimIndex
60-
6115

6216
/// Parses an optional child element with the provided name and Parse function
6317
/// Returns an none if the child doesn't exist, Ok(Some(e)) if parsing succeeds,

0 commit comments

Comments
 (0)