pushover 0.4.0

A wrapper for the Pushover API.
Documentation
use url::form_urlencoded;
use url::Url;

use std::time::Duration;

use crate::error::Error;
use crate::requests::{Request, Response};

pub const API_URL: &'static str = "https://api.pushover.net";
pub const API_VERSION: &'static str = "1";
const DEFAULT_TIMEOUT: u64 = 30;

pub struct API {
    base_url: String,
    timeout: Duration,
}

impl API {
    pub fn new() -> Self {
        Self {
            timeout: Duration::from_secs(DEFAULT_TIMEOUT),
            base_url: API_URL.to_owned(),
        }
    }

    pub fn timeout(mut self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    pub fn base_url(mut self, url: &str) -> Self {
        self.base_url = url.to_owned();
        self
    }

    pub fn send<R: Request>(&self, request: &R) -> Result<<R as Request>::ResponseType, Error> {
        let mut url = Url::parse(&self.base_url).unwrap();
        url.set_path(API_VERSION);

        let client_builder = reqwest::blocking::ClientBuilder::new().timeout(self.timeout);

        request.build_url(&mut url);

        let req = client_builder.build()?.request(request.get_method(), url);

        let req = if let Some(params) = request.get_form_parameters() {
            let encoded: String = form_urlencoded::Serializer::new(String::new())
                .extend_pairs(params.into_iter())
                .finish();

            req.body(encoded)
        } else {
            req
        };

        let res = req.send()?;

        res.json()
            .map_err(From::from)
            .and_then(|value| match value {
                Response::Success::<R>(raw) => Ok(R::map(raw)),
                Response::Error::<R>(err) => Err(err.into()),
            })
    }

    pub async fn send_async<R: Request>(
        &self,
        request: &R,
    ) -> Result<<R as Request>::ResponseType, Error> {
        let mut url = Url::parse(&self.base_url).unwrap();
        url.set_path(API_VERSION);

        let client_builder = reqwest::ClientBuilder::new().timeout(self.timeout);

        request.build_url(&mut url);

        let req = client_builder.build()?.request(request.get_method(), url);

        let req = if let Some(params) = request.get_form_parameters() {
            let encoded: String = form_urlencoded::Serializer::new(String::new())
                .extend_pairs(params.into_iter())
                .finish();

            req.body(encoded)
        } else {
            req
        };

        let res = req.send().await?;

        res.json()
            .await
            .map_err(From::from)
            .and_then(|value| match value {
                Response::Success::<R>(raw) => Ok(R::map(raw)),
                Response::Error::<R>(err) => Err(err.into()),
            })
    }
}