coinapi 1.1.2

A simple Library to interact with the CoinAPI api.
Documentation
use std::collections::HashMap;
use std::hash::Hash;
use std::cmp::{Eq, PartialEq};
use std::fmt;

extern crate reqwest;

const URL: &str = "https://rest.coinapi.io/v1/exchangerate";

#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum Coins {
    BTC,
    BCH,
    ETH,
    XMR,
    NANO,
    DOGE,
    CUSTOM(String),
}

impl<'a> fmt::Display for Coins {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match self {
            Self::CUSTOM(x) => x.clone(),
            x => {
                format!("{:?}", x)
            }
        })
    }
}

#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum Currencies {
    USD,
    EUR,
    CUSTOM(String),
}

impl fmt::Display for Currencies {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match self {
            Self::CUSTOM(x) => x.clone(),
            x => {
                format!("{:?}", x)
            }
        })
    }
}

type CoinReturn = Result<f32, String>;

pub struct CoinApi {
    key: String,
    client: reqwest::blocking::Client,
}

impl CoinApi {
    /// Returns a new Coinapi object
    pub fn new(key: String) -> Self {
        Self {
            key,
            client: reqwest::blocking::Client::new(),
        }
    }

    /// Get exchange rate for a currency.
    pub fn get(&mut self, coin: Coins, currency: Currencies) -> CoinReturn {
        self.internal_get(coin, currency)
    }

    fn internal_get(&self, coin: Coins, currency: Currencies) -> CoinReturn {
        self.request_exchange(format!("{}", coin), format!("{}", currency))
    }

    fn request_exchange(&self, coin: String, currency: String) -> CoinReturn {
        let response = self.client.get(format!("{}/{}/{}", URL, coin, currency))
            .header("X-CoinAPI-Key", self.key.clone())
            .send().unwrap()
            .json::<HashMap<String, String>>().unwrap();
        match response.get(&"rate".to_string()) {
            Some(x) => Ok(x.parse::<f32>().unwrap_or(0.0)),
            None => {
                match response.get("error") {
                    Some(x) => Err(x.clone()),
                    None => Err("Neither Error nor Rate found.".to_string())
                }
            }
        }
    }
}