1use std::{borrow::Cow, error::Error};
6
7use faststr::FastStr;
8use http::{
9 header::{HeaderMap, HeaderName, HeaderValue},
10 method::Method,
11 uri::{PathAndQuery, Scheme, Uri},
12 version::Version,
13};
14use motore::layer::Layer;
15use volo::{
16 client::{Apply, OneShotService, WithOptService},
17 net::Address,
18};
19
20use super::{CallOpt, insert_header, target::Target};
21use crate::{
22 body::Body,
23 context::ClientContext,
24 error::{
25 BoxError, ClientError,
26 client::{Result, builder_error},
27 },
28 request::Request,
29 response::Response,
30 utils::consts,
31};
32
33pub struct RequestBuilder<S, B = Body> {
35 inner: S,
36 target: Target,
37 version: Option<Version>,
38 request: Request<B>,
39 status: Result<()>,
40}
41
42impl<S> RequestBuilder<S> {
43 pub(super) fn new(inner: S) -> Self {
44 Self {
45 inner,
46 target: Default::default(),
47 version: None,
48 request: Request::default(),
49 status: Ok(()),
50 }
51 }
52
53 pub fn data<D>(mut self, data: D) -> Self
55 where
56 D: TryInto<Body>,
57 D::Error: Error + Send + Sync + 'static,
58 {
59 if self.status.is_err() {
60 return self;
61 }
62
63 let body = match data.try_into() {
64 Ok(body) => body,
65 Err(err) => {
66 self.status = Err(builder_error(err));
67 return self;
68 }
69 };
70
71 let (parts, _) = self.request.into_parts();
72 self.request = Request::from_parts(parts, body);
73
74 self
75 }
76
77 #[cfg(feature = "json")]
79 pub fn json<T>(mut self, json: &T) -> Self
80 where
81 T: serde::Serialize,
82 {
83 if self.status.is_err() {
84 return self;
85 }
86
87 let json = match crate::utils::json::serialize(json) {
88 Ok(json) => json,
89 Err(err) => {
90 self.status = Err(builder_error(err));
91 return self;
92 }
93 };
94
95 let (mut parts, _) = self.request.into_parts();
96 parts.headers.insert(
97 http::header::CONTENT_TYPE,
98 crate::utils::consts::APPLICATION_JSON,
99 );
100 self.request = Request::from_parts(parts, Body::from(json));
101
102 self
103 }
104
105 #[cfg(feature = "form")]
107 pub fn form<T>(mut self, form: &T) -> Self
108 where
109 T: serde::Serialize,
110 {
111 if self.status.is_err() {
112 return self;
113 }
114
115 let form = match serde_urlencoded::to_string(form) {
116 Ok(form) => form,
117 Err(err) => {
118 self.status = Err(builder_error(err));
119 return self;
120 }
121 };
122
123 let (mut parts, _) = self.request.into_parts();
124 parts.headers.insert(
125 http::header::CONTENT_TYPE,
126 crate::utils::consts::APPLICATION_WWW_FORM_URLENCODED,
127 );
128 self.request = Request::from_parts(parts, Body::from(form));
129
130 self
131 }
132}
133
134impl<S, B> RequestBuilder<S, B> {
135 pub fn method(mut self, method: Method) -> Self {
137 *self.request.method_mut() = method;
138 self
139 }
140
141 pub fn method_ref(&self) -> &Method {
143 self.request.method()
144 }
145
146 pub fn uri<U>(mut self, uri: U) -> Self
155 where
156 U: TryInto<Uri>,
157 U::Error: Into<BoxError>,
158 {
159 if self.status.is_err() {
160 return self;
161 }
162 let uri = match uri.try_into() {
163 Ok(uri) => uri,
164 Err(err) => {
165 self.status = Err(builder_error(err));
166 return self;
167 }
168 };
169 if uri.host().is_some() {
170 match Target::from_uri(&uri) {
171 Ok(target) => self.target = target,
172 Err(err) => {
173 self.status = Err(err);
174 return self;
175 }
176 }
177 }
178 let rela_uri = uri
179 .path_and_query()
180 .map(PathAndQuery::to_owned)
181 .unwrap_or_else(|| PathAndQuery::from_static("/"))
182 .into();
183 *self.request.uri_mut() = rela_uri;
184
185 self
186 }
187
188 #[cfg(feature = "query")]
190 pub fn set_query<T>(mut self, query: &T) -> Self
191 where
192 T: serde::Serialize,
193 {
194 if self.status.is_err() {
195 return self;
196 }
197 let query_str = match serde_urlencoded::to_string(query) {
198 Ok(query) => query,
199 Err(err) => {
200 self.status = Err(builder_error(err));
201 return self;
202 }
203 };
204
205 let path_str = self.request.uri().path();
207 let mut path = String::with_capacity(path_str.len() + 1 + query_str.len());
208 path.push_str(path_str);
209 path.push('?');
210 path.push_str(&query_str);
211 let Ok(uri) = Uri::from_maybe_shared(path) else {
212 unreachable!();
214 };
215
216 *self.request.uri_mut() = uri;
217
218 self
219 }
220
221 pub fn uri_ref(&self) -> &Uri {
223 self.request.uri()
224 }
225
226 pub fn version(mut self, version: Version) -> Self {
230 self.version = Some(version);
231 self
232 }
233
234 pub fn version_ref(&self) -> Option<Version> {
236 self.version
237 }
238
239 pub fn header<K, V>(mut self, key: K, value: V) -> Self
241 where
242 K: TryInto<HeaderName>,
243 K::Error: Error + Send + Sync + 'static,
244 V: TryInto<HeaderValue>,
245 V::Error: Error + Send + Sync + 'static,
246 {
247 if self.status.is_err() {
248 return self;
249 }
250
251 if let Err(err) = insert_header(self.request.headers_mut(), key, value) {
252 self.status = Err(err);
253 }
254
255 self
256 }
257
258 pub fn headers(&self) -> &HeaderMap {
260 self.request.headers()
261 }
262
263 pub fn headers_mut(&mut self) -> &mut HeaderMap {
265 self.request.headers_mut()
266 }
267
268 pub fn address<A>(mut self, address: A) -> Self
270 where
271 A: Into<Address>,
272 {
273 self.target = Target::from(address.into());
274 self
275 }
276
277 pub fn host<H>(mut self, host: H) -> Self
284 where
285 H: Into<Cow<'static, str>>,
286 {
287 self.target = unsafe {
289 Target::new_host_unchecked(
290 Scheme::HTTP,
291 FastStr::from(host.into()),
292 consts::HTTP_DEFAULT_PORT,
293 )
294 };
295 self
296 }
297
298 pub fn with_scheme(mut self, scheme: Scheme) -> Self {
300 if self.status.is_err() {
301 return self;
302 }
303 if let Err(err) = self.target.set_scheme(scheme) {
304 self.status = Err(err);
305 }
306 self
307 }
308
309 pub fn with_port(mut self, port: u16) -> Self {
311 if self.status.is_err() {
312 return self;
313 }
314 if let Err(err) = self.target.set_port(port) {
315 self.status = Err(err);
316 }
317 self
318 }
319
320 pub fn target_ref(&self) -> &Target {
322 &self.target
323 }
324
325 pub fn target_mut(&mut self) -> &mut Target {
327 &mut self.target
328 }
329
330 pub fn body<B2>(self, body: B2) -> RequestBuilder<S, B2> {
332 let (parts, _) = self.request.into_parts();
333 let request = Request::from_parts(parts, body);
334
335 RequestBuilder {
336 inner: self.inner,
337 target: self.target,
338 version: self.version,
339 request,
340 status: self.status,
341 }
342 }
343
344 pub fn body_ref(&self) -> &B {
346 self.request.body()
347 }
348
349 pub fn layer<L>(self, layer: L) -> RequestBuilder<L::Service, B>
353 where
354 L: Layer<S>,
355 {
356 RequestBuilder {
357 inner: layer.layer(self.inner),
358 target: self.target,
359 version: self.version,
360 request: self.request,
361 status: self.status,
362 }
363 }
364
365 pub fn with_callopt(self, callopt: CallOpt) -> RequestBuilder<WithOptService<S, CallOpt>, B> {
367 self.layer(WithOptLayer::new(callopt))
368 }
369
370 fn set_version(&mut self) {
371 let ver = match self.version {
372 Some(ver) => ver,
373 None => {
374 if cfg!(feature = "http1") {
376 Version::HTTP_11
377 } else {
378 Version::HTTP_2
379 }
380 }
381 };
382 *self.request.version_mut() = ver;
383 }
384
385 pub async fn send<RespBody>(mut self) -> Result<Response<RespBody>>
387 where
388 S: OneShotService<
389 ClientContext,
390 Request<B>,
391 Response = Response<RespBody>,
392 Error = ClientError,
393 > + Send
394 + Sync
395 + 'static,
396 B: Send + 'static,
397 {
398 self.set_version();
399 self.status?;
400
401 let mut cx = ClientContext::new();
402 self.target.apply(&mut cx)?;
403 self.inner.call(&mut cx, self.request).await
404 }
405}
406
407struct WithOptLayer {
408 opt: CallOpt,
409}
410
411impl WithOptLayer {
412 const fn new(opt: CallOpt) -> Self {
413 Self { opt }
414 }
415}
416
417impl<S> Layer<S> for WithOptLayer {
418 type Service = WithOptService<S, CallOpt>;
419
420 fn layer(self, inner: S) -> Self::Service {
421 WithOptService::new(inner, self.opt)
422 }
423}