openalex 0.2.2

A rust module to to access metadata from openalex.org
Documentation
pub mod author;
pub mod common_types;
pub mod concept;
pub mod funder;
pub mod institution;
pub mod keyword;
pub mod publisher;
pub mod source;
pub mod topic;
pub mod work;

use reqwest::blocking::{Client, Response};

use crate::{
    prelude::*,
    utils::{filter::Filter, sort::Sort},
};

#[macro_export]
macro_rules! impl_try_from_for_single_entity {
    ($entity:ty) => {
        impl TryFrom<reqwest::blocking::Response> for $entity {
            type Error = $crate::error::Error;

            fn try_from(response: reqwest::blocking::Response) -> $crate::prelude::Result<Self> {
                match response.status() {
                    reqwest::StatusCode::OK => {
                        let res = response.json::<Self>();
                        match res {
                            Ok(entity) => Ok(entity),
                            Err(e) => Err($crate::error::Error::Generic(format!(
                                "Error deserializing object. Original Message: {}",
                                e
                            ))),
                        }
                    }
                    reqwest::StatusCode::NOT_FOUND => {
                        let oa_error = $crate::error::OpenAlexError {
                            error: "Document not found".to_string(),
                            message: "The document with the specified id was not found. HTTP 404"
                                .to_string(),
                        };
                        Err($crate::error::Error::OpenAlex(oa_error))
                    }
                    _ => Err($crate::error::Error::Generic(format!(
                        "Unknown Error. Response Code was {}",
                        response.status()
                    ))),
                }
            }
        }
    };
}

#[macro_export]
macro_rules! impl_try_from_for_entity_response {
    ($entity_response:ty) => {
        impl TryFrom<reqwest::blocking::Response> for $entity_response {
            type Error = $crate::error::Error;

            fn try_from(response: reqwest::blocking::Response) -> $crate::prelude::Result<Self> {
                match response.status() {
                    reqwest::StatusCode::OK => {
                        let res = response.json::<Self>();
                        match res {
                            Ok(author_response) => Ok(author_response),
                            Err(e) => Err($crate::error::Error::Generic(format!(
                                "Error deserializing Work object. Original Message: {}",
                                e
                            ))),
                        }
                    }
                    reqwest::StatusCode::FORBIDDEN => {
                        let res = response.json::<$crate::error::OpenAlexError>();
                        match res {
                            Ok(oa_error) => Err($crate::error::Error::OpenAlex(oa_error)),
                            Err(e) => Err($crate::error::Error::Generic(format!(
                                "Error deserializing OpenAlexError object. Original Message: {}",
                                e
                            ))),
                        }
                    }
                    _ => Err($crate::error::Error::Generic(format!(
                        "Unknown Error. Response Code was {}",
                        response.status()
                    ))),
                }
            }
        }
    };
}

pub trait APIEntity<EntityType, ResponseType>
where
    EntityType: TryFrom<Response, Error = Error>,
    ResponseType: TryFrom<Response, Error = Error>,
{
    const API_URL: &'static str;

    #[allow(clippy::new_ret_no_self)]
    fn new(id: &str) -> Result<EntityType> {
        let url = format!("{}/{}", Self::API_URL, id);
        let response = reqwest::blocking::get(url)?;
        response.try_into()
    }
    fn get_samples(number_of_samples: u32, seed: impl Into<String>) -> Result<ResponseType> {
        let client = Client::new();
        let response = client
            .get(Self::API_URL)
            .query(&[
                ("sample", number_of_samples.to_string()),
                ("seed", seed.into()),
            ])
            .send()?;
        response.try_into()
    }
    fn filter(filter: Filter, page: u32, per_page: u32, sort: Sort) -> Result<ResponseType> {
        let client = Client::new();
        let response = client
            .get(Self::API_URL)
            .query(&[
                ("filter", filter.to_string()),
                ("page", page.to_string()),
                ("per-page", per_page.to_string()),
                ("sort", sort.to_string()),
            ])
            .send()?;
        response.try_into()
    }

    fn search(
        search: impl Into<String>,
        page: u32,
        per_page: u32,
        sort: Sort,
    ) -> Result<ResponseType> {
        let client = Client::new();
        let response = client
            .get(Self::API_URL)
            .query(&[
                ("search", search.into()),
                ("page", page.to_string()),
                ("per-page", per_page.to_string()),
                ("sort", sort.to_string()),
            ])
            .send()?;

        response.try_into()
    }
}