1use reqwest::IntoUrl;
16use reqwest::StatusCode;
17use reqwest::Url;
18
19use crate::Error;
20
21#[derive(Debug, Clone)]
22pub struct ClientFactory {
23 client: reqwest::Client,
24}
25
26impl ClientFactory {
27 pub fn new() -> Result<Self, Error> {
28 let client = reqwest::ClientBuilder::new()
29 .no_proxy()
30 .build()
31 .map_err(Error::Http)?;
32 Ok(Self { client })
33 }
34
35 pub fn make_client(&self, endpoint: String) -> Result<Client, Error> {
36 Client::new(endpoint, self.client.clone())
37 }
38}
39
40pub struct Client {
41 client: reqwest::Client,
42 base_url: Url,
43}
44
45impl Client {
46 pub async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, Error> {
47 do_get(self, key).await
48 }
49
50 pub async fn put(&self, key: &str, value: &[u8]) -> Result<(), Error> {
51 do_put(self, key, value).await
52 }
53
54 pub async fn delete(&self, key: &str) -> Result<(), Error> {
55 do_delete(self, key).await
56 }
57
58 fn new(base_url: impl IntoUrl, client: reqwest::Client) -> Result<Self, Error> {
59 let base_url = base_url.into_url().map_err(Error::Http)?;
60 Ok(Client { client, base_url })
61 }
62}
63
64async fn do_get(client: &Client, key: &str) -> Result<Option<Vec<u8>>, Error> {
65 let url = client
66 .base_url
67 .join(key)
68 .map_err(|e| Error::Other(e.to_string()))?;
69
70 let resp = client.client.get(url).send().await.map_err(Error::Http)?;
71
72 match resp.status() {
73 StatusCode::NOT_FOUND => Ok(None),
74 StatusCode::OK => {
75 let body = resp.bytes().await.map_err(Error::Http)?;
76 Ok(Some(body.to_vec()))
77 }
78 StatusCode::TOO_MANY_REQUESTS => Err(Error::TooManyRequests),
79 _ => Err(Error::Other(resp.status().to_string())),
80 }
81}
82
83async fn do_put(client: &Client, key: &str, value: &[u8]) -> Result<(), Error> {
84 let url = client
85 .base_url
86 .join(key)
87 .map_err(|e| Error::Other(e.to_string()))?;
88
89 let resp = client
90 .client
91 .put(url)
92 .body(value.to_vec())
93 .send()
94 .await
95 .map_err(Error::Http)?;
96
97 match resp.status() {
98 StatusCode::OK | StatusCode::CREATED => Ok(()),
99 StatusCode::TOO_MANY_REQUESTS => Err(Error::TooManyRequests),
100 status => Err(Error::Other(status.to_string())),
101 }
102}
103
104async fn do_delete(client: &Client, key: &str) -> Result<(), Error> {
105 let url = client
106 .base_url
107 .join(key)
108 .map_err(|e| Error::Other(e.to_string()))?;
109
110 let resp = client
111 .client
112 .delete(url)
113 .send()
114 .await
115 .map_err(Error::Http)?;
116
117 match resp.status() {
118 StatusCode::OK | StatusCode::NO_CONTENT => Ok(()),
119 StatusCode::TOO_MANY_REQUESTS => Err(Error::TooManyRequests),
120 status => Err(Error::Other(status.to_string())),
121 }
122}