Merge branch 'use_btreemap_for_attributes' into 'master'

Use a BTreeMap<String, String> instead of a Vec<Attribute> to store attributes

Closes #4

See merge request !4
This commit is contained in:
lumi 2017-04-30 14:00:05 +00:00
commit 1b1e2fa86a
4 changed files with 29 additions and 95 deletions

View file

@ -1,45 +0,0 @@
//! Provides an `Attribute` type which represents an attribute in an XML document.
use xml::escape::escape_str_attribute;
use std::fmt;
/// An attribute of a DOM element.
///
/// This is of the form: `name`="`value`"
///
/// This does not support prefixed/namespaced attributes yet.
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub struct Attribute {
/// The name of the attribute.
pub name: String,
/// The value of the attribute.
pub value: String,
}
impl fmt::Display for Attribute {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}=\"{}\"", self.name, escape_str_attribute(&self.value))
}
}
impl Attribute {
/// Construct a new attribute from the given `name` and `value`.
///
/// # Examples
///
/// ```
/// use minidom::Attribute;
///
/// let attr = Attribute::new("name", "value");
///
/// assert_eq!(attr.name, "name");
/// assert_eq!(attr.value, "value");
/// ```
pub fn new<N: Into<String>, V: Into<String>>(name: N, value: V) -> Attribute {
Attribute {
name: name.into(),
value: value.into(),
}
}
}

View file

