crates-api 0.1.0

Call crates.io API from rust
#![crate_type = "lib"]
#![crate_name = "crates_api"]

#![feature(proc_macro)]

extern crate hyper;
extern crate serde;
extern crate serde_json;
#[macro_use] extern crate serde_derive;

use hyper::Url;
use hyper::Error as HyperError;
use hyper::client::Client;
use hyper::error::ParseError;
use hyper::header::{Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};

use serde_json::Error as JsonError;

use serde::Deserialize;

use std::io::Read;

#[derive(Debug, Deserialize)]
pub struct CrateData {
    #[serde(rename="crate")]
    pub metadata : CrateMetadata,
    pub versions : Vec<CrateVersion>,
}

#[derive(Debug, Deserialize)]
pub struct CrateMetadata {
    pub name : String,
    pub id : String,
    pub max_version : String,
    pub description : String,
    pub license : String,
    pub downloads : usize,
    pub keywords : Vec<String>,
    pub homepage : Option<String>,
    pub documentation : Option<String>,
    pub repository : Option<String>,
    pub versions : Vec<usize>,
}

#[derive(Debug, Deserialize)]
pub struct CrateVersion {
    #[serde(rename="crate")]
    pub name : String,
    pub downloads : usize,
    pub yanked : bool,
    pub id : usize,
    pub num : String,
}

#[derive(Debug, Deserialize)]
struct CrateVersions_ {
    pub versions : Vec<CrateVersion>,
}

#[derive(Debug)]
pub enum CratesApiError {
    UrlError(ParseError),
    RequestError(HyperError),
    DeserializationError(JsonError),
}

pub fn poll_api <T : Deserialize> (path : &str) -> Result<T, CratesApiError> {
    let full_path = format!("https://crates.io/api/v1{}", path);

    let url = match Url::parse(&full_path) {
        Ok(url) => url,
        Err(error) => {
            return Err(CratesApiError::UrlError(error));
        },
    };

    let client = Client::new();

    let request = client
        .get(url)
        .header(Accept(vec![
            qitem(Mime(TopLevel::Application, SubLevel::Json, vec![]))
        ]))
        .send();

    let mut response = match request {
        Ok(res) => res,
        Err(error) => {
            return Err(CratesApiError::RequestError(error));
        },
    };

    let mut body = String::new();
    let _ = response.read_to_string(&mut body);

    let api_result : T = match serde_json::from_str(&body) {
        Ok(data) => data,
        Err(deserialization_error) => {
            return Err(CratesApiError::DeserializationError(deserialization_error));
        },
    };

    return Ok(api_result);
}

pub fn get_crate (name : &str) -> Result<CrateData, CratesApiError> {
    let path = format!("/crates/{}", name);

    return poll_api(&path);
}

pub fn get_versions (name : &str) -> Result<Vec<CrateVersion>, CratesApiError> {
    let path = format!("/crates/{}/versions", name);

    return poll_api(&path).map(|c : CrateVersions_| c.versions);
}