ajars_reqwest/
lib.rs

1use std::{convert::TryFrom, marker::PhantomData};
2
3use crate::reqwest::{Client, RequestBuilder as ReqwestRequestBuilder};
4use ::reqwest::header::{HeaderName, HeaderValue};
5use ajars_core::{HttpMethod, RestType};
6use http::HeaderMap;
7use serde::{de::DeserializeOwned, Serialize};
8
9pub mod reqwest {
10    pub use reqwest::*;
11}
12
13#[derive(Clone)]
14pub struct AjarsReqwest {
15    client: Client,
16    base_url: String,
17}
18
19impl AjarsReqwest {
20    pub fn new<S: Into<String>>(client: Client, base_url: S) -> Self {
21        Self { client, base_url: base_url.into() }
22    }
23
24    pub fn request<'a, I: Serialize + DeserializeOwned, O: Serialize + DeserializeOwned, REST: RestType<I, O>>(
25        &self,
26        rest: &'a REST,
27    ) -> RequestBuilder<'a, I, O, REST> {
28        let url = format!("{}{}", &self.base_url, rest.path());
29
30        let request = match rest.method() {
31            HttpMethod::DELETE => self.client.delete(&url),
32            HttpMethod::GET => self.client.get(&url),
33            HttpMethod::POST => self.client.post(&url),
34            HttpMethod::PUT => self.client.put(&url),
35        };
36
37        RequestBuilder { rest, request, phantom_i: PhantomData, phantom_o: PhantomData }
38    }
39}
40
41pub struct RequestBuilder<'a, I: Serialize + DeserializeOwned, O: Serialize + DeserializeOwned, REST: RestType<I, O>> {
42    rest: &'a REST,
43    request: ReqwestRequestBuilder,
44    phantom_i: PhantomData<I>,
45    phantom_o: PhantomData<O>,
46}
47
48impl<'a, I: Serialize + DeserializeOwned, O: Serialize + DeserializeOwned, REST: RestType<I, O>>
49    RequestBuilder<'a, I, O, REST>
50{
51    /// Sends the Request to the target URL, returning a
52    /// future Response.
53    pub async fn send(self, data: &I) -> Result<O, reqwest::Error> {
54        let request = match self.rest.method() {
55            HttpMethod::DELETE | HttpMethod::GET => self.request.query(data),
56            HttpMethod::POST | HttpMethod::PUT => self.request.header("Content-Type", "application/json").json(data),
57        };
58
59        request.send().await?.json().await
60    }
61
62    /// Add a `Header` to this Request.
63    pub fn header<K, V>(mut self, key: K, value: V) -> Self
64    where
65        HeaderName: TryFrom<K>,
66        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
67        HeaderValue: TryFrom<V>,
68        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
69    {
70        self.request = self.request.header(key, value);
71        self
72    }
73
74    /// Add a set of Headers to the existing ones on this Request.
75    ///
76    /// The headers will be merged in to any already set.
77    pub fn headers(mut self, headers: HeaderMap) -> Self {
78        self.request = self.request.headers(headers);
79        self
80    }
81
82    #[cfg(not(target_arch = "wasm32"))]
83    /// Enable HTTP basic authentication.
84    pub fn basic_auth<U, P>(mut self, username: U, password: Option<P>) -> Self
85    where
86        U: std::fmt::Display,
87        P: std::fmt::Display,
88    {
89        self.request = self.request.basic_auth(username, password);
90        self
91    }
92
93    /// Enable HTTP bearer authentication.
94    pub fn bearer_auth<T>(mut self, token: T) -> Self
95    where
96        T: std::fmt::Display,
97    {
98        self.request = self.request.bearer_auth(token);
99        self
100    }
101
102    #[cfg(not(target_arch = "wasm32"))]
103    /// Enables a request timeout.
104    ///
105    /// The timeout is applied from when the request starts connecting until the
106    /// response body has finished. It affects only this request and overrides
107    /// the timeout configured using `ClientBuilder::timeout()`.
108    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
109        self.request = self.request.timeout(timeout);
110        self
111    }
112}