roas_arazzo_executor/
client.rs1use crate::http::{
9 AsyncHttpClient, ClientError, HttpClient, HttpRequest, HttpResponse, SendFuture, SleepFuture,
10};
11use std::time::Duration;
12
13#[derive(Clone, Debug)]
18pub struct Client<C> {
19 client: C,
20}
21
22impl<C> Client<C> {
23 pub fn with(client: C) -> Self {
26 Self { client }
27 }
28}
29
30impl Client<reqwest::blocking::Client> {
31 #[must_use]
38 pub fn blocking() -> Self {
39 Self::with(reqwest::blocking::Client::new())
40 }
41}
42
43impl Client<reqwest::Client> {
44 #[must_use]
46 pub fn asynchronous() -> Self {
47 Self::with(reqwest::Client::new())
48 }
49}
50
51impl HttpClient for Client<reqwest::blocking::Client> {
52 fn send(&mut self, request: &HttpRequest) -> Result<HttpResponse, ClientError> {
53 let method = method(request)?;
54 let mut builder = self.client.request(method, &request.url);
55 for (name, value) in &request.headers {
56 builder = builder.header(name, value);
57 }
58 if let Some(body) = &request.body {
59 builder = builder.body(body.clone());
60 }
61 if let Some(timeout) = request.timeout {
62 builder = builder.timeout(timeout);
63 }
64 let response = builder.send().map_err(ClientError::new)?;
65 let status = response.status().as_u16();
66 let headers = headers(response.headers());
67 let body = response.bytes().map_err(ClientError::new)?.to_vec();
68 Ok(HttpResponse {
69 status,
70 headers,
71 body,
72 })
73 }
74}
75
76impl AsyncHttpClient for Client<reqwest::Client> {
77 fn send<'a>(&'a mut self, request: &'a HttpRequest) -> SendFuture<'a> {
78 let client = self.client.clone();
79 Box::pin(async move {
80 let method = method(request)?;
81 let mut builder = client.request(method, &request.url);
82 for (name, value) in &request.headers {
83 builder = builder.header(name, value);
84 }
85 if let Some(body) = &request.body {
86 builder = builder.body(body.clone());
87 }
88 if let Some(timeout) = request.timeout {
89 builder = builder.timeout(timeout);
90 }
91 let response = builder.send().await.map_err(ClientError::new)?;
92 let status = response.status().as_u16();
93 let headers = headers(response.headers());
94 let body = response.bytes().await.map_err(ClientError::new)?.to_vec();
95 Ok(HttpResponse {
96 status,
97 headers,
98 body,
99 })
100 })
101 }
102
103 fn sleep(&self, duration: Duration) -> SleepFuture<'_> {
104 Box::pin(tokio::time::sleep(duration))
105 }
106}
107
108fn method(request: &HttpRequest) -> Result<reqwest::Method, ClientError> {
109 reqwest::Method::from_bytes(request.method.as_bytes()).map_err(ClientError::new)
110}
111
112fn headers(headers: &reqwest::header::HeaderMap) -> Vec<(String, String)> {
113 headers
114 .iter()
115 .map(|(name, value)| {
116 (
117 name.to_string(),
118 value.to_str().unwrap_or_default().to_owned(),
119 )
120 })
121 .collect()
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn a_method_is_read_from_what_the_step_wrote() {
130 let request = HttpRequest {
131 method: "PATCH".to_owned(),
132 ..HttpRequest::default()
133 };
134 assert_eq!(method(&request).unwrap(), reqwest::Method::PATCH);
135 let bad = HttpRequest {
136 method: "not a method".to_owned(),
137 ..HttpRequest::default()
138 };
139 assert!(method(&bad).is_err());
140 }
141
142 #[test]
143 fn headers_come_across_as_pairs() {
144 let mut map = reqwest::header::HeaderMap::new();
145 map.insert("x-thing", "value".parse().expect("a header value"));
146 assert_eq!(headers(&map), [("x-thing".to_owned(), "value".to_owned())]);
147 }
148
149 #[test]
150 fn both_clients_can_be_built() {
151 let _ = Client::blocking();
152 let _ = Client::asynchronous();
153 let _ = Client::with(reqwest::Client::new());
154 }
155}