tfl_api_wrapper/
request.rs1use 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 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 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 serde_json::from_str::<Self::Response>(&t).map_err(|e| {
64 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}