extern crate serde; extern crate serde_xml_rs; #[macro_use] extern crate serde_derive; extern crate reqwest; #[macro_use] extern crate derive_error; use std::io::Read; use std::fmt; use std::fmt::{Display, Formatter}; pub struct Cats { url: String } impl Cats { pub fn new() -> Cats { Cats::with_url("http://thecatapi.com/api/") } pub fn with_url(url: &str) -> Cats { let mut api_url = url.to_owned(); if api_url.ends_with("/") { api_url.remove(url.len() - 1); } Cats { url: api_url } } pub fn random_image(&self) -> Result { let mut response : Response = self.make_call("/images/get?format=xml")?; Ok(response.data.images.images.remove(0)) } pub fn categories(&self) -> Result { let response : Response = self.make_call("/categories/list?format=xml")?; Ok(response.data.categories) } pub fn random_image_of_category(&self, category: &str) -> Result { let mut response : Response = self.make_call(&format!("/images/get?format=xml&category={}", category))?; Ok(response.data.images.images.remove(0)) } fn make_call(&self, endpoint: &str) -> Result, Error> where for <'a> T: serde::Deserialize<'a> { let mut response = reqwest::get(&format!( "{}{}", self.url, endpoint ))?; let mut content = String::new(); response.read_to_string(&mut content)?; let parsed_response: Response = serde_xml_rs::deserialize(content.as_bytes())?; Ok(parsed_response) } } #[derive(Debug, Error)] pub enum Error { /// HTTP request failed HTTP(reqwest::Error), /// IO error IO(std::io::Error), /// Failed to parse Parse(serde_xml_rs::Error), /// Error from API API(APIError) } #[derive(Serialize, Deserialize, Debug)] pub struct APIError { pub status: String, pub code: String, pub message: Option } impl std::error::Error for APIError { fn description(&self) -> &str { self.message.as_ref().map(|message| message.as_str()).unwrap_or("") } fn cause(&self) -> Option<&std::error::Error> { None } } impl Display for APIError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self.message { Some(ref message) => write!(f, "{}: {}", self.code, message), None => write!(f, "{}", self.code) } } } #[derive(Serialize, Deserialize, Debug)] struct Response { data: T } #[derive(Serialize, Deserialize, Debug)] struct ImagesData { images: Images } #[derive(Serialize, Deserialize, Debug)] pub struct Images { #[serde(rename="image")] images: Vec } #[derive(Serialize, Deserialize, Debug)] pub struct Image { id: String, url: String, source_url: String } #[derive(Serialize, Deserialize, Debug)] struct CategoriesData { categories: Categories } #[derive(Serialize, Deserialize, Debug)] pub struct Categories { #[serde(rename="category")] categories: Vec } #[derive(Serialize, Deserialize, Debug)] pub struct Category { id: String, name: String } mod test { use serde_xml_rs; use {Image, Images}; #[test] fn test_deserialize_cat () { let xml = r#" http://25.media.tumblr.com/tumblr_m99be6qgNT1qejbiro1_500.jpg MjA0MjYyMA http://thecatapi.com/?id=MjA0MjYyMA "#; let cat: Image = serde_xml_rs::deserialize(xml.as_bytes()).unwrap(); } #[test] fn test_deserialize_cats () { let xml = r#" http://25.media.tumblr.com/tumblr_m99be6qgNT1qejbiro1_500.jpg MjA0MjYyMA http://thecatapi.com/?id=MjA0MjYyMA "#; let cats: Images = serde_xml_rs::deserialize(xml.as_bytes()).unwrap(); } }