@ -2,13 +2,13 @@
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use std::collections::BTreeMap;
use std::iter::FromIterator;
use std::fmt; use std::fmt;
use error::Error; use error::Error;
use attribute::Attribute;
use xml::reader::{XmlEvent as ReaderEvent, EventReader}; use xml::reader::{XmlEvent as ReaderEvent, EventReader};
use xml::writer::{XmlEvent as WriterEvent, EventWriter}; use xml::writer::{XmlEvent as WriterEvent, EventWriter};
use xml::name::Name; use xml::name::Name;
@ -20,28 +20,15 @@ use std::slice;
use convert::{IntoElements, IntoAttributeValue, ElementEmitter}; use convert::{IntoElements, IntoAttributeValue, ElementEmitter};
#[derive(Clone, Eq)] #[derive(Clone, PartialEq, Eq)]
/// A struct representing a DOM Element. /// A struct representing a DOM Element.
pub struct Element { pub struct Element {
name: String, name: String,
namespace: Option<String>, namespace: Option<String>,
attributes: Vec<Attribute>, attributes: BTreeMap<String, String>,
children: Vec<Node>, children: Vec<Node>,
} }
impl PartialEq for Element {
fn eq(&self, other: &Element) -> bool {
let mut my_attr = self.attributes.clone();
my_attr.sort();
let mut other_attr = other.attributes.clone();
other_attr.sort();
self.name == other.name &&
self.namespace == other.namespace &&
my_attr == other_attr &&
self.children == other.children
}
}
impl fmt::Debug for Element { impl fmt::Debug for Element {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
@ -50,7 +37,7 @@ impl fmt::Debug for Element {
write!(fmt, " xmlns=\"{}\"", ns)?; write!(fmt, " xmlns=\"{}\"", ns)?;
} }
for attr in &self.attributes { for attr in &self.attributes {
write!(fmt, " {}", attr)?; write!(fmt, " {}=\"{}\"", attr.0, attr.1)?;
} }
if self.children.is_empty() { if self.children.is_empty() {
write!(fmt, "/>")?; write!(fmt, "/>")?;
@ -92,7 +79,7 @@ pub enum Node {
} }
impl Element { impl Element {
fn new(name: String, namespace: Option<String>, attributes: Vec<Attribute>, children: Vec<Node>) -> Element { fn new(name: String, namespace: Option<String>, attributes: BTreeMap<String, String>, children: Vec<Node>) -> Element {
Element { Element {
name: name, name: name,
namespace: namespace, namespace: namespace,
@ -122,7 +109,7 @@ impl Element {
/// ``` /// ```
pub fn builder<S: Into<String>>(name: S) -> ElementBuilder { pub fn builder<S: Into<String>>(name: S) -> ElementBuilder {
ElementBuilder { ElementBuilder {
root: Element::new(name.into(), None, Vec::new(), Vec::new()), root: Element::new(name.into(), None, BTreeMap::new(), Vec::new()),
} }
} }
@ -144,7 +131,7 @@ impl Element {
Element { Element {
name: name.into(), name: name.into(),
namespace: None, namespace: None,
attributes: Vec::new(), attributes: BTreeMap::new(),
children: Vec::new(), children: Vec::new(),
} }
} }
@ -162,10 +149,8 @@ impl Element {
/// Returns a reference to the value of the given attribute, if it exists, else `None`. /// Returns a reference to the value of the given attribute, if it exists, else `None`.
pub fn attr(&self, name: &str) -> Option<&str> { pub fn attr(&self, name: &str) -> Option<&str> {
for attr in &self.attributes { if let Some(value) = self.attributes.get(name) {
if attr.name == name { return Some(&value)
return Some(&attr.value);
}
} }
None None
} }
@ -174,14 +159,14 @@ impl Element {
pub fn set_attr<S: Into<String>, V: IntoAttributeValue>(&mut self, name: S, val: V) { pub fn set_attr<S: Into<String>, V: IntoAttributeValue>(&mut self, name: S, val: V) {
let name = name.into(); let name = name.into();
let val = val.into_attribute_value(); let val = val.into_attribute_value();
for attr in &mut self.attributes {
if attr.name == name { if let Some(value) = self.attributes.get_mut(&name) {
attr.value = val.expect("removing existing value via set_attr, this is not yet supported (TODO)"); // TODO *value = val.expect("removing existing value via set_attr, this is not yet supported (TODO)"); // TODO
return; return;
}
} }
if let Some(val) = val { if let Some(val) = val {
self.attributes.push(Attribute::new(name, val)); self.attributes.insert(name, val);
} }
} }
@ -212,13 +197,11 @@ impl Element {
ReaderEvent::StartElement { name, attributes, namespace } => { ReaderEvent::StartElement { name, attributes, namespace } => {
let attributes = attributes.into_iter() let attributes = attributes.into_iter()
.map(|o| { .map(|o| {
Attribute::new( (match o.name.prefix {
match o.name.prefix { Some(prefix) => format!("{}:{}", prefix, o.name.local_name),
Some(prefix) => format!("{}:{}", prefix, o.name.local_name), None => o.name.local_name
None => o.name.local_name },
}, o.value)
o.value
)
}) })
.collect(); .collect();
let ns = if let Some(ref prefix) = name.prefix { let ns = if let Some(ref prefix) = name.prefix {
@ -247,13 +230,11 @@ impl Element {
ReaderEvent::StartElement { name, attributes, namespace } => { ReaderEvent::StartElement { name, attributes, namespace } => {
let attributes = attributes.into_iter() let attributes = attributes.into_iter()
.map(|o| { .map(|o| {
Attribute::new( (match o.name.prefix {
match o.name.prefix { Some(prefix) => format!("{}:{}", prefix, o.name.local_name),
Some(prefix) => format!("{}:{}", prefix, o.name.local_name), None => o.name.local_name
None => o.name.local_name },
}, o.value)
o.value
)
}) })
.collect(); .collect();
let ns = if let Some(ref prefix) = name.prefix { let ns = if let Some(ref prefix) = name.prefix {
@ -297,7 +278,7 @@ impl Element {
start = start.default_ns(ns.as_ref()); start = start.default_ns(ns.as_ref());
} }
for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently
start = start.attr(Name::local(&attr.name), &attr.value); start = start.attr(Name::local(&attr.0), &attr.1);
} }
writer.write(start)?; writer.write(start)?;
for child in &self.children { for child in &self.children {
@ -582,7 +563,7 @@ impl ElementBuilder {
fn test_element_new() { fn test_element_new() {
let elem = Element::new( "name".to_owned() let elem = Element::new( "name".to_owned()
, Some("namespace".to_owned()) , Some("namespace".to_owned())
, vec![ Attribute::new("name", "value") ] , BTreeMap::from_iter(vec![ ("name".to_string(), "value".to_string()) ].into_iter() )
, Vec::new() ); , Vec::new() );
assert_eq!(elem.name(), "name"); assert_eq!(elem.name(), "name");

View file

@ -67,13 +67,11 @@
extern crate xml; extern crate xml;
pub mod error; pub mod error;
pub mod attribute;
pub mod element; pub mod element;
pub mod convert; pub mod convert;
#[cfg(test)] mod tests; #[cfg(test)] mod tests;
pub use error::Error; pub use error::Error;
pub use attribute::Attribute;
pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder}; pub use element::{Element, Node, Children, ChildrenMut, ElementBuilder};
pub use convert::{IntoElements, IntoAttributeValue}; pub use convert::{IntoElements, IntoAttributeValue};

View file

@ -7,7 +7,7 @@ use xml::writer::EventWriter;
use element::Element; use element::Element;
const TEST_STRING: &'static str = r#"<?xml version="1.0" encoding="utf-8"?><root xmlns="root_ns" xml:lang="en" a="b">meow<child c="d" /><child xmlns="child_ns" d="e" xml:lang="fr" />nya</root>"#; const TEST_STRING: &'static str = r#"<?xml version="1.0" encoding="utf-8"?><root xmlns="root_ns" a="b" xml:lang="en">meow<child c="d" /><child xmlns="child_ns" d="e" xml:lang="fr" />nya</root>"#;
fn build_test_tree() -> Element { fn build_test_tree() -> Element {
let mut root = Element::builder("root") let mut root = Element::builder("root")