openalex/api_entities/
mod.rs

1pub mod author;
2pub mod common_types;
3pub mod concept;
4pub mod funder;
5pub mod institution;
6pub mod keyword;
7pub mod publisher;
8pub mod source;
9pub mod topic;
10pub mod work;
11
12use reqwest::blocking::{Client, Response};
13
14use crate::{
15    prelude::*,
16    utils::{filter::Filter, sort::Sort},
17};
18
19#[macro_export]
20macro_rules! impl_try_from_for_single_entity {
21    ($entity:ty) => {
22        impl TryFrom<reqwest::blocking::Response> for $entity {
23            type Error = $crate::error::Error;
24
25            fn try_from(response: reqwest::blocking::Response) -> $crate::prelude::Result<Self> {
26                match response.status() {
27                    reqwest::StatusCode::OK => {
28                        let res = response.json::<Self>();
29                        match res {
30                            Ok(entity) => Ok(entity),
31                            Err(e) => Err($crate::error::Error::Generic(format!(
32                                "Error deserializing object. Original Message: {}",
33                                e
34                            ))),
35                        }
36                    }
37                    reqwest::StatusCode::NOT_FOUND => {
38                        let oa_error = $crate::error::OpenAlexError {
39                            error: "Document not found".to_string(),
40                            message: "The document with the specified id was not found. HTTP 404"
41                                .to_string(),
42                        };
43                        Err($crate::error::Error::OpenAlex(oa_error))
44                    }
45                    _ => Err($crate::error::Error::Generic(format!(
46                        "Unknown Error. Response Code was {}",
47                        response.status()
48                    ))),
49                }
50            }
51        }
52    };
53}
54
55#[macro_export]
56macro_rules! impl_try_from_for_entity_response {
57    ($entity_response:ty) => {
58        impl TryFrom<reqwest::blocking::Response> for $entity_response {
59            type Error = $crate::error::Error;
60
61            fn try_from(response: reqwest::blocking::Response) -> $crate::prelude::Result<Self> {
62                match response.status() {
63                    reqwest::StatusCode::OK => {
64                        let res = response.json::<Self>();
65                        match res {
66                            Ok(author_response) => Ok(author_response),
67                            Err(e) => Err($crate::error::Error::Generic(format!(
68                                "Error deserializing Work object. Original Message: {}",
69                                e
70                            ))),
71                        }
72                    }
73                    reqwest::StatusCode::FORBIDDEN => {
74                        let res = response.json::<$crate::error::OpenAlexError>();
75                        match res {
76                            Ok(oa_error) => Err($crate::error::Error::OpenAlex(oa_error)),
77                            Err(e) => Err($crate::error::Error::Generic(format!(
78                                "Error deserializing OpenAlexError object. Original Message: {}",
79                                e
80                            ))),
81                        }
82                    }
83                    _ => Err($crate::error::Error::Generic(format!(
84                        "Unknown Error. Response Code was {}",
85                        response.status()
86                    ))),
87                }
88            }
89        }
90    };
91}
92
93pub trait APIEntity<EntityType, ResponseType>
94where
95    EntityType: TryFrom<Response, Error = Error>,
96    ResponseType: TryFrom<Response, Error = Error>,
97{
98    const API_URL: &'static str;
99
100    #[allow(clippy::new_ret_no_self)]
101    fn new(id: &str) -> Result<EntityType> {
102        let url = format!("{}/{}", Self::API_URL, id);
103        let response = reqwest::blocking::get(url)?;
104        response.try_into()
105    }
106    fn get_samples(number_of_samples: u32, seed: impl Into<String>) -> Result<ResponseType> {
107        let client = Client::new();
108        let response = client
109            .get(Self::API_URL)
110            .query(&[
111                ("sample", number_of_samples.to_string()),
112                ("seed", seed.into()),
113            ])
114            .send()?;
115        response.try_into()
116    }
117    fn filter(filter: Filter, page: u32, per_page: u32, sort: Sort) -> Result<ResponseType> {
118        let client = Client::new();
119        let response = client
120            .get(Self::API_URL)
121            .query(&[
122                ("filter", filter.to_string()),
123                ("page", page.to_string()),
124                ("per-page", per_page.to_string()),
125                ("sort", sort.to_string()),
126            ])
127            .send()?;
128        response.try_into()
129    }
130
131    fn search(
132        search: impl Into<String>,
133        page: u32,
134        per_page: u32,
135        sort: Sort,
136    ) -> Result<ResponseType> {
137        let client = Client::new();
138        let response = client
139            .get(Self::API_URL)
140            .query(&[
141                ("search", search.into()),
142                ("page", page.to_string()),
143                ("per-page", per_page.to_string()),
144                ("sort", sort.to_string()),
145            ])
146            .send()?;
147
148        response.try_into()
149    }
150}