Skip to content

Instantly share code, notes, and snippets.

@kroltan
Created September 7, 2021 16:44
Show Gist options
  • Save kroltan/d756450e7d0aa79e947be71b86587f16 to your computer and use it in GitHub Desktop.
Save kroltan/d756450e7d0aa79e947be71b86587f16 to your computer and use it in GitHub Desktop.
World's second-worst Rust XML writing library
use std::{
borrow::BorrowMut,
io::{Error, ErrorKind, Write},
};
#[derive(Debug)]
enum State {
Open,
Attribute,
Children,
}
pub struct Xml<W: Write> {
writer: W
}
impl<W: Write> Xml<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
pub fn open(self, tag: &str) -> XmlElement<W> {
XmlElement {
tag,
writer: self.writer,
state: State::Open,
}
}
pub fn end(self) -> W {
self.writer
}
}
#[derive(Debug)]
#[must_use = "You must call `.close()` to finish writing the element"]
pub struct XmlElement<'a, W: Write> {
tag: &'a str,
writer: W,
state: State,
}
impl<'a, W: Write> XmlElement<'a, W> {
pub fn sibling<'b>(self, tag: &'b str) -> Result<XmlElement<'b, W>, Error> {
let writer = self.close()?.end();
Ok(Xml::new(writer).open(tag))
}
pub fn attribute(mut self, attribute: &str, value: &str) -> Result<Self, Error> {
match self.state {
State::Open => {
write!(self.writer.borrow_mut(), "<{}", self.tag)?;
self.state = State::Attribute
}
State::Attribute => {}
State::Children => {
return Err(Error::new(
ErrorKind::Other,
"Attempted to write attribute after children",
))
}
}
write!(self.writer.borrow_mut(), " {}=\"{}\"", attribute, value)?;
Ok(self)
}
pub fn children(mut self, builder: fn(Xml<W>) -> Result<W, Error>) -> Result<Self, Error> {
match self.state {
State::Open => {
write!(self.writer.borrow_mut(), "<{}>", self.tag)?;
self.state = State::Children
}
State::Attribute => {
write!(self.writer.borrow_mut(), ">")?;
self.state = State::Children
}
State::Children => {}
}
let writer = builder(Xml::new(self.writer))?;
Ok(Self { writer, ..self })
}
pub fn close(mut self) -> Result<Xml<W>, Error> {
let writer = self.writer.borrow_mut();
match self.state {
State::Open => write!(writer, "<{}/>", self.tag)?,
State::Attribute => write!(writer, "/>")?,
State::Children => write!(writer, "</{}>", self.tag)?,
}
Ok(Xml::new(self.writer))
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn it_works() -> Result<(), std::io::Error> {
let writer = Xml::new(Vec::new())
.open("foo")
.attribute("bar", "12")?
.children(|xml| Ok(xml.open("kek").sibling("kek")?.close()?.end()))?
.close()?
.end();
assert_eq!(&writer, "<foo bar=\"12\"><kek/><kek/></foo>".as_bytes());
Ok(())
}
}
@kroltan
Copy link
Author

kroltan commented Sep 7, 2021

Also if you get hacked or your server catches fire because you were mad enough to use this with actual real world data it's not my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment