implement cat api

This commit is contained in:
Adrian Malacoda 2018-05-04 00:39:40 -05:00
parent 22b26f791a
commit e545b130f9
4 changed files with 97 additions and 77 deletions

View File

@ -1,5 +1,5 @@
[package] [package]
name = "wow-such-doge" name = "i-can-has-cat"
version = "0.0.1" version = "0.0.1"
authors = ["A. Malacoda <adrian.malacoda@monarch-pass.net>"] authors = ["A. Malacoda <adrian.malacoda@monarch-pass.net>"]
@ -7,5 +7,5 @@ authors = ["A. Malacoda <adrian.malacoda@monarch-pass.net>"]
reqwest = "0.8.0" reqwest = "0.8.0"
serde = "1.0.14" serde = "1.0.14"
serde_derive = "1.0.14" serde_derive = "1.0.14"
serde_json = "1.0.3" serde-xml-rs = "0.2.1"
derive-error = "0.0.4" derive-error = "0.0.4"

View File

@ -1,17 +1,16 @@
# Wow Such Doge # I Can Has Cat
[Dog API](https://dog.ceo/dog-api/) client in Rust. [The Cat API](http://thecatapi.com/) client in Rust.
## Usage ## Usage
### Command Line ### Command Line
* `cargo run`: output list of breeds * `cargo run`: output list of categories
* `cargo run <breed>` output all dogs of breed * `cargo run random`: output random cat
* `cargo run random`: output random dog * `cargo run random <category>` output random cat of category
* `cargo run random <breed>` output random dog of breed
### Library ### Library
use wow_such_doge::Dogs; use i_can_has_cat::Cats;
let dogs = Dogs::new(); let cats = Cats::new();
let breeds = Dogs::breeds(); let categories = cats.categories();
let random_dog = Dogs::random_image(); let random_cat = cats.random_image();
let random_dog_of_breed = Dogs::random_image_by_breed("<breed>"); let random_cat_of_breed = cats.random_image_of_category("<category>");

View File

@ -1,61 +1,47 @@
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_xml_rs;
#[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_derive;
extern crate reqwest; extern crate reqwest;
#[macro_use] extern crate derive_error; #[macro_use] extern crate derive_error;
use std::collections::HashMap;
use std::io::Read; use std::io::Read;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
pub struct Dogs { pub struct Cats {
url: String url: String
} }
impl Dogs { impl Cats {
pub fn new() -> Dogs { pub fn new() -> Cats {
Dogs::with_url("https://dog.ceo/api/") Cats::with_url("http://thecatapi.com/api/")
} }
pub fn with_url(url: &str) -> Dogs { pub fn with_url(url: &str) -> Cats {
let mut api_url = url.to_owned(); let mut api_url = url.to_owned();
if api_url.ends_with("/") { if api_url.ends_with("/") {
api_url.remove(url.len() - 1); api_url.remove(url.len() - 1);
} }
Dogs { Cats {
url: api_url url: api_url
} }
} }
pub fn random_image(&self) -> Result<Image, Error> { pub fn random_image(&self) -> Result<Image, Error> {
Ok(self.make_call("/breeds/image/random")?.message) let mut response : Response<ImagesData> = self.make_call("/images/get?format=xml")?;
Ok(response.data.images.images.remove(0))
} }
pub fn breeds(&self) -> Result<Breeds, Error> { pub fn categories(&self) -> Result<Categories, Error> {
Ok(self.make_call("/breeds/list/all")?.message) let response : Response<CategoriesData> = self.make_call("/categories/list?format=xml")?;
Ok(response.data.categories)
} }
pub fn subbreeds(&self, breed: &str) -> Result<Subbreeds, Error> { pub fn random_image_of_category(&self, category: &str) -> Result<Image, Error> {
Ok(self.make_call(&format!("/breed/{}/list", breed))?.message) let mut response : Response<ImagesData> = self.make_call(&format!("/images/get?format=xml&category={}", category))?;
} Ok(response.data.images.images.remove(0))
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> fn make_call<T>(&self, endpoint: &str) -> Result<Response<T>, Error>
@ -69,21 +55,11 @@ impl Dogs {
let mut content = String::new(); let mut content = String::new();
response.read_to_string(&mut content)?; response.read_to_string(&mut content)?;
let parsed_response: Response<T> = serde_json::from_str(&content)?; let parsed_response: Response<T> = serde_xml_rs::deserialize(content.as_bytes())?;
if parsed_response.is_success() { Ok(parsed_response)
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)] #[derive(Debug, Error)]
pub enum Error { pub enum Error {
/// HTTP request failed /// HTTP request failed
@ -91,7 +67,7 @@ pub enum Error {
/// IO error /// IO error
IO(std::io::Error), IO(std::io::Error),
/// Failed to parse /// Failed to parse
Parse(serde_json::Error), Parse(serde_xml_rs::Error),
/// Error from API /// Error from API
API(APIError) API(APIError)
} }
@ -123,12 +99,67 @@ impl Display for APIError {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct Response<T> { struct Response<T> {
status: String, data: T
message: T
} }
impl<T> Response<T> { #[derive(Serialize, Deserialize, Debug)]
pub fn is_success(&self) -> bool { struct ImagesData {
self.status == "success" images: Images
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Images {
#[serde(rename="image")]
images: Vec<Image>
}
#[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<Category>
}
#[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#"<image>
<url>http://25.media.tumblr.com/tumblr_m99be6qgNT1qejbiro1_500.jpg</url>
<id>MjA0MjYyMA</id>
<source_url>http://thecatapi.com/?id=MjA0MjYyMA</source_url>
</image>"#;
let cat: Image = serde_xml_rs::deserialize(xml.as_bytes()).unwrap();
}
#[test]
fn test_deserialize_cats () {
let xml = r#"<images>
<image>
<url>http://25.media.tumblr.com/tumblr_m99be6qgNT1qejbiro1_500.jpg</url>
<id>MjA0MjYyMA</id>
<source_url>http://thecatapi.com/?id=MjA0MjYyMA</source_url>
</image>
</images>"#;
let cats: Images = serde_xml_rs::deserialize(xml.as_bytes()).unwrap();
} }
} }

View File

@ -1,27 +1,17 @@
extern crate wow_such_doge; extern crate i_can_has_cat;
use wow_such_doge::Dogs; use i_can_has_cat::Cats;
use std::env; use std::env;
fn main() { fn main() {
let dogs = Dogs::new(); let cats = Cats::new();
match env::args().nth(1).as_ref().map(|arg| arg.as_str()) { match env::args().nth(1).as_ref().map(|arg| arg.as_str()) {
Some("random") => { Some("random") => {
match env::args().nth(2).as_ref().map(|arg| arg.as_str()) { match env::args().nth(2).as_ref().map(|arg| arg.as_str()) {
Some(breed) => { Some(category) => println!("{:?}", cats.random_image_of_category(category).unwrap()),
match env::args().nth(3).as_ref().map(|arg| arg.as_str()) { None => println!("{:?}", cats.random_image().unwrap())
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) => { Some(_) => println!("{:?}", cats.categories()),
match env::args().nth(2).as_ref().map(|arg| arg.as_str()) { None => println!("{:?}", cats.categories())
Some(subbreed) => println!("{:?}", dogs.images_by_subbreed(breed, subbreed).unwrap()),
None => println!("{:?}", dogs.images_by_breed(breed).unwrap())
}
},
None => println!("{:?}", dogs.breeds())
} }
} }