166 lines
4.1 KiB
Rust
166 lines
4.1 KiB
Rust
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<Image, Error> {
|
|
let mut response : Response<ImagesData> = self.make_call("/images/get?format=xml")?;
|
|
Ok(response.data.images.images.remove(0))
|
|
}
|
|
|
|
pub fn categories(&self) -> Result<Categories, Error> {
|
|
let response : Response<CategoriesData> = self.make_call("/categories/list?format=xml")?;
|
|
Ok(response.data.categories)
|
|
}
|
|
|
|
pub fn random_image_of_category(&self, category: &str) -> Result<Image, Error> {
|
|
let mut response : Response<ImagesData> = self.make_call(&format!("/images/get?format=xml&category={}", category))?;
|
|
Ok(response.data.images.images.remove(0))
|
|
}
|
|
|
|
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_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<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> {
|
|
data: T
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
struct ImagesData {
|
|
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();
|
|
}
|
|
}
|