use std::str::FromStr; use minidom::Element; use base64; use error::Error; use ns; #[derive(Debug, Clone)] pub enum Stanza { Iq, Message, } impl Default for Stanza { fn default() -> Stanza { Stanza::Iq } } impl FromStr for Stanza { type Err = Error; fn from_str(s: &str) -> Result { if s == "iq" { Ok(Stanza::Iq) } else if s == "message" { Ok(Stanza::Message) } else { Err(Error::ParseError("Unknown 'stanza' attribute.")) } } } #[derive(Debug, Clone)] pub enum IBB { Open { block_size: u16, sid: String, stanza: Stanza }, Data { seq: u16, sid: String, data: Vec }, Close(String), } fn optional_attr(root: &Element, attr: &str) -> Option { root.attr(attr) .and_then(|value| value.parse().ok()) } fn required_attr(root: &Element, attr: &str, err: Error) -> Result { optional_attr(root, attr).ok_or(err) } pub fn parse_ibb(root: &Element) -> Result { if root.is("open", ns::IBB) { let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; let stanza = root.attr("stanza") .and_then(|value| value.parse().ok()) .unwrap_or_default(); for _ in root.children() { return Err(Error::ParseError("Unknown child in open element.")); } Ok(IBB::Open { block_size: block_size, sid: sid, stanza: stanza }) } else if root.is("data", ns::IBB) { let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?; let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?; let data = base64::decode(&root.text())?; for _ in root.children() { return Err(Error::ParseError("Unknown child in data element.")); } Ok(IBB::Data { seq: seq, sid: sid, data: data }) } else { Err(Error::ParseError("This is not an ibb element.")) } } #[cfg(test)] mod tests { use minidom::Element; use error::Error; use ibb; #[test] fn test_simple() { let elem: Element = "".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); let elem: Element = "AAAA".parse().unwrap(); ibb::parse_ibb(&elem).unwrap(); } #[test] fn test_invalid() { let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'block-size' missing in open element."); // TODO: maybe make a better error message here. let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'block-size' missing in open element."); let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'sid' missing in open element."); } #[test] #[ignore] fn test_invalid_stanza() { let elem: Element = "".parse().unwrap(); let error = ibb::parse_ibb(&elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Wrong value for 'stanza' attribute in open."); } }