1use std::marker::PhantomData;
7
8use http::Method;
9use indexmap::IndexMap;
10
11use crate::request::RequestBuilder;
12use crate::url_build::QueryValue;
13
14#[cfg(feature = "json")]
15use serde::de::DeserializeOwned;
16
17pub trait Endpoint {
48 const METHOD: Method;
50 const PATH: &'static str;
52
53 #[cfg(feature = "json")]
54 type Response: DeserializeOwned;
56
57 #[cfg(not(feature = "json"))]
58 type Response;
60
61 type Params: EndpointParams + Default;
63 type Query: EndpointQuery + Default;
65}
66
67pub trait EndpointParams {
69 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_>;
71}
72
73impl EndpointParams for () {
74 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
75 builder
76 }
77}
78
79impl EndpointParams for std::collections::HashMap<String, String> {
80 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
81 builder.params(self)
82 }
83}
84
85impl EndpointParams for Vec<(String, String)> {
86 fn apply_params(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
87 builder.params_iter(self)
88 }
89}
90
91pub trait EndpointQuery {
93 fn apply_query(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_>;
95}
96
97impl EndpointQuery for () {
98 fn apply_query(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
99 builder
100 }
101}
102
103impl EndpointQuery for IndexMap<String, QueryValue> {
104 fn apply_query(self, builder: RequestBuilder<'_>) -> RequestBuilder<'_> {
105 builder.queries(self)
106 }
107}
108
109pub struct EndpointRequestBuilder<'a, E: Endpoint> {
111 pub(crate) inner: RequestBuilder<'a>,
112 _marker: PhantomData<E>,
113}
114
115impl<'a, E: Endpoint> EndpointRequestBuilder<'a, E> {
116 pub(crate) fn new(inner: RequestBuilder<'a>) -> Self {
117 Self {
118 inner,
119 _marker: PhantomData,
120 }
121 }
122
123 pub fn params(self, params: E::Params) -> Self {
125 Self {
126 inner: params.apply_params(self.inner),
127 _marker: PhantomData,
128 }
129 }
130
131 pub fn query(self, query: E::Query) -> Self {
133 Self {
134 inner: query.apply_query(self.inner),
135 _marker: PhantomData,
136 }
137 }
138
139 pub fn param(self, key: impl Into<String>, value: impl ToString) -> Self {
141 Self {
142 inner: self.inner.param(key, value),
143 _marker: PhantomData,
144 }
145 }
146
147 pub fn query_pair(self, key: impl Into<String>, value: impl ToString) -> Self {
149 Self {
150 inner: self.inner.query(key, value),
151 _marker: PhantomData,
152 }
153 }
154
155 pub fn header(self, key: impl AsRef<str>, value: impl AsRef<str>) -> crate::Result<Self> {
157 Ok(Self {
158 inner: self.inner.header(key, value)?,
159 _marker: PhantomData,
160 })
161 }
162
163 pub fn bearer_token(self, token: impl Into<String>) -> Self {
165 Self {
166 inner: self.inner.bearer_token(token),
167 _marker: PhantomData,
168 }
169 }
170
171 pub fn cancellation_token(self, token: crate::CancellationToken) -> Self {
173 Self {
174 inner: self.inner.cancellation_token(token),
175 _marker: PhantomData,
176 }
177 }
178
179 pub fn throw_on_error(self, throw: bool) -> Self {
181 Self {
182 inner: self.inner.throw_on_error(throw),
183 _marker: PhantomData,
184 }
185 }
186
187 pub async fn send(self) -> crate::Result<crate::Response> {
189 self.inner.send().await
190 }
191
192 #[cfg(feature = "json")]
194 pub async fn send_json(self) -> crate::Result<E::Response> {
195 self.inner.send().await?.json::<E::Response>().await
196 }
197
198 pub fn into_inner(self) -> RequestBuilder<'a> {
200 self.inner
201 }
202}
203
204#[macro_export]
220macro_rules! endpoint {
221 (
222 $name:ident,
223 $method:ident,
224 $path:literal,
225 Response = $response:ty
226 ) => {
227 pub struct $name;
228 impl $crate::Endpoint for $name {
229 const METHOD: http::Method = http::Method::$method;
230 const PATH: &'static str = $path;
231 type Response = $response;
232 type Params = ();
233 type Query = ();
234 }
235 };
236}