-
Notifications
You must be signed in to change notification settings - Fork 236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added XmlWriter and Element::create API #2
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,7 @@ pub mod attributes; | |
mod test; | ||
|
||
use std::fs::File; | ||
use std::io::{self, BufRead, BufReader}; | ||
use std::io::{self, BufRead, BufReader, Write}; | ||
use std::iter::Iterator; | ||
use std::path::Path; | ||
use std::fmt; | ||
|
@@ -303,6 +303,25 @@ impl Element { | |
Attributes::new(&self.buf[self.start..self.end], self.name_end) | ||
} | ||
|
||
/// Creates a new Element from the given name and attributes. | ||
/// attributes are represented as an iterator over (key, value) tuples where | ||
/// key and value are both &str. | ||
pub fn create<'a, I: Iterator<Item = (&'a str, &'a str)>>(name: &str, attributes: I) -> Element { | ||
let mut string = String::from(name); | ||
let name_end = string.len(); | ||
for attr in attributes { | ||
string.push_str(&format!(" {}=\"{}\"", attr.0, attr.1)); | ||
} | ||
let bytes = string.into_bytes(); | ||
let end = bytes.len(); | ||
Element { | ||
buf: bytes, | ||
start: 0, | ||
end: end, | ||
name_end: name_end | ||
} | ||
} | ||
|
||
/// consumes entire self (including eventual attributes!) and returns `String` | ||
/// | ||
/// useful when we need to get Text event value (which don't have attributes) | ||
|
@@ -395,3 +414,53 @@ fn read_until<R: BufRead>(r: &mut R, byte: u8, buf: &mut Vec<u8>) -> Result<usiz | |
Ok(read) | ||
} | ||
|
||
/// Xml writer | ||
/// | ||
/// Consumes a `Write` and writes xml Events | ||
pub struct XmlWriter<W: Write> { | ||
/// underlying writer | ||
writer: W | ||
} | ||
|
||
impl<W: Write> XmlWriter<W> { | ||
|
||
/// Creates a XmlWriter from a generic Write | ||
pub fn new(inner: W) -> XmlWriter<W> { | ||
XmlWriter { | ||
writer: inner | ||
} | ||
} | ||
|
||
/// Consumes this Xml Writer, returning the underlying writer. | ||
pub fn into_inner(self) -> W { self.writer } | ||
|
||
pub fn write(&mut self, event: Event) -> Result<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment for the pub function (even simple)? |
||
match event { | ||
Event::Start(e) => self.write_start_tag(e), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just using |
||
Event::End(ref e) => self.write_wrapped_str(b"</", e, b">"), | ||
Event::Text(ref e) => self.write_bytes(e.as_bytes()), | ||
Event::Comment(ref e) => self.write_wrapped_str(b"<!--", e, b"-->"), | ||
Event::CData(ref e) => self.write_wrapped_str(b"<![CDATA[", e, b"]]>"), | ||
Event::Header(ref e) => self.write_wrapped_str(b"<?", e, b"?>"), | ||
} | ||
} | ||
|
||
#[inline] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alignment typo |
||
fn write_bytes(&mut self, value: &[u8]) -> Result<()> { | ||
try!(self.writer.write(value)); | ||
Ok(()) | ||
} | ||
|
||
fn write_start_tag(&mut self, element: Element) -> Result<()> { | ||
try!(self.write_bytes(b"<")); | ||
try!(self.write_bytes(&try!(element.into_string()).into_bytes())); | ||
self.write_bytes(b">") | ||
} | ||
|
||
fn write_wrapped_str(&mut self, before: &[u8], element: &Element, after: &[u8]) -> Result<()> { | ||
try!(self.write_bytes(before)); | ||
try!(self.write_bytes(element.as_bytes())); | ||
self.write_bytes(after) | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
use super::{XmlReader, Element}; | ||
use super::{Element, XmlReader, XmlWriter}; | ||
use super::Event::*; | ||
use std::str::from_utf8; | ||
use std::io::Cursor; | ||
|
||
macro_rules! next_eq { | ||
($r: expr, $($t:path, $bytes:expr),*) => { | ||
|
@@ -125,3 +126,42 @@ fn test_nested() { | |
); | ||
} | ||
|
||
#[test] | ||
fn test_writer() { | ||
let str = r#"<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.github.sample" android:versionName="Lollipop" android:versionCode="5.1"><application android:label="SampleApplication"></application></manifest>"#; | ||
let reader = XmlReader::from_str(&str).trim_text(true); | ||
let mut writer = XmlWriter::new(Cursor::new(Vec::new())); | ||
for event in reader { | ||
assert!(writer.write(event.unwrap()).is_ok()); | ||
} | ||
|
||
let result = writer.into_inner().into_inner(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alignment typo |
||
assert_eq!(result, str.as_bytes()); | ||
} | ||
|
||
#[test] | ||
fn test_write_attrs() { | ||
let str_from = r#"<elem attr="val"></elem>"#; | ||
let expected = r#"<elem attr="val" another="one"></elem>"#; | ||
let reader = XmlReader::from_str(&str_from).trim_text(true); | ||
let mut writer = XmlWriter::new(Cursor::new(Vec::new())); | ||
for event in reader { | ||
let event = event.unwrap(); | ||
let event = match event { | ||
Start(elem) => { | ||
let mut attrs = elem.attributes().map(|e| { | ||
let (k,v) = e.unwrap(); | ||
(from_utf8(k).unwrap(), v) | ||
}).collect::<Vec<_>>(); | ||
attrs.push(("another", "one")); | ||
let elem = Element::create("elem", attrs.into_iter()); | ||
Start(elem) | ||
}, | ||
_ => event | ||
}; | ||
assert!(writer.write(event).is_ok()); | ||
} | ||
|
||
let result = writer.into_inner().into_inner(); | ||
assert_eq!(result, expected.as_bytes()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few remarks:
new
instead ofcreate
and rename the existing (private)new
function intofrom_buffer
for instance (opened for other suggestion)(&'a str, &'a str)
is not symmetric withAttributes::Item = (&[u8], &str)
. I wanted to avoid utf8 conversion when possible because most of the time we (I ?) compare it with statically known names, thus I could always usekey == b"mykey"
. Having symmetric behavior could help creating Elements based on other Elements (in your test, you had to usefrom_utf8
), I think it is a common scenarioVec<u8>
and push bytes directlyAttributes
-like iterators, one pure&[u8]
, another one&str