uprs 0.1.0

A wrapper for the Up Bank API.
Documentation
use std::collections::HashMap;
use async_trait::async_trait;
use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde::Deserialize;

#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct ApiResponse<T> {
    pub data: T,
    pub links: Option<HashMap<String, Option<String>>>,
}

impl<T> ApiResponse<T> {
    pub(crate) fn has_next(api_response: &ApiResponse<T>) -> bool {
        return match &api_response.links {
            None => false,
            Some(v) => match v.get("next") {
                None => false,
                Some(v2) => v2.is_some(),
            },
        };
    }
}

#[async_trait]
pub trait ApiRequest {
    type T;
    async fn send(self) -> Result<Self::T, String>;
    fn get_url(&self) -> &String;
    fn get_params(&self) -> &Vec<(String, String)>;
    fn get_api_key(&self) -> &String;
}

pub(crate) struct RequestSender {}

impl RequestSender {
    pub(crate) async fn send_paginate<T: DeserializeOwned, K: ApiRequest>(
        base: K,
    ) -> Result<Vec<T>, String> {
        let client = reqwest::Client::new();
        let res = client
            .get(base.get_url())
            .bearer_auth(base.get_api_key())
            .query(base.get_params())
            .send()
            .await
            .unwrap();

        return match res.status() {
            StatusCode::OK => {
                let mut rtn: Vec<T> = Vec::new();
                let mut json: ApiResponse<Vec<T>> = res.json().await.unwrap();
                rtn.append(&mut json.data);
                while ApiResponse::has_next(&json) {
                    let next = json
                        .links
                        .as_ref()
                        .unwrap()
                        .get("next")
                        .unwrap()
                        .to_owned()
                        .unwrap();
                    let client = reqwest::Client::new();
                    let res = client
                        .get(next)
                        .bearer_auth(base.get_api_key())
                        .send()
                        .await
                        .unwrap();
                    match res.status() {
                        StatusCode::OK => {
                            json = res.json().await.unwrap();
                            rtn.append(&mut json.data);
                        }
                        _ => {
                            return Err(res.text().await.unwrap());
                        }
                    }
                }
                Ok(rtn)
            }
            _ => Err(res.text().await.unwrap()),
        };
    }

    pub async fn send<T: DeserializeOwned, K: ApiRequest>(base: K) -> Result<T, String> {
        let client = reqwest::Client::new();
        let res = client
            .get(base.get_url())
            .bearer_auth(base.get_api_key())
            .query(base.get_params())
            .send()
            .await
            .unwrap();

        return match res.status() {
            StatusCode::OK => {
                let json: ApiResponse<T> = res.json().await.unwrap();
                Ok(json.data)
            }
            _ => Err(res.text().await.unwrap()),
        };
    }
}

#[cfg(test)]
mod tests {
    use crate::models;
    use std::fs;

    #[tokio::test]
    async fn response_has_next() {
        let json: String = fs::read_to_string("tests/example_json/list_accounts.json").unwrap();
        let des: models::ApiResponse<Vec<models::Account>> = serde_json::from_str(&*json).unwrap();
        assert_eq!(models::ApiResponse::has_next(&des), true);
    }
}