use quick_xml::{events::Event, 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::Value;
pub fn response_from_str<T>(input: &str) -> Result<T>
where
T: serde::de::DeserializeOwned,
{
let mut reader = Reader::from_str(input);
reader.expand_empty_elements(true);
reader.trim_text(true);
let mut buf = Vec::new();
loop {
match reader
.read_event(&mut buf)
.map_err(error::ParseError::from)?
{
Event::Decl(_) => continue,
Event::Start(e) if e.name() == b"methodResponse" => {
break;
}
e => return Err(error::ParseError::UnexpectedEvent(format!("{:?}", e)).into()),
};
}
match reader
.read_event(&mut buf)
.map_err(error::ParseError::from)?
{
Event::Start(e) if e.name() == b"params" => {
let mut buf = Vec::new();
reader.expect_tag(b"param", &mut buf)?;
let mut deserializer = ValueDeserializer::new(reader)?;
let ret = T::deserialize(&mut deserializer)?;
let mut reader = deserializer.into_inner();
reader
.read_to_end(b"param", &mut buf)
.map_err(error::ParseError::from)?;
reader
.read_to_end(e.name(), &mut buf)
.map_err(error::ParseError::from)?;
Ok(ret)
}
Event::Start(e) if e.name() == b"fault" => {
let mut deserializer = ValueDeserializer::new(reader)?;
let fault: Fault = Fault::deserialize(&mut deserializer)?;
let mut reader = deserializer.into_inner();
let mut buf = Vec::new();
reader
.read_to_end(e.name(), &mut buf)
.map_err(error::ParseError::from)?;
Err(fault.into())
}
e => Err(error::ParseError::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(br#"<?xml version="1.0" encoding="utf-8"?>"#)
.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")?;
let deserializer = value::Deserializer::from_value(value);
let serializer = ValueSerializer::new(&mut writer);
transcode(deserializer, serializer)?;
writer.write_end_tag(b"param")?;
}
writer.write_end_tag(b"params")?;
writer.write_end_tag(b"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);
let mut deserializer = ValueDeserializer::new(reader)?;
let serializer = value::Serializer::new();
transcode(&mut 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)?)
}
#[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());
}
}