ebay 0.2.0

eBay API client for Rust.
Documentation
use super::types::ResponseMeta;
use reqwest::Response;
use result::*;
pub use xmltree::Element;

pub trait FromXmlElement: Sized + Default {
  fn from_xml_element(elem: &Element) -> EbayResult<Self>;
}

impl FromXmlElement for i64 {
  fn from_xml_element(elem: &Element) -> EbayResult<Self> {
    let v = match elem.text {
      Some(ref text) => text
        .parse()
        .map_err(|err| format!("parse error: {:?}", err))?,
      None => Default::default(),
    };
    Ok(v)
  }
}

impl FromXmlElement for String {
  fn from_xml_element(elem: &Element) -> EbayResult<Self> {
    let v = match elem.text {
      Some(ref text) => text.clone(),
      None => Default::default(),
    };
    Ok(v)
  }
}

impl FromXmlElement for () {
  fn from_xml_element(_elem: &Element) -> EbayResult<()> {
    Ok(())
  }
}

impl<T> FromXmlElement for Vec<T>
where
  T: FromXmlElement,
{
  fn from_xml_element(elem: &Element) -> EbayResult<Self> {
    use std::iter::FromIterator;
    FromIterator::from_iter(elem.children.iter().map(T::from_xml_element))
  }
}

pub struct XmlResponse<T> {
  inner: T,
  meta: ResponseMeta,
  elem: Element,
  text: String,
}

impl<T> ::std::ops::Deref for XmlResponse<T> {
  type Target = T;

  fn deref(&self) -> &Self::Target {
    &self.inner
  }
}

impl<T> XmlResponse<T>
where
  T: FromXmlElement,
{
  pub fn parse(res: &mut Response) -> EbayResult<Self> {
    let text = res.text()?;
    Self::parse_string(text)
  }

  pub fn parse_string(text: String) -> EbayResult<Self> {
    use std::io::Cursor;

    let elem =
      Element::parse(Cursor::new(text.as_bytes())).map_err(|err| EbayError::Deserialize {
        msg: format!("parse response xml: {}", err.to_string()),
        body: text.to_string(),
      })?;

    let meta = ResponseMeta::from_xml_element(&elem).map_err(|err| EbayError::Deserialize {
      msg: format!("parse response meta error: {}", err),
      body: text.to_string(),
    })?;

    if meta.ack == "Failure" {
      use super::types::Error;
      use std::iter::FromIterator;
      let errors: Vec<Error> = EbayResult::<_>::from_iter(
        elem
          .children
          .iter()
          .filter(|elem| elem.name == "Errors")
          .map(Error::from_xml_element),
      )
      .map_err(|err| EbayError::Deserialize {
        msg: format!("parse errors error: {}", err),
        body: text.to_string(),
      })?;
      return Err(EbayError::TradingApiResponseError(errors));
    }

    let inner = T::from_xml_element(&elem)?;

    Ok(XmlResponse {
      inner,
      text,
      elem,
      meta,
    })
  }

  pub fn meta(&self) -> &ResponseMeta {
    &self.meta
  }

  pub fn text(&self) -> &str {
    self.text.as_ref()
  }

  pub fn element(&self) -> &Element {
    &self.elem
  }

  pub fn into_inner(self) -> T {
    self.inner
  }
}

pub trait GetChildText {
  fn get_child_text(&self, name: &str) -> Option<String>;
  fn get_child_text_or_default(&self, name: &str) -> String {
    self.get_child_text(name).unwrap_or_default()
  }
}

impl GetChildText for Element {
  fn get_child_text(&self, name: &str) -> Option<String> {
    self.get_child(name).and_then(|c| c.text.clone())
  }
}

#[macro_export]
macro_rules! ebay_xml_element {
  (
    ATTRS $e:expr, [
      $($attr_name:ident = $attr_value:expr),*
    ]
  ) => {
    $(
      $e.attributes.insert(stringify!($attr_name).to_string(), $attr_value.to_string());
    )*
  };

  (
    CHILDREN $e:expr, [
      $($tag_name:ident $attrs:tt $children:tt)*
    ]
  ) => {
    $(
      $e.children.push(
        ebay_xml_element!($tag_name $attrs $children)
      );
    )*
  };

  (
    CHILDREN $e:expr, [
      $text:expr
    ]
  ) => {
    $e.text = Some($text.to_string());
  };

  (
    $tag_name:ident $attrs:tt $children:tt
  ) => {{
    use $crate::trading::Element;
    let mut elem = Element::new(stringify!($tag_name));
    ebay_xml_element!(ATTRS elem, $attrs);
    ebay_xml_element!(CHILDREN elem, $children);
    elem
  }};
}

#[test]
fn test_xml_element() {
  use std::io::Cursor;
  let a = 1;
  let elem = ebay_xml_element!(
    GetMyeBaySellingRequest[xmlns="urn:ebay:apis:eBLBaseComponents"][
      RequesterCredentials[a=a][
        eBayAuthToken[][
          "AUTH_TOKEN"
        ]
      ]
    ]
  );
  let mut buf: Cursor<Vec<u8>> = Cursor::new(vec![]);
  elem.write(&mut buf).unwrap();
  let xml_text = String::from_utf8(buf.into_inner()).unwrap();
  assert_eq!(xml_text, r##"<?xml version="1.0" encoding="UTF-8"?><GetMyeBaySellingRequest xmlns="urn:ebay:apis:eBLBaseComponents"><RequesterCredentials a="1"><eBayAuthToken>AUTH_TOKEN</eBayAuthToken></RequesterCredentials></GetMyeBaySellingRequest>"##);
}