commit 14f905bc4a7b2f7b4d182efe376fcc1228f42db0 Author: Adrian Malacoda Date: Thu May 3 00:31:44 2018 -0500 initial commit diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b85a158 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wow-such-doge" +version = "0.0.1" +authors = ["A. Malacoda "] + +[dependencies] +reqwest = "0.8.0" +serde = "1.0.14" +serde_derive = "1.0.14" +serde_json = "1.0.3" +derive-error = "0.0.4" diff --git a/README.md b/README.md new file mode 100644 index 0000000..06d44b2 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Wow Such Doge +[Dog API](https://dog.ceo/dog-api/) client in Rust. + +## Usage +### Command Line +* `cargo run`: output list of breeds +* `cargo run ` output all dogs of breed +* `cargo run random`: output random dog +* `cargo run random ` output random dog of breed + +### Library + + use wow_such_doge::Dogs; + let dogs = Dogs::new(); + let breeds = Dogs::breeds(); + let random_dog = Dogs::random_image(); + let random_dog_of_breed = Dogs::random_image_by_breed(""); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..709060b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,134 @@ +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)?)) + } + } +} + +type Breed = String; +type Image = String; +type Images = Vec; +type Subbreeds = Vec; +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" + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..de3f092 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +extern crate wow_such_doge; +use wow_such_doge::Dogs; +use std::env; + +fn main() { + let dogs = Dogs::new(); + match env::args().nth(1).as_ref().map(|arg| arg.as_str()) { + Some("random") => { + match env::args().nth(2).as_ref().map(|arg| arg.as_str()) { + Some(breed) => { + match env::args().nth(3).as_ref().map(|arg| arg.as_str()) { + Some(subbreed) => println!("{}", dogs.random_image_by_subbreed(breed, subbreed).unwrap()), + None => println!("{}", dogs.random_image_by_breed(breed).unwrap()) + } + }, + None => println!("{}", dogs.random_image().unwrap()) + } + }, + Some(breed) => { + match env::args().nth(2).as_ref().map(|arg| arg.as_str()) { + Some(subbreed) => println!("{:?}", dogs.images_by_subbreed(breed, subbreed).unwrap()), + None => println!("{:?}", dogs.images_by_breed(breed).unwrap()) + } + }, + None => println!("{:?}", dogs.breeds()) + } +}