//! This library provides a basic API for serializing / deserializng xmlrpc. //! Combine with your transport or server of choice for an easy and quick xmlrpc experience. use quick_xml::{events::Event, name::QName, Reader, Writer}; use serde::Deserialize; use serde_transcode::transcode; mod de; mod error; mod ser; mod value; mod xml_ext; use de::Deserializer as ValueDeserializer; use ser::Serializer as ValueSerializer; use xml_ext::{ReaderExt, WriterExt}; pub use error::{Error, Fault, Result}; pub use value::{from_value, to_value, Value}; /// Parses the body of an xmlrpc http request and attempts to convert it to the desired type. /// ``` /// let val: String = serde_xmlrpc::response_from_str( /// r#" /// /// /// hello world /// /// "#).unwrap(); /// /// assert_eq!(val, "hello world".to_string()); /// ``` pub fn response_from_str<'a, T>(input: &'a str) -> Result where T: serde::de::Deserialize<'a>, { let mut reader = Reader::from_str(input); reader.expand_empty_elements(true); reader.trim_text(true); // Check the first event. This will determine if we're loading a Fault or a // Value. loop { match reader.read_event().map_err(error::DecodingError::from)? { Event::Decl(_) => continue, Event::Start(e) if e.name() == QName(b"methodResponse") => { break; } e => return Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()), }; } match reader.read_event().map_err(error::DecodingError::from)? { Event::Start(e) if e.name() == QName(b"params") => { reader.expect_tag(QName(b"param"))?; reader.expect_tag(QName(b"value"))?; let deserializer = ValueDeserializer::new(&mut reader)?; let ret = T::deserialize(deserializer)?; reader .read_to_end(QName(b"param")) .map_err(error::DecodingError::from)?; reader .read_to_end(e.name()) .map_err(error::DecodingError::from)?; Ok(ret) } Event::Start(e) if e.name() == QName(b"fault") => { // The inner portion of a fault is just a Value tag, so we // deserialize it from a value. reader.expect_tag(QName(b"value"))?; let deserializer = ValueDeserializer::new(&mut reader)?; let fault: Fault = Fault::deserialize(deserializer)?; reader .read_to_end(e.name()) .map_err(error::DecodingError::from)?; Err(fault.into()) } e => Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()), } } /// Attempt to serialize a xmlrpc response from a list of values. /// Each item in the list will be represented as a separate "param" in xmlrpc parlance. /// ``` /// use serde_xmlrpc::{response_to_string,Value}; /// let body = response_to_string(vec![Value::Int(42), Value::String("data".to_string())].into_iter()).unwrap(); /// assert_eq!(body, /// r#"42data"# /// ); /// ``` pub fn response_to_string(params: impl Iterator) -> Result { let mut writer = Writer::new(Vec::new()); writer.write_decl()?; writer.write_start_tag("methodResponse")?; writer.write_start_tag("params")?; for value in params { writer.write_start_tag("param")?; let serializer = ValueSerializer::new(&mut writer); transcode(value, serializer)?; writer.write_end_tag("param")?; } writer.write_end_tag("params")?; writer.write_end_tag("methodResponse")?; Ok(String::from_utf8(writer.into_inner()).map_err(error::EncodingError::from)?) } /// Expects an input string which is a valid xmlrpc request body, and parses out the method name and parameters from it. /// This function would typically be used by a server to parse incoming requests. /// * Returns a tuple of (method name, Arguments) if successful /// This does not parse the types of the arguments, as typically the server needs to resolve /// the method name before it can know the expected types. pub fn request_from_str(request: &str) -> Result<(String, Vec)> { let mut reader = Reader::from_str(request); reader.expand_empty_elements(true); reader.trim_text(true); // Search for methodCall start loop { match reader.read_event().map_err(error::DecodingError::from)? { Event::Decl(_) => continue, Event::Start(e) if e.name() == QName(b"methodCall") => { break; } e => return Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()), }; } // This code currently assumes that the will always precede // in the xmlrpc request, I'm not certain that this is actually enforced by the // specification, but could find not counter example where it wasn't true... -Carter let method_name = match reader.read_event().map_err(error::DecodingError::from)? { Event::Start(e) if e.name() == QName(b"methodName") => reader .read_text(e.name()) .map_err(error::DecodingError::from)?, e => return Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()), }; match reader.read_event().map_err(error::DecodingError::from)? { Event::Start(e) if e.name() == QName(b"params") => { let mut params = Vec::new(); let params = loop { break match reader.read_event().map_err(error::DecodingError::from)? { // Read each parameter into a Value Event::Start(e) if e.name() == QName(b"param") => { reader.expect_tag(QName(b"value"))?; let deserializer = ValueDeserializer::new(&mut reader)?; let serializer = value::Serializer::new(); let x = transcode(deserializer, serializer)?; params.push(x); reader .read_to_end(e.name()) .map_err(error::DecodingError::from)?; continue; } // Once we see the relevant params end tag, we know we have all the params. Event::End(e) if e.name() == QName(b"params") => params, e => { return Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()) } }; }; // We can skip reading to the end of the params tag because if we're // here, we've already hit the end tag. Ok((method_name.into_owned(), params)) } e => Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()), } } /// Takes in the name of a method call and a list of parameters and attempts to convert them to a String /// which would be a valid body for an xmlrpc request. /// /// ``` /// let body = serde_xmlrpc::request_to_string("myMethod", vec![1.into(), "param2".into()].into_iter()); /// ``` pub fn request_to_string(name: &str, args: impl Iterator) -> Result { let mut writer = Writer::new(Vec::new()); writer.write_decl()?; writer.write_start_tag("methodCall")?; writer.write_tag("methodName", name)?; writer.write_start_tag("params")?; for value in args { writer.write_start_tag("param")?; let serializer = ValueSerializer::new(&mut writer); transcode(value, serializer)?; writer.write_end_tag("param")?; } writer.write_end_tag("params")?; writer.write_end_tag("methodCall")?; Ok(String::from_utf8(writer.into_inner()).map_err(error::EncodingError::from)?) } /// Attempts to parse an individual value out of a str. /// ``` /// let x: i32 = serde_xmlrpc::value_from_str("42").unwrap(); /// assert_eq!(x, 42); /// ``` pub fn value_from_str<'a, T>(input: &'a str) -> Result where T: serde::de::Deserialize<'a>, { let mut reader = Reader::from_str(input); reader.expand_empty_elements(true); reader.trim_text(true); reader.expect_tag(QName(b"value"))?; T::deserialize(ValueDeserializer::new(&mut reader)?) } /// Attempts to convert any data type which can be represented as an xmlrpc value into a String. /// ``` /// let a = serde_xmlrpc::value_to_string(42); /// let b = serde_xmlrpc::value_to_string("Text"); /// let c = serde_xmlrpc::value_to_string(false); /// ``` pub fn value_to_string(val: T) -> Result where T: serde::ser::Serialize, { let mut writer = Writer::new(Vec::new()); let ser = crate::ser::Serializer::new(&mut writer); val.serialize(ser)?; Ok(String::from_utf8(writer.into_inner()).map_err(error::DecodingError::from)?) } /// Attempts to convert a Vec of values to any data type which can be deserialized. /// This is typically used with [request_from_str] to implement server behavior: /// ``` /// let val = r#" /// /// requestTopic /// /// /rosout /// 42 /// /// "#; /// // Parse the request /// let (method, vals) = serde_xmlrpc::request_from_str(val).unwrap(); /// // Now that we know what method is being called we can typecast our args /// let (a, b): (String, i32) = serde_xmlrpc::from_values(vals).unwrap(); /// ``` pub fn from_values(values: Vec) -> Result { // Wrap input vec into our value type so it is compatible with our deserializer // Kinda a cheap hack, but I like returning Vec for the args to a function // instead of a Value which is itself an array... let val = Value::Array(values); from_value(val) } #[cfg(test)] mod tests { use super::*; #[test] fn test_stringify_request() { assert_eq!( request_to_string("hello world", vec![].into_iter()).unwrap(), r#"hello world"#.to_owned() ) } /// A 32-bit signed integer (`` or ``). #[test] fn parse_int_values() { assert_eq!( value_from_str::("42") .unwrap() .as_i32(), Some(42) ); assert_eq!( value_from_str::("-42") .unwrap() .as_i32(), Some(-42) ); assert_eq!( value_from_str::("2147483647") .unwrap() .as_i32(), Some(2147483647) ); } /// A 64-bit signed integer (``). #[test] fn parse_long_values() { assert_eq!( value_from_str::("42") .unwrap() .as_i64(), Some(42) ); assert_eq!( value_from_str::("9223372036854775807") .unwrap() .as_i64(), Some(9223372036854775807) ); } /// A boolean value (``, 0 == `false`, 1 == `true`). #[test] fn parse_boolean_values() { assert_eq!( value_from_str::("1") .unwrap() .as_bool(), Some(true) ); assert_eq!( value_from_str::("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!( value_from_str::("hello") .unwrap() .as_str(), Some("hello") ); assert_eq!( value_from_str::("world") .unwrap() .as_str(), Some("world") ); assert_eq!( value_from_str::("").unwrap().as_str(), Some("") ); } /// A double-precision IEEE 754 floating point number (``). #[test] fn parse_double_values() { assert_eq!( value_from_str::("1") .unwrap() .as_f64(), Some(1.0) ); assert_eq!( value_from_str::("0") .unwrap() .as_f64(), Some(0.0) ); assert_eq!( value_from_str::("42") .unwrap() .as_f64(), Some(42.0) ); assert_eq!( value_from_str::("3.14") .unwrap() .as_f64(), Some(3.14) ); assert_eq!( value_from_str::("-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!( value_from_str::("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!( value_from_str::( "" ) .unwrap() .as_array(), Some(&[Value::String("".to_owned()), Value::Nil][..]) ); } /// The empty (Unit) value (``). #[test] fn parse_nil_values() { assert_eq!( value_from_str::("").unwrap(), Value::Nil ); } #[test] fn parse_fault() { let err = response_from_str::( 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(), } ), _ => { println!("{:?}", err); assert!(false); } } } #[test] fn parse_value() { let val: String = response_from_str( r#" hello world "#, ) .unwrap(); assert_eq!(val, "hello world".to_string()); } #[test] fn test_parse_request_multiple_params() { let val = r#" requestTopic /rosout 42 TCPROS "#; let (method, vals) = request_from_str(val).unwrap(); assert_eq!(&method, "requestTopic"); // This is a little redundant with test_from_values, but is easiest way // to confirm parsing was perfect let (a, b, c): (String, i32, Vec>) = from_values(vals).unwrap(); assert_eq!(a, "/rosout"); assert_eq!(b, 42); assert_eq!(c, vec![vec!["TCPROS".to_string()]]); } #[test] fn test_response_to_value() { // Ensure Value implementes serde::Deserialize. This allows reading // responses into a Value rather than a specific type. let val: Value = response_from_str( r#" hello world "#, ) .unwrap(); assert_eq!(val, Value::String("hello world".to_string())); } #[test] fn test_from_values() { let vals = vec![ Value::Int(32), Value::Double(1.0), Value::String("hello".to_string()), ]; let (a, b, c): (i32, f64, String) = from_values(vals).unwrap(); assert_eq!(a, 32); assert_eq!(b, 1.0); assert_eq!(c, "hello"); } #[test] fn test_from_str() { let x: i32 = value_from_str("42").unwrap(); assert_eq!(x, 42); let x: bool = value_from_str("1").unwrap(); assert_eq!(x, true); let x: Vec = value_from_str("123").unwrap(); assert_eq!(x, vec![1, 2, 3]); let x: Test = value_from_str("helloworld").unwrap(); assert_eq!( x, Test { hello: "world".to_string() } ); let x: Option = value_from_str("").unwrap(); assert_eq!(x, None); let x: Option = value_from_str("hello world").unwrap(); assert_eq!(x, Some("hello world".to_string())); } use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Test { hello: String, } #[test] fn test_to_string() { assert_eq!( &value_to_string(&42).unwrap(), "42" ); assert_eq!( &value_to_string(&true).unwrap(), "1" ); assert_eq!( &value_to_string(&vec![1, 2, 3]).unwrap(), "123" ); assert_eq!( &value_to_string(&Test { hello: "world".to_string() }).unwrap(), "helloworld", ); assert_eq!( &value_to_string(&Some("hello world".to_string())).unwrap(), "hello world", ); assert_eq!( &value_to_string(&None::).unwrap(), "", ); } }