From 0a1f18b51f29dda73755ff12be98b84342ce72ca Mon Sep 17 00:00:00 2001 From: Kaleb Elwert Date: Tue, 28 Jan 2020 02:35:49 -0800 Subject: Initial code commit (#2) --- src/lib.rs | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 2 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..02c79a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,259 @@ +use quick_xml::{Reader, Writer}; + +mod error; +mod utils; +mod value; + +use utils::{ReaderExt, WriterExt}; + +pub use crate::error::{Error, Result}; +pub use crate::value::Value; + +pub fn parse_response(data: &str) -> Result { + let mut reader = Reader::from_str(data); + reader.expand_empty_elements(true); + reader.trim_text(true); + + let mut buf = Vec::new(); + + // We expect a value tag first, followed by a value. Note that the inner + // read will properly handle ensuring we get a closing value tag. + reader.expect_tag(b"methodResponse", &mut buf)?; + + Value::read_response_from_reader(&mut reader, &mut buf) +} + +pub fn parse_value(data: &str) -> Result { + let mut reader = Reader::from_str(data); + reader.expand_empty_elements(true); + reader.trim_text(true); + + let mut buf = Vec::new(); + + // We expect a value tag first, followed by a value. Note that the inner + // read will properly handle ensuring we get a closing value tag. + reader.expect_tag(b"value", &mut buf)?; + + Value::read_value_from_reader(&mut reader, &mut buf) +} + +pub fn stringify_request(name: &str, args: &[Value]) -> Result { + let mut buf = Vec::new(); + let mut writer = Writer::new(&mut buf); + + writer + .write(br#""#) + .map_err(error::EncodingError::from)?; + + writer.write_start_tag(b"methodCall")?; + writer.write_tag(b"methodName", name)?; + + writer.write_start_tag(b"params")?; + for value in args { + writer.write_start_tag(b"param")?; + + writer + .write(value.stringify()?.as_ref()) + .map_err(error::EncodingError::from)?; + + writer.write_end_tag(b"param")?; + } + writer.write_end_tag(b"params")?; + writer.write_end_tag(b"methodCall")?; + + Ok(String::from_utf8(buf).map_err(error::EncodingError::from)?) +} + #[cfg(test)] mod tests { + use super::*; + + #[test] + fn test_stringify_request() { + assert_eq!( + stringify_request("hello world", &[]).unwrap(), + r#"hello world"#.to_owned() + ) + } + + /// A 32-bit signed integer (`` or ``). + #[test] + fn parse_int_values() { + assert_eq!( + parse_value("42").unwrap().as_i32(), + Some(42) + ); + + assert_eq!( + parse_value("-42") + .unwrap() + .as_i32(), + Some(-42) + ); + + assert_eq!( + parse_value("2147483647") + .unwrap() + .as_i32(), + Some(2147483647) + ); + } + + /// A 64-bit signed integer (``). + #[test] + fn parse_long_values() { + assert_eq!( + parse_value("42").unwrap().as_i64(), + Some(42) + ); + + assert_eq!( + parse_value("9223372036854775807") + .unwrap() + .as_i64(), + Some(9223372036854775807) + ); + } + + /// A boolean value (``, 0 == `false`, 1 == `true`). + #[test] + fn parse_boolean_values() { + assert_eq!( + parse_value("1") + .unwrap() + .as_bool(), + Some(true) + ); + assert_eq!( + parse_value("0") + .unwrap() + .as_bool(), + Some(false) + ); + } + + /// A string (``). Note that these can also appear as a raw + /// value tag as well. + #[test] + fn parse_string_values() { + assert_eq!( + parse_value("hello") + .unwrap() + .as_str(), + Some("hello") + ); + + assert_eq!( + parse_value("world").unwrap().as_str(), + Some("world") + ); + + assert_eq!(parse_value("").unwrap().as_str(), Some("")); + } + + /// A double-precision IEEE 754 floating point number (``). + #[test] + fn parse_double_values() { + assert_eq!( + parse_value("1") + .unwrap() + .as_f64(), + Some(1.0) + ); + assert_eq!( + parse_value("0") + .unwrap() + .as_f64(), + Some(0.0) + ); + assert_eq!( + parse_value("42") + .unwrap() + .as_f64(), + Some(42.0) + ); + assert_eq!( + parse_value("3.14") + .unwrap() + .as_f64(), + Some(3.14) + ); + assert_eq!( + parse_value("-3.14") + .unwrap() + .as_f64(), + Some(-3.14) + ); + } + + /// An ISO 8601 formatted date/time value (``). + + /// Base64-encoded binary data (``). + #[test] + fn parse_base64_values() { + assert_eq!( + parse_value("aGVsbG8gd29ybGQ=") + .unwrap() + .as_bytes(), + Some(&b"hello world"[..]) + ); + } + + /// A mapping of named values (``). + + /// A list of arbitrary (heterogeneous) values (``). + #[test] + fn parse_array_values() { + assert_eq!( + parse_value( + "" + ) + .unwrap() + .as_array(), + Some(&[Value::String("".to_owned()), Value::Nil][..]) + ); + } + + /// The empty (Unit) value (``). + #[test] + fn parse_nil_values() { + assert_eq!(parse_value("").unwrap(), Value::Nil); + } + #[test] - fn it_works() { - assert_eq!(2 + 2, 4); + fn parse_fault() { + let err = parse_response( + r#" + + + + + + faultCode + 4 + + + faultString + Too many parameters. + + + + + "#, + ) + .unwrap_err(); + + match err { + error::Error::Fault(f) => assert_eq!( + f, + error::Fault { + fault_code: 4, + fault_string: "Too many parameters.".into(), + } + ), + _ => { + assert!(false); + } + } } } -- cgit v1.2.3