xml-rpc 0.1.0

Pure Rust implementation of XML-RPC
Documentation
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::de::Unexpected;
use std;
use std::collections::HashMap;
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(i64::from(v)),
            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 {
        use std::fmt::Write;
        format!(
            include_str!("templates/call.xml"),
            name = self.name,
            params = self.params.iter().fold(String::new(), |mut output, param| {
                let _ = write!(output, "<param>{}</param>", param.to_xml());
                output
            }),
        )
    }
}

impl ToXml for Response {
    fn to_xml(&self) -> String {
        use std::fmt::Write;
        match *self {
            Ok(ref params) => format!(
                include_str!("templates/response_success.xml"),
                params = params.iter().fold(String::new(), |mut output, param| {
                    let _ = write!(output, "<param>{}</param>", param.to_xml());
                    output
                })
            ),
            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>", STANDARD.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().fold(String::new(), |mut output, (key, value)| {
                    use std::fmt::Write;
                    let _ = write!(
                        output,
                        "<member><name>{}</name>{}</member>",
                        key,
                        value.to_xml()
                    );
                    output
                })
            ),
        }
    }
}