extern crate serde; extern crate serde_json; #[macro_use] extern crate serde_derive; extern crate reqwest; #[macro_use] extern crate derive_error; use std::collections::HashMap; use std::io::Read; use std::fmt; use std::fmt::{Display, Formatter}; pub struct Dogs { url: String } impl Dogs { pub fn new() -> Dogs { Dogs::with_url("https://dog.ceo/api/") } pub fn with_url(url: &str) -> Dogs { let mut api_url = url.to_owned(); if api_url.ends_with("/") { api_url.remove(url.len() - 1); } Dogs { url: api_url } } pub fn random_image(&self) -> Result { Ok(self.make_call("/breeds/image/random")?.message) } pub fn breeds(&self) -> Result { Ok(self.make_call("/breeds/list/all")?.message) } pub fn subbreeds(&self, breed: &str) -> Result { Ok(self.make_call(&format!("/breed/{}/list", breed))?.message) } pub fn images_by_breed(&self, breed: &str) -> Result { Ok(self.make_call(&format!("/breed/{}/images", breed))?.message) } pub fn random_image_by_breed(&self, breed: &str) -> Result { Ok(self.make_call(&format!("/breed/{}/images/random", breed))?.message) } pub fn images_by_subbreed(&self, breed: &str, subbreed: &str) -> Result { Ok(self.make_call(&format!("/breed/{}/{}/images", breed, subbreed))?.message) } pub fn random_image_by_subbreed(&self, breed: &str, subbreed: &str) -> Result { Ok(self.make_call(&format!("/breed/{}/{}/images/random", breed, subbreed))?.message) } 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_json::from_str(&content)?; if parsed_response.is_success() { Ok(parsed_response) } else { Err(Error::API(serde_json::from_str(&content)?)) } } } pub type Breed = String; pub type Image = String; pub type Images = Vec; pub type Subbreeds = Vec; pub type Breeds = HashMap; #[derive(Debug, Error)] pub enum Error { /// HTTP request failed HTTP(reqwest::Error), /// IO error IO(std::io::Error), /// Failed to parse Parse(serde_json::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 { status: String, message: T } impl Response { pub fn is_success(&self) -> bool { self.status == "success" } }