mal 0.8.1

A library to interact with the MyAnimeList API.
Documentation
use error::RequestError;
use MAL;
use reqwest::{RequestBuilder, Response, StatusCode, Url};
use reqwest::header::{ContentType, Headers};

lazy_static! {
    static ref BASE_URL: Url = Url::parse("https://myanimelist.net").unwrap();
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ListType {
    Anime,
    Manga,
}

pub type ID = u32;
pub type Username<'a> = &'a str;
pub type Name<'a> = &'a str;
pub type Body<'a> = &'a str;

#[derive(Debug)]
pub enum Request<'a> {
    Search(Name<'a>, ListType),
    List(Username<'a>, ListType),
    Add(ID, ListType, Body<'a>),
    Update(ID, ListType, Body<'a>),
    Delete(ID, ListType),
    VerifyCredentials,
}

impl<'a> Request<'a> {
    fn send_req(self, mal: &MAL) -> Result<Response, RequestError> {
        let mut url = BASE_URL.clone();
        use self::Request::*;

        match self {
            Search(name, list_type) => {
                match list_type {
                    ListType::Anime => url.set_path("/api/anime/search.xml"),
                    ListType::Manga => url.set_path("/api/manga/search.xml"),
                }

                url.query_pairs_mut().append_pair("q", name);
                mal.client.get(url).with_auth(mal).send_req()
            }
            List(uname, list_type) => {
                url.set_path("/malappinfo.php");

                let query = match list_type {
                    ListType::Anime => "anime",
                    ListType::Manga => "manga",
                };

                url.query_pairs_mut()
                    .append_pair("u", uname)
                    .append_pair("status", "all")
                    .append_pair("type", query);

                mal.client.get(url).send_req()
            }
            Add(id, list_type, body) => {
                match list_type {
                    ListType::Anime => url.set_path(&format!("/api/animelist/add/{}.xml", id)),
                    ListType::Manga => url.set_path(&format!("/api/mangalist/add/{}.xml", id)),
                }

                mal.client
                    .post(url)
                    .with_body(body)
                    .with_auth(mal)
                    .send_req()
            }
            Update(id, list_type, body) => {
                match list_type {
                    ListType::Anime => url.set_path(&format!("/api/animelist/update/{}.xml", id)),
                    ListType::Manga => url.set_path(&format!("/api/mangalist/update/{}.xml", id)),
                }

                mal.client
                    .post(url)
                    .with_body(body)
                    .with_auth(mal)
                    .send_req()
            }
            Delete(id, list_type) => {
                match list_type {
                    ListType::Anime => url.set_path(&format!("/api/animelist/delete/{}.xml", id)),
                    ListType::Manga => url.set_path(&format!("/api/mangalist/delete/{}.xml", id)),
                }

                mal.client.delete(url).with_auth(mal).send_req()
            }
            VerifyCredentials => {
                url.set_path("/api/account/verify_credentials.xml");
                mal.client.get(url).with_auth(mal).send_req()
            }
        }
    }

    pub fn send(self, mal: &MAL) -> Result<String, RequestError> {
        self.send_req(mal)?
            .text()
            .map_err(RequestError::ReadResponse)
    }
}

trait RequestExt {
    fn with_auth(&mut self, mal: &MAL) -> &mut RequestBuilder;
    fn with_body(&mut self, body: &str) -> &mut RequestBuilder;

    fn send_req(&mut self) -> Result<Response, RequestError>;
}

impl RequestExt for RequestBuilder {
    fn with_auth(&mut self, mal: &MAL) -> &mut RequestBuilder {
        self.basic_auth(mal.username.clone(), Some(mal.password.clone()))
    }

    fn with_body(&mut self, body: &str) -> &mut RequestBuilder {
        let mut headers = Headers::new();
        headers.set(ContentType::form_url_encoded());

        self.body(format!("data={}", body)).headers(headers)
    }

    fn send_req(&mut self) -> Result<Response, RequestError> {
        let resp = self.send().map_err(RequestError::HttpError)?;

        match resp.status() {
            StatusCode::Ok | StatusCode::Created => Ok(resp),
            status => Err(RequestError::BadResponseCode(status)),
        }
    }
}