xml-rpc 0.0.6

Pure Rust implementation of XML-RPC
Documentation
use std;
use std::collections::HashMap;
use base64;
use serde::de::Unexpected;
use xml::escape::escape_str_pcdata;

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Int(i32),
    Bool(bool),
    String(String),
    Double(f64),
    DateTime(String),
    Base64(Vec<u8>),
    Array(Vec<Value>),
    Struct(HashMap<String, Value>),
}

impl Value {
    pub fn unexpected(&self) -> Unexpected {
        match *self {
            Value::Int(v) => Unexpected::Signed(v as i64),
            Value::Bool(v) => Unexpected::Bool(v),
            Value::String(ref v) => Unexpected::Str(v),
            Value::Double(v) => Unexpected::Float(v),
            Value::DateTime(_) => Unexpected::Other("dateTime.iso8601"),
            Value::Base64(ref v) => Unexpected::Bytes(v),
            Value::Array(_) => Unexpected::Seq,
            Value::Struct(_) => Unexpected::Map,
        }
    }
}

pub type Params = Vec<Value>;

#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Fault {
    #[serde(rename = "faultCode")]
    pub code: i32,
    #[serde(rename = "faultString")]
    pub message: String,
}

impl Fault {
    pub fn new<T>(code: i32, message: T) -> Fault
    where
        T: Into<String>,
    {
        Fault {
            code,
            message: message.into(),
        }
    }
}

pub type Response = std::result::Result<Params, Fault>;

#[derive(Clone, Debug, PartialEq)]
pub struct Call {
    pub name: String,
    pub params: Params,
}

pub trait ToXml {
    fn to_xml(&self) -> String;
}

impl ToXml for Call {
    fn to_xml(&self) -> String {
        format!(
            include_str!("templates/call.xml"),
            name = self.name,
            params = self.params
                .iter()
                .map(|param| format!("<param>{}</param>", param.to_xml()))
                .collect::<String>()
        )
    }
}

impl ToXml for Response {
    fn to_xml(&self) -> String {
        match *self {
            Ok(ref params) => {
                format!(
                    include_str!("templates/response_success.xml"),
                    params = params
                        .iter()
                        .map(|param| format!("<param>{}</param>", param.to_xml()))
                        .collect::<String>()
                )
            }
            Err(Fault { code, ref message }) => {
                format!(
                    include_str!("templates/response_fault.xml"),
                    code = code,
                    message = message
                )
            }
        }
    }
}

impl ToXml for Value {
    fn to_xml(&self) -> String {
        match *self {
            Value::Int(v) => format!("<value><i4>{}</i4></value>", v),
            Value::Bool(v) => {
                format!(
                    "<value><boolean>{}</boolean></value>",
                    if v { 1 } else { 0 }
                )
            }
            Value::String(ref v) => {
                format!("<value><string>{}</string></value>", escape_str_pcdata(v))
            }
            Value::Double(v) => format!("<value><double>{}</double></value>", v),
            Value::DateTime(ref v) => {
                format!("<value><dateTime.iso8601>{}</dateTime.iso8601></value>", v)
            }
            Value::Base64(ref v) => {
                format!("<value><base64>{}</base64></value>", base64::encode(v))
            }
            Value::Array(ref v) => {
                format!(
                    "<value><array><data>{}</data></array></value>",
                    v.iter().map(Value::to_xml).collect::<String>()
                )
            }
            Value::Struct(ref v) => {
                format!(
                    "<value><struct>{}</struct></value>",
                    v.iter()
                        .map(|(key, value)| {
                            format!("<member><name>{}</name>{}</member>", key, value.to_xml())
                        })
                        .collect::<String>()
                )
            }
        }

    }
}