1use std::marker::PhantomData;
14use std::ops::Deref;
15
16use http::Method;
17use indexmap::IndexMap;
18
19use crate::error::Error;
20use crate::request::RequestBuilder;
21use crate::url_build::QueryValue;
22
23#[cfg(feature = "json")]
24use serde::de::DeserializeOwned;
25
26#[derive(Debug, Clone, Copy, Default)]
28pub struct NeedsParams;
29
30#[derive(Debug, Clone, Copy, Default)]
32pub struct NeedsBody;
33
34#[derive(Debug, Clone, Copy, Default)]
36pub struct Ready;
37
38pub trait Endpoint {
82 const METHOD: Method;
84 const PATH: &'static str;
86
87 #[cfg(feature = "json")]
88 type Response: DeserializeOwned;
90
91 #[cfg(not(feature = "json"))]
92 type Response;
94
95 type Params: EndpointParams + Default;
97 type Query: EndpointQuery + Default;
102
103 type Body: EndpointBody + Default;
105
106 type Headers: EndpointHeaders + Default;
108}
109
110pub trait EndpointBody: Default + Sized {
112 type ParamsNext: Default;
114 type CallInitial: Default;
116
117 fn apply_body(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>>;
119}
120
121impl EndpointBody for () {
122 type ParamsNext = Ready;
123 type CallInitial = Ready;
124
125 fn apply_body(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>> {
126 Ok(builder)
127 }
128}
129
130pub trait DefaultParamsInitial<E: Endpoint> {
132 fn initial(
133 client: &crate::Client,
134 ) -> EndpointRequestBuilder<'_, E, <E::Body as EndpointBody>::CallInitial>;
135}
136
137impl<E: Endpoint> DefaultParamsInitial<E> for ()
138where
139 E::Params: EndpointParams<BuilderState = Ready>,
140 E::Body: EndpointBody<CallInitial = Ready>,
141{
142 fn initial(client: &crate::Client) -> EndpointRequestBuilder<'_, E, Ready> {
143 EndpointRequestBuilder::new_ready(client.request(E::METHOD, E::PATH))
144 }
145}
146
147pub trait EndpointHeaders: Default + Sized {
149 fn apply_headers(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>>;
151}
152
153impl EndpointHeaders for () {
154 fn apply_headers(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>> {
155 Ok(builder)
156 }
157}
158
159pub type ParamsBuilderState<P> = <P as EndpointParams>::BuilderState;
161
162pub trait EndpointParamsInitial<E: Endpoint>: EndpointParams {
164 type State;
166 fn initial(client: &crate::Client) -> EndpointRequestBuilder<'_, E, Self::State>;
167}
168
169impl<E: Endpoint> EndpointParamsInitial<E> for ()
170where
171 (): DefaultParamsInitial<E>,
172{
173 type State = <E::Body as EndpointBody>::CallInitial;
174
175 fn initial(client: &crate::Client) -> EndpointRequestBuilder<'_, E, Self::State> {
176 <() as DefaultParamsInitial<E>>::initial(client)
177 }
178}
179
180impl<E: Endpoint, P: EndpointParams<BuilderState = NeedsParams>> EndpointParamsInitial<E> for P {
181 type State = NeedsParams;
182
183 fn initial(client: &crate::Client) -> EndpointRequestBuilder<'_, E, NeedsParams> {
184 EndpointRequestBuilder::new_needs_params(client.request(E::METHOD, E::PATH))
185 }
186}
187
188pub trait EndpointParams: Default + Sized {
193 type BuilderState;
195 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_>;
197}
198
199impl EndpointParams for () {
200 type BuilderState = Ready;
201
202 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
203 builder
204 }
205}
206
207impl EndpointParams for std::collections::HashMap<String, String> {
208 type BuilderState = NeedsParams;
209
210 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
211 builder.params(self)
212 }
213}
214
215impl EndpointParams for Vec<(String, String)> {
216 type BuilderState = NeedsParams;
217
218 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
219 builder.params_iter(self)
220 }
221}
222
223pub trait EndpointQuery {
230 fn apply_query(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>>;
232}
233
234impl EndpointQuery for () {
235 fn apply_query(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>> {
236 Ok(builder)
237 }
238}
239
240impl EndpointQuery for IndexMap<String, QueryValue> {
241 fn apply_query(self, builder: RequestBuilder<'_>) -> crate::Result<RequestBuilder<'_>> {
242 Ok(builder.queries(self))
243 }
244}
245
246#[cfg(feature = "json")]
248pub fn apply_serialized_query<T: serde::Serialize>(
249 query: T,
250 builder: RequestBuilder<'_>,
251) -> crate::Result<RequestBuilder<'_>> {
252 let map = crate::url_build::serialize_to_query_map(&query).map_err(|e| match e {
253 Error::Other(msg) => Error::query_serialize(msg),
254 other => other,
255 })?;
256 Ok(builder.queries(map))
257}
258
259#[cfg(all(feature = "json", feature = "validate"))]
261pub fn apply_serialized_query_validated<T>(
262 query: T,
263 builder: RequestBuilder<'_>,
264) -> crate::Result<RequestBuilder<'_>>
265where
266 T: serde::Serialize + garde::Validate,
267 T::Context: Default,
268{
269 garde::Validate::validate(&query).map_err(|report: garde::Report| {
270 Error::RequestValidation {
271 message: report.to_string(),
272 }
273 })?;
274 apply_serialized_query(query, builder)
275}
276
277#[must_use = "endpoint builders do nothing until you call `.send().await`, `.send_json().await`, or similar"]
286pub struct EndpointRequestBuilder<'a, E: Endpoint, S> {
287 pub(crate) inner: RequestBuilder<'a>,
288 _marker: PhantomData<(E, S)>,
289}
290
291impl<'a, E: Endpoint> EndpointRequestBuilder<'a, E, NeedsParams> {
292 pub(crate) fn new_needs_params(inner: RequestBuilder<'a>) -> Self {
293 Self {
294 inner,
295 _marker: PhantomData,
296 }
297 }
298
299 pub fn params(
301 self,
302 params: E::Params,
303 ) -> EndpointRequestBuilder<'a, E, ParamsBuilderStateAfter<E>>
304 where
305 E::Body: EndpointBody,
306 {
307 EndpointRequestBuilder {
308 inner: params.apply_params(self.inner),
309 _marker: PhantomData,
310 }
311 }
312}
313
314pub type ParamsBuilderStateAfter<E> = <<E as Endpoint>::Body as EndpointBody>::ParamsNext;
316
317impl<'a, E: Endpoint> EndpointRequestBuilder<'a, E, NeedsBody> {
318 pub fn new_needs_body(inner: RequestBuilder<'a>) -> Self {
319 Self {
320 inner,
321 _marker: PhantomData,
322 }
323 }
324
325 #[cfg(feature = "json")]
327 pub fn json<T: serde::Serialize>(
328 self,
329 body: &T,
330 ) -> crate::Result<EndpointRequestBuilder<'a, E, Ready>> {
331 Ok(EndpointRequestBuilder {
332 inner: self.inner.json(body)?,
333 _marker: PhantomData,
334 })
335 }
336
337 #[cfg(feature = "validate")]
339 pub fn json_validated<T>(self, body: &T) -> crate::Result<EndpointRequestBuilder<'a, E, Ready>>
340 where
341 T: serde::Serialize + garde::Validate,
342 T::Context: Default,
343 {
344 Ok(EndpointRequestBuilder {
345 inner: self.inner.json_validated(body)?,
346 _marker: PhantomData,
347 })
348 }
349
350 pub fn with_body(self, body: E::Body) -> crate::Result<EndpointRequestBuilder<'a, E, Ready>> {
352 Ok(EndpointRequestBuilder {
353 inner: body.apply_body(self.inner)?,
354 _marker: PhantomData,
355 })
356 }
357
358 pub fn body(self, body: impl Into<bytes::Bytes>) -> EndpointRequestBuilder<'a, E, Ready> {
360 EndpointRequestBuilder {
361 inner: self.inner.body(body),
362 _marker: PhantomData,
363 }
364 }
365}
366
367impl<'a, E: Endpoint> EndpointRequestBuilder<'a, E, Ready> {
368 pub(crate) fn new_ready(inner: RequestBuilder<'a>) -> Self {
369 Self {
370 inner,
371 _marker: PhantomData,
372 }
373 }
374
375 pub fn query(self, query: E::Query) -> crate::Result<Self> {
419 Ok(Self {
420 inner: query.apply_query(self.inner)?,
421 _marker: PhantomData,
422 })
423 }
424
425 #[cfg(all(feature = "json", feature = "validate"))]
429 pub fn query_validated(self, query: E::Query) -> crate::Result<Self>
430 where
431 E::Query: serde::Serialize + garde::Validate,
432 <E::Query as garde::Validate>::Context: Default,
433 {
434 Ok(Self {
435 inner: apply_serialized_query_validated(query, self.inner)?,
436 _marker: PhantomData,
437 })
438 }
439
440 pub fn header(self, key: impl AsRef<str>, value: impl AsRef<str>) -> crate::Result<Self> {
442 Ok(Self {
443 inner: self.inner.header(key, value)?,
444 _marker: PhantomData,
445 })
446 }
447
448 pub fn bearer_token(self, token: impl Into<String>) -> Self {
450 Self {
451 inner: self.inner.bearer_token(token),
452 _marker: PhantomData,
453 }
454 }
455
456 pub fn cancellation_token(self, token: crate::CancellationToken) -> Self {
458 Self {
459 inner: self.inner.cancellation_token(token),
460 _marker: PhantomData,
461 }
462 }
463
464 pub fn throw_on_error(self, throw: bool) -> Self {
466 Self {
467 inner: self.inner.throw_on_error(throw),
468 _marker: PhantomData,
469 }
470 }
471
472 pub fn base_url(self, base_url: impl AsRef<str>) -> crate::Result<Self> {
474 Ok(Self {
475 inner: self.inner.base_url(base_url)?,
476 _marker: PhantomData,
477 })
478 }
479
480 pub fn retry(self, policy: crate::RetryPolicy) -> Self {
482 Self {
483 inner: self.inner.retry(policy),
484 _marker: PhantomData,
485 }
486 }
487
488 pub fn timeout(self, timeout: std::time::Duration) -> Self {
490 Self {
491 inner: self.inner.timeout(timeout),
492 _marker: PhantomData,
493 }
494 }
495
496 pub async fn send_stream(self) -> crate::Result<crate::StreamingResponse> {
498 self.inner.send_stream().await
499 }
500
501 pub fn max_response_bytes(self, limit: u64) -> Self {
503 Self {
504 inner: self.inner.max_response_bytes(limit),
505 _marker: PhantomData,
506 }
507 }
508
509 #[cfg(feature = "schema-validate")]
511 pub fn disable_validation(self, disable: bool) -> Self {
512 Self {
513 inner: self.inner.disable_validation(disable),
514 _marker: PhantomData,
515 }
516 }
517
518 #[cfg(feature = "json")]
520 pub fn json<T: serde::Serialize>(self, body: &T) -> crate::Result<Self> {
521 Ok(Self {
522 inner: self.inner.json(body)?,
523 _marker: PhantomData,
524 })
525 }
526
527 #[cfg(feature = "validate")]
529 pub fn json_validated<T>(self, body: &T) -> crate::Result<Self>
530 where
531 T: serde::Serialize + garde::Validate,
532 T::Context: Default,
533 {
534 Ok(Self {
535 inner: self.inner.json_validated(body)?,
536 _marker: PhantomData,
537 })
538 }
539
540 pub fn body(self, body: impl Into<bytes::Bytes>) -> Self {
542 Self {
543 inner: self.inner.body(body),
544 _marker: PhantomData,
545 }
546 }
547
548 pub async fn send(self) -> crate::Result<crate::Response> {
550 self.inner.send().await
551 }
552
553 pub fn with_body(self, body: E::Body) -> crate::Result<Self> {
555 Ok(Self {
556 inner: body.apply_body(self.inner)?,
557 _marker: PhantomData,
558 })
559 }
560
561 pub fn with_headers(self, headers: E::Headers) -> crate::Result<Self> {
563 Ok(Self {
564 inner: headers.apply_headers(self.inner)?,
565 _marker: PhantomData,
566 })
567 }
568
569 #[cfg(feature = "validate")]
571 pub fn with_headers_validated(self, headers: E::Headers) -> crate::Result<Self>
572 where
573 E::Headers: garde::Validate,
574 <E::Headers as garde::Validate>::Context: Default,
575 {
576 garde::Validate::validate(&headers).map_err(|report: garde::Report| {
577 Error::RequestValidation {
578 message: report.to_string(),
579 }
580 })?;
581 self.with_headers(headers)
582 }
583
584 #[cfg(feature = "json")]
586 pub async fn send_json(self) -> crate::Result<E::Response> {
587 self.inner.send().await?.json::<E::Response>().await
588 }
589
590 #[cfg(feature = "json")]
592 pub async fn send_api<T, ErrBody>(self) -> crate::Result<std::result::Result<T, ErrBody>>
593 where
594 T: serde::de::DeserializeOwned,
595 ErrBody: serde::de::DeserializeOwned,
596 {
597 crate::api_response::into_api_result(self.inner.send().await?)
598 }
599}
600
601impl<'a, E: Endpoint> Deref for EndpointRequestBuilder<'a, E, Ready> {
602 type Target = RequestBuilder<'a>;
603
604 fn deref(&self) -> &Self::Target {
605 &self.inner
606 }
607}
608
609#[macro_export]
625macro_rules! define_params {
626 (
627 $name:ident for $path:literal {
628 $( $field:ident : $ty:ty ),* $(,)?
629 }
630 ) => {
631 #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
632 pub struct $name {
633 $( pub $field: $ty, )*
634 }
635
636 impl $crate::EndpointParams for $name {
637 type BuilderState = $crate::NeedsParams;
638
639 fn apply_params(self, builder: $crate::RequestBuilder<'_>) -> $crate::RequestBuilder<'_> {
640 let builder = builder;
641 $(
642 let builder = builder.param(stringify!($field), self.$field);
643 )*
644 builder
645 }
646 }
647 };
648}
649
650#[cfg(feature = "json")]
652#[macro_export]
653macro_rules! impl_serde_endpoint_query {
654 ($ty:ty) => {
655 impl $crate::EndpointQuery for $ty {
656 fn apply_query(
657 self,
658 builder: $crate::RequestBuilder<'_>,
659 ) -> $crate::Result<$crate::RequestBuilder<'_>> {
660 $crate::endpoint::apply_serialized_query(self, builder)
661 }
662 }
663 };
664}
665
666#[macro_export]
685macro_rules! endpoint {
686 (
687 $name:ident,
688 $method:ident,
689 $path:literal,
690 Response = $response:ty
691 ) => {
692 $crate::endpoint!(
693 $name,
694 $method,
695 $path,
696 Response = $response,
697 Params = (),
698 Query = ()
699 );
700 };
701 (
702 $name:ident,
703 $method:ident,
704 $path:literal,
705 Response = $response:ty,
706 Params = $params:ty
707 ) => {
708 $crate::endpoint!(
709 $name,
710 $method,
711 $path,
712 Response = $response,
713 Params = $params,
714 Query = ()
715 );
716 };
717 (
718 $name:ident,
719 $method:ident,
720 $path:literal,
721 Response = $response:ty,
722 Query = $query:ty
723 ) => {
724 $crate::endpoint!(
725 $name,
726 $method,
727 $path,
728 Response = $response,
729 Params = (),
730 Query = $query
731 );
732 };
733 (
734 $name:ident,
735 $method:ident,
736 $path:literal,
737 Response = $response:ty,
738 Params = $params:ty,
739 Query = $query:ty
740 ) => {
741 pub struct $name;
742 impl $crate::Endpoint for $name {
743 const METHOD: http::Method = http::Method::$method;
744 const PATH: &'static str = $path;
745 type Response = $response;
746 type Params = $params;
747 type Query = $query;
748 type Body = ();
749 type Headers = ();
750 }
751 };
752}
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757
758 define_params!(TestParams for "/items/:id" { id: u64 });
759
760 #[test]
761 fn params_builder_state_is_needs_params() {
762 fn assert_needs<T: EndpointParams<BuilderState = NeedsParams>>() {}
763 assert_needs::<TestParams>();
764 }
765
766 #[test]
767 fn unit_params_builder_state_is_ready() {
768 fn assert_ready<T: EndpointParams<BuilderState = Ready>>() {}
769 assert_ready::<()>();
770 }
771}