xml-rpc 0.0.6

Pure Rust implementation of XML-RPC
Documentation
use std;
use std::collections::HashMap;
use base64;
use serde_xml_rs::deserialize;
use super::{Call, Fault, Response, Value};
use super::error::{Result, ResultExt};

#[allow(dead_code)]
pub fn xml<T: std::io::Read>(r: T) -> Result<Value> {
    let data: XmlValue = deserialize(r).chain_err(|| "Failed to parse XML-RPC data.")?;
    data.into()
}

pub fn call<T: std::io::Read>(r: T) -> Result<Call> {
    let data: XmlCall = deserialize(r).chain_err(|| "Failed to parse XML-RPC call.")?;
    data.into()
}

pub fn response<T: std::io::Read>(r: T) -> Result<Response> {
    let data: XmlResponse = deserialize(r).chain_err(
        || "Failed to parse XML-RPC response.",
    )?;
    data.into()
}

#[derive(Debug, PartialEq, Deserialize)]
enum XmlValue {
    #[serde(rename = "i4")]
    I4(i32),
    #[serde(rename = "int")]
    Int(i32),
    #[serde(rename = "boolean")]
    Bool(i32),
    #[serde(rename = "string")]
    Str(String),
    #[serde(rename = "double")]
    Double(String),
    #[serde(rename = "dateTime.iso8601")]
    DateTime(String),
    #[serde(rename = "base64")]
    Base64(String),
    #[serde(rename = "array")]
    Array(XmlArray),
    #[serde(rename = "struct")]
    Struct(XmlStruct),
}

impl Into<Result<Value>> for XmlValue {
    fn into(self) -> Result<Value> {
        Ok(match self {
            XmlValue::I4(v) | XmlValue::Int(v) => Value::Int(v),
            XmlValue::Bool(v) => Value::Bool(v != 0),
            XmlValue::Str(v) => Value::String(v),
            XmlValue::Double(v) => Value::Double(v.parse().chain_err(|| "Failed to parse double")?),
            XmlValue::DateTime(v) => Value::DateTime(v),
            XmlValue::Base64(v) => Value::Base64(base64::decode(v.as_bytes()).chain_err(
                || "Failed to parse base64",
            )?),
            XmlValue::Array(v) => {
                let items: Result<Vec<Value>> = v.into();
                Value::Array(items?)
            }
            XmlValue::Struct(v) => {
                let items: Result<HashMap<String, Value>> = v.into();
                Value::Struct(items?)
            }
        })
    }
}

#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename = "methodCall")]
struct XmlCall {
    #[serde(rename = "methodName")]
    pub name: String,
    pub params: XmlParams,
}

impl Into<Result<Call>> for XmlCall {
    fn into(self) -> Result<Call> {
        let params: Result<Vec<Value>> = self.params.into();
        Ok(Call {
            name: self.name,
            params: params?,
        })
    }
}

#[derive(Debug, PartialEq, Deserialize)]
enum XmlResponseResult {
    #[serde(rename = "params")]
    Success(XmlParams),
    #[serde(rename = "fault")]
    Failure { value: XmlValue },
}

impl Into<Result<Response>> for XmlResponseResult {
    fn into(self) -> Result<Response> {
        match self {
            XmlResponseResult::Success(params) => {
                let params: Result<Vec<Value>> = params.into();
                Ok(Ok(params?))
            }
            XmlResponseResult::Failure { value: v } => {
                use serde::Deserialize;

                let val: Result<Value> = v.into();

                Ok(Err(Fault::deserialize(val?).chain_err(
                    || "Failed to decode fault structure",
                )?))
            }
        }
    }
}

#[derive(Debug, PartialEq, Deserialize)]
enum XmlResponse {
    #[serde(rename = "methodResponse")]
    Response(XmlResponseResult),
}

impl Into<Result<Response>> for XmlResponse {
    fn into(self) -> Result<Response> {
        match self {
            XmlResponse::Response(v) => v.into(),
        }
    }
}


#[derive(Debug, PartialEq, Deserialize)]
struct XmlParams {
    #[serde(rename = "param", default)]
    pub params: Vec<XmlParamData>,
}

impl Into<Result<Vec<Value>>> for XmlParams {
    fn into(self) -> Result<Vec<Value>> {
        self.params
            .into_iter()
            .map(Into::<Result<Value>>::into)
            .collect()
    }
}

#[derive(Debug, PartialEq, Deserialize)]
struct XmlParamData {
    pub value: XmlValue,
}

impl Into<Result<Value>> for XmlParamData {
    fn into(self) -> Result<Value> {
        self.value.into()
    }
}

#[derive(Debug, PartialEq, Deserialize)]
struct XmlArray {
    #[serde(rename = "data")]
    pub data: XmlArrayData,
}

impl Into<Result<Vec<Value>>> for XmlArray {
    fn into(self) -> Result<Vec<Value>> {
        self.data.into()
    }
}

#[derive(Debug, PartialEq, Deserialize)]
struct XmlArrayData {
    #[serde(default)]
    pub value: Vec<XmlValue>,
}

impl Into<Result<Vec<Value>>> for XmlArrayData {
    fn into(self) -> Result<Vec<Value>> {
        self.value
            .into_iter()
            .map(Into::<Result<Value>>::into)
            .collect()
    }
}

#[derive(Debug, PartialEq, Deserialize)]
struct XmlStruct {
    #[serde(rename = "member", default)]
    pub members: Vec<XmlStructItem>,
}

impl Into<Result<HashMap<String, Value>>> for XmlStruct {
    fn into(self) -> Result<HashMap<String, Value>> {
        self.members
            .into_iter()
            .map(Into::<Result<(String, Value)>>::into)
            .collect()
    }
}

#[derive(Debug, PartialEq, Deserialize)]
struct XmlStructItem {
    pub name: String,
    pub value: XmlValue,
}

impl Into<Result<(String, Value)>> for XmlStructItem {
    fn into(self) -> Result<(String, Value)> {
        let value: Result<Value> = self.value.into();
        Ok((self.name, value?))
    }
}