tfl_api_wrapper/
request.rs

1use crate::client::Client;
2use crate::models;
3use async_trait::async_trait;
4use reqwest::StatusCode;
5use serde::de::DeserializeOwned;
6
7const ROOT_URL: &str = "https://api.tfl.gov.uk";
8
9#[derive(Debug)]
10pub struct Error {
11    pub api_error: Option<models::ApiException>,
12    pub http_status: StatusCode,
13}
14
15impl Error {
16    pub fn new(api_error: Option<models::ApiException>, http_status: StatusCode) -> Self {
17        Self {
18            api_error,
19            http_status,
20        }
21    }
22    pub fn from_status(http_status: StatusCode) -> Self {
23        Self {
24            api_error: None,
25            http_status,
26        }
27    }
28}
29
30#[async_trait]
31pub trait RequestBuilder {
32    type Response: DeserializeOwned + std::fmt::Debug;
33
34    fn get_request_url(&self) -> String;
35    fn get_client(&self) -> &Client;
36    fn get_parameters(&self) -> &models::Parameters;
37    async fn fetch(&self) -> Result<Self::Response, Error> {
38        let url = format!("{}{}", ROOT_URL, self.get_request_url());
39        // println!("Request: {}", url);
40        let auth_params: Vec<(String, String)> =
41            vec![("app_key".into(), self.get_client().app_key.clone())];
42
43        let request = self.get_client().req_client.get(url).query(&auth_params);
44
45        let response = request
46            .send()
47            .await
48            .map_err(|e| Error::from_status(e.status().unwrap_or(StatusCode::BAD_REQUEST)))?;
49
50        let status = response.status();
51        // todo: implement an appropriate logging solution
52        // println!("{:?}", response);
53        if status != StatusCode::OK {
54            return Err(Error::new(
55                response.json::<models::ApiException>().await.ok(),
56                status,
57            ));
58        }
59
60        let t = response.text().await.expect("No body");
61        // println!("Response: {}", t);
62
63        serde_json::from_str::<Self::Response>(&t).map_err(|e| {
64            //response.json::<Self::Response>().await.map_err(|e| {
65            println!("{e:#?}");
66            Error::from_status(StatusCode::BAD_REQUEST)
67        })
68    }
69}
70
71macro_rules! create_endpoint {
72    ($name: ident) => {
73        pub struct $name<'a> {
74            client: &'a Client,
75            parameters: models::Parameters,
76        }
77        impl<'a> $name<'a> {
78            pub(crate) fn new(client: &'a Client) -> Self {
79                Self {
80                    client,
81                    parameters: Default::default(),
82                }
83            }
84        }
85    };
86}
87
88pub(crate) use create_endpoint;
89
90create_endpoint!(VersionRequest);
91
92impl RequestBuilder for VersionRequest<'_> {
93    type Response = models::Version;
94
95    fn get_request_url(&self) -> String {
96        "/version".into()
97    }
98
99    fn get_parameters(&self) -> &models::Parameters {
100        &self.parameters
101    }
102
103    fn get_client(&self) -> &Client {
104        self.client
105    }
106}