use quick_xml::{events::Event, name::QName, Reader, Writer};
use serde::Deserialize;
use serde_transcode::transcode;
mod error;
mod util;
mod value;
use util::{ReaderExt, ValueDeserializer, ValueSerializer, WriterExt};
pub use error::{Error, Fault, Result};
pub use value::{to_value, Value};
pub fn response_from_str<'a, T>(input: &'a str) -> Result<T>
where
T: serde::de::Deserialize<'a>,
{
let mut reader = Reader::from_str(input);
reader.expand_empty_elements(true);
reader.trim_text(true);
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") => {
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()),
}
}
pub fn response_to_string(params: impl Iterator<Item = Value>) -> Result<String> {
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 deserializer = value::Deserializer::from_value(value);
let serializer = ValueSerializer::new(&mut writer);
transcode(deserializer, 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)?)
}
pub fn request_from_str(request: &str) -> Result<(String, Vec<Value>)> {
let mut reader = Reader::from_str(request);
reader.expand_empty_elements(true);
reader.trim_text(true);
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()),
};
}
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)? {
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;
}
Event::End(e) if e.name() == QName(b"params") => params,
e => {
return Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into())
}
};
};
Ok((method_name.into_owned(), params))
}
e => Err(error::DecodingError::UnexpectedEvent(format!("{:?}", e)).into()),
}
}
pub fn request_to_string(name: &str, args: Vec<Value>) -> Result<String> {
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 deserializer = value::Deserializer::from_value(value);
let serializer = ValueSerializer::new(&mut writer);
transcode(deserializer, 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)?)
}
pub fn value_from_str(input: &str) -> Result<Value> {
let mut reader = Reader::from_str(input);
reader.expand_empty_elements(true);
reader.trim_text(true);
reader.expect_tag(QName(b"value"))?;
let deserializer = ValueDeserializer::new(&mut reader)?;
let serializer = value::Serializer::new();
transcode(deserializer, serializer)
}
pub fn value_to_string<I>(val: I) -> Result<String>
where
I: Into<Value>,
{
let d = value::Deserializer::from_value(val.into());
let mut writer = Writer::new(Vec::new());
let s = ValueSerializer::new(&mut writer);
transcode(d, s)?;
Ok(String::from_utf8(writer.into_inner()).map_err(error::EncodingError::from)?)
}
pub fn from_values<T: serde::de::DeserializeOwned>(values: Vec<Value>) -> Result<T> {
let val = Value::Array(values);
from_value(val)
}
pub fn from_value<T: serde::de::DeserializeOwned>(value: Value) -> Result<T> {
let d = value::Deserializer::from_value(value);
T::deserialize(d)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stringify_request() {
assert_eq!(
request_to_string("hello world", vec![]).unwrap(),
r#"<?xml version="1.0" encoding="utf-8"?><methodCall><methodName>hello world</methodName><params></params></methodCall>"#.to_owned()
)
}
#[test]
fn parse_int_values() {
assert_eq!(
value_from_str("<value><int>42</int></value>")
.unwrap()
.as_i32(),
Some(42)
);
assert_eq!(
value_from_str("<value><int>-42</int></value>")
.unwrap()
.as_i32(),
Some(-42)
);
assert_eq!(
value_from_str("<value><int>2147483647</int></value>")
.unwrap()
.as_i32(),
Some(2147483647)
);
}
#[test]
fn parse_long_values() {
assert_eq!(
value_from_str("<value><int>42</int></value>")
.unwrap()
.as_i64(),
Some(42)
);
assert_eq!(
value_from_str("<value><int>9223372036854775807</int></value>")
.unwrap()
.as_i64(),
Some(9223372036854775807)
);
}
#[test]
fn parse_boolean_values() {
assert_eq!(
value_from_str("<value><boolean>1</boolean></value>")
.unwrap()
.as_bool(),
Some(true)
);
assert_eq!(
value_from_str("<value><boolean>0</boolean></value>")
.unwrap()
.as_bool(),
Some(false)
);
}
#[test]
fn parse_string_values() {
assert_eq!(
value_from_str("<value><string>hello</string></value>")
.unwrap()
.as_str(),
Some("hello")
);
assert_eq!(
value_from_str("<value>world</value>").unwrap().as_str(),
Some("world")
);
assert_eq!(value_from_str("<value />").unwrap().as_str(), Some(""));
}
#[test]
fn parse_double_values() {
assert_eq!(
value_from_str("<value><double>1</double></value>")
.unwrap()
.as_f64(),
Some(1.0)
);
assert_eq!(
value_from_str("<value><double>0</double></value>")
.unwrap()
.as_f64(),
Some(0.0)
);
assert_eq!(
value_from_str("<value><double>42</double></value>")
.unwrap()
.as_f64(),
Some(42.0)
);
assert_eq!(
value_from_str("<value><double>3.14</double></value>")
.unwrap()
.as_f64(),
Some(3.14)
);
assert_eq!(
value_from_str("<value><double>-3.14</double></value>")
.unwrap()
.as_f64(),
Some(-3.14)
);
}
#[test]
fn parse_base64_values() {
assert_eq!(
value_from_str("<value><base64>aGVsbG8gd29ybGQ=</base64></value>")
.unwrap()
.as_bytes(),
Some(&b"hello world"[..])
);
}
#[test]
fn parse_array_values() {
assert_eq!(
value_from_str(
"<value><array><data><value></value><value><nil /></value></data></array></value>"
)
.unwrap()
.as_array(),
Some(&[Value::String("".to_owned()), Value::Nil][..])
);
}
#[test]
fn parse_nil_values() {
assert_eq!(
value_from_str("<value><nil /></value>").unwrap(),
Value::Nil
);
}
#[test]
fn parse_fault() {
let err = response_from_str::<String>(
r#"<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>4</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Too many parameters.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>"#,
)
.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#"<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<params>
<param><value><string>hello world</string></value></param>
</params>
</methodResponse>"#,
)
.unwrap();
assert_eq!(val, "hello world".to_string());
}
#[test]
fn test_parse_request_multiple_params() {
let val = r#"<?xml version=\"1.0\"?>
<methodCall>
<methodName>requestTopic</methodName>
<params>
<param><value>/rosout</value></param>
<param><value><int>42</int></value></param>
<param><value><array><data><value><array><data><value>TCPROS</value></data></array></value></data></array></value></param>
</params>
</methodCall>"#;
let (method, vals) = request_from_str(val).unwrap();
assert_eq!(&method, "requestTopic");
let (a, b, c): (String, i32, Vec<Vec<String>>) = 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() {
let val: Value = response_from_str(
r#"<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<params>
<param><value><string>hello world</string></value></param>
</params>
</methodResponse>"#,
)
.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");
}
}