135 lines
3.4 KiB
Rust
Raw Normal View History

2018-05-03 22:54:56 -05:00
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<Image, Error> {
Ok(self.make_call("/breeds/image/random")?.message)
}
pub fn breeds(&self) -> Result<Breeds, Error> {
Ok(self.make_call("/breeds/list/all")?.message)
}
pub fn subbreeds(&self, breed: &str) -> Result<Subbreeds, Error> {
Ok(self.make_call(&format!("/breed/{}/list", breed))?.message)
}
pub fn images_by_breed(&self, breed: &str) -> Result<Images, Error> {
Ok(self.make_call(&format!("/breed/{}/images", breed))?.message)
}
pub fn random_image_by_breed(&self, breed: &str) -> Result<Image, Error> {
Ok(self.make_call(&format!("/breed/{}/images/random", breed))?.message)
}
pub fn images_by_subbreed(&self, breed: &str, subbreed: &str) -> Result<Images, Error> {
Ok(self.make_call(&format!("/breed/{}/{}/images", breed, subbreed))?.message)
}
pub fn random_image_by_subbreed(&self, breed: &str, subbreed: &str) -> Result<Image, Error> {
Ok(self.make_call(&format!("/breed/{}/{}/images/random", breed, subbreed))?.message)
}
fn make_call<T>(&self, endpoint: &str) -> Result<Response<T>, 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<T> = 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<Image>;
pub type Subbreeds = Vec<String>;
pub type Breeds = HashMap<Breed, Subbreeds>;
#[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<String>
}
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<T> {
status: String,
message: T
}
impl<T> Response<T> {
pub fn is_success(&self) -> bool {
self.status == "success"
}
}