Skip to content
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

Merged
merged 4 commits into from
Feb 26, 2016
Merged

Conversation

matt2xu
Copy link

@matt2xu matt2xu commented Feb 25, 2016

This PR adds the possibility to transform (add/change elements) and write XML. Two changes:

  1. add a XmlWriter structure, which just wraps a Write at the moment but could (should?) be extended in the future to support pretty-printing or automatic shortening of empty tags.
  2. add a "create" method to Element, to create an element with a name/content, and possibly attributes (should only be used for start elements, but at the moment this is not enforced). For elements with no attributes, use the iter::empty() function.

I added one test for the writer, and one test for the writer + element create. Also, note that I made the XmlWriter use a Write trait rather than BufWrite because it doesn't need its additional methods at the moment, but of course for best performance one should use a writer implementing BufWrite.

@matt2xu matt2xu mentioned this pull request Feb 25, 2016
/// 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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few remarks:

  • you can use new instead of create and rename the existing (private) new function into from_buffer for instance (opened for other suggestion)
  • (&'a str, &'a str) is not symmetric with Attributes::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 use key == b"mykey". Having symmetric behavior could help creating Elements based on other Elements (in your test, you had to use from_utf8), I think it is a common scenario
    • in your example, we could use Vec<u8> and push bytes directly
    • an alternative is to have 2 Attributes-like iterators, one pure &[u8], another one &str

@tafia
Copy link
Owner

tafia commented Feb 25, 2016

That's awesome!
I've done some comments but it looks really nice, thank you very much!

@tafia
Copy link
Owner

tafia commented Feb 25, 2016

Also might be good to update the readme 👍

Renamed Element::new to Element::from_buffer, and Element::create to
Element::new. Reordered the functions for "new" to appear first, as
seems to be the convention.

Added an example in the documentation of how to use the XmlWriter and
Element::new.

Fixed the existing example by replacing println! by panic! in case of
errors. Indeed the XML was erroneous, and the parser was emitting
errors, but because we didn't panic, the test was passing.

Fixed the XML to make the test pass.
@matt2xu
Copy link
Author

matt2xu commented Feb 25, 2016

Thanks for the review! I have made some changes to address these points: typos, comment on the write function, new instead of create.

Rather than change the README (not testable), I have added an example in the lib.rs file, and also taken the liberty to improve the existing example and fix the XML. Ideally, the documentation could be hosted on Github pages from the output of rustdoc, to make sure the examples are always up to date and compile.

Regarding the signature of Iterator<&str, &str> I know it's not symmetric, it just feels more natural in my opinion :-) I find it a bit low-level to do comparisons with byte string literals, but I understand that you want to avoid unnecessary UTF-8 conversion. In the case of the element's name, you have a choice: you can use either as_bytes() or as_str(). But for attributes, since the type is fixed on the Iterator implementation, it's a bit more difficult. At the same time, is that really a bottleneck on attributes? Generally, you match on element names first, so I assume this is where a program would typically spend most of its time checking.

Alternatively, for the Element::new method, maybe I could use AsRef<[u8]> to support both (&[u8], &str) and (&str, &str); I haven't tried, but it seems doable. Then we would implement two iterator methods on Attributes to support both cases.

Just let me know what your preference is :-)

@tafia
Copy link
Owner

tafia commented Feb 25, 2016 via email

This uses AsRef<[u8]> which is implemented for [u8], str, and String
@matt2xu
Copy link
Author

matt2xu commented Feb 25, 2016

Works with AsRef, which I think is the way to go: Into would move its argument, while we only need to borrow it as a reference to a byte slice. In the future (in another contribution) I say we should try to improve Attributes to have the same flexibility as Element regarding the as_bytes()/as_str() duality. But for now it's good as it is I think :-)

tafia added a commit that referenced this pull request Feb 26, 2016
Added XmlWriter and Element::create API
@tafia tafia merged commit 1be42c8 into tafia:master Feb 26, 2016
@tafia
Copy link
Owner

tafia commented Feb 26, 2016

Thanks a lot!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants