1use std::{marker::PhantomData, rc::Rc};
2
3use crate::error::{Failure, IntoFailure};
4use crate::router::{IntoPattern, ResourceDef};
5use crate::service::{IntoServiceFactory, ServiceFactory, boxed};
6
7use super::error::{WebError, WebResponseError};
8use super::guard::{AllGuard, Guard};
9use super::{HttpService, State, WebRequest, WebResponse, dev::insert_slash, rmap::ResourceMap};
10
11pub trait WebServiceFactory<St: State, In>: 'static {
18 fn register(self, config: &mut WebServiceConfig<St, In>);
20}
21
22pub(super) trait AppServiceFactory<St: State, In> {
23 fn register(&mut self, config: &mut WebServiceConfig<St, In>);
24}
25
26pub(super) struct ServiceFactoryWrapper<T> {
27 factory: Option<T>,
28}
29
30impl<T> ServiceFactoryWrapper<T> {
31 pub(super) fn new(factory: T) -> Self {
32 Self {
33 factory: Some(factory),
34 }
35 }
36}
37
38impl<T, St, In> AppServiceFactory<St, In> for ServiceFactoryWrapper<T>
39where
40 T: WebServiceFactory<St, In>,
41 St: State,
42{
43 fn register(&mut self, config: &mut WebServiceConfig<St, In>) {
44 if let Some(item) = self.factory.take() {
45 item.register(config);
46 }
47 }
48}
49
50type Guards = Vec<Box<dyn Guard>>;
51
52#[derive(derive_more::Debug)]
54#[debug("WebServiceConfig")]
55pub struct WebServiceConfig<St: State, In = ()> {
56 root: bool,
57 services: Vec<(
58 ResourceDef,
59 HttpService<St, In>,
60 Option<Guards>,
61 Option<Rc<ResourceMap>>,
62 )>,
63}
64
65impl<St: State, In: 'static> WebServiceConfig<St, In> {
66 pub(crate) fn new() -> Self {
68 WebServiceConfig {
69 root: true,
70 services: Vec::new(),
71 }
72 }
73
74 pub fn is_root(&self) -> bool {
76 self.root
77 }
78
79 pub(crate) fn into_services(
80 self,
81 ) -> Vec<(
82 ResourceDef,
83 HttpService<St, In>,
84 Option<Guards>,
85 Option<Rc<ResourceMap>>,
86 )> {
87 self.services
88 }
89
90 pub fn register_service<S>(
92 &mut self,
93 rdef: ResourceDef,
94 guards: Option<Vec<Box<dyn Guard>>>,
95 nested: Option<Rc<ResourceMap>>,
96 factory: impl IntoServiceFactory<S, St, WebRequest<In>>,
97 ) where
98 S: ServiceFactory<
99 St,
100 WebRequest<In>,
101 Res = WebResponse,
102 Error = WebError<St, St::Error>,
103 InitError = Failure,
104 > + 'static,
105 {
106 self.services
107 .push((rdef, boxed::factory(factory.into_factory()), guards, nested));
108 }
109}
110
111#[derive(Debug)]
128pub struct WebServiceAdapter {
129 rdef: Vec<String>,
130 name: Option<String>,
131 guards: AllGuard,
132}
133
134impl WebServiceAdapter {
135 #[must_use]
136 #[allow(clippy::needless_pass_by_value)]
137 pub fn new<T: IntoPattern>(path: T) -> Self {
139 WebServiceAdapter {
140 rdef: path.patterns(),
141 name: None,
142 guards: AllGuard::default(),
143 }
144 }
145
146 #[must_use]
150 pub fn name(mut self, name: &str) -> Self {
151 self.name = Some(name.to_string());
152 self
153 }
154
155 #[must_use]
180 pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
181 self.guards.add(guard);
182 self
183 }
184
185 pub fn build<Sf, St, In, F>(self, service: F) -> impl WebServiceFactory<St, In>
187 where
188 St: State,
189 In: 'static,
190 F: IntoServiceFactory<Sf, St, WebRequest<In>>,
191 Sf: ServiceFactory<St, WebRequest<In>, Res = WebResponse> + 'static,
192 Sf::Error: WebResponseError<St, St::Error>,
193 Sf::InitError: IntoFailure,
194 {
195 WebServiceImpl {
196 srv: service
197 .into_factory()
198 .map_err(WebError::from_err)
199 .map_init_err(IntoFailure::fail),
200 rdef: self.rdef,
201 name: self.name,
202 guards: self.guards,
203 ph: PhantomData,
204 }
205 }
206}
207
208struct WebServiceImpl<Sf, In> {
209 srv: Sf,
210 rdef: Vec<String>,
211 name: Option<String>,
212 guards: AllGuard,
213 ph: PhantomData<In>,
214}
215
216impl<Sf, In, St> WebServiceFactory<St, In> for WebServiceImpl<Sf, In>
217where
218 St: State,
219 In: 'static,
220 Sf: ServiceFactory<
221 St,
222 WebRequest<In>,
223 Res = WebResponse,
224 Error = WebError<St, St::Error>,
225 InitError = Failure,
226 > + 'static,
227{
228 fn register(mut self, config: &mut WebServiceConfig<St, In>) {
229 let guards = if self.guards.0.is_empty() {
230 None
231 } else {
232 Some(std::mem::take(&mut self.guards.0))
233 };
234
235 let mut rdef = if config.is_root() || !self.rdef.is_empty() {
236 ResourceDef::new(insert_slash(self.rdef))
237 } else {
238 ResourceDef::new(self.rdef)
239 };
240 if let Some(name) = self.name {
241 rdef.set_name(name);
242 }
243 config.register_service(rdef, guards, None, self.srv);
244 }
245}
246
247#[allow(unused_parens)]
248impl<T, St, In> WebServiceFactory<St, In> for Vec<T>
249where
250 T: WebServiceFactory<St, In> + 'static,
251 St: State,
252{
253 fn register(mut self, config: &mut WebServiceConfig<St, In>) {
254 for service in self.drain(..) {
255 service.register(config);
256 }
257 }
258}
259
260macro_rules! tuple_web_service(
261 {$(#[$meta:meta])* $(($n:tt, $T:ident)),+} => {
262
263 $(#[$meta])*
264 impl<St: State, In, $($T: WebServiceFactory<St, In> + 'static),+> WebServiceFactory<St, In> for ($($T,)+) {
265 fn register(self, config: &mut WebServiceConfig<St, In>) {
266 $(
267 self.$n.register(config);
268 )+
269 }
270 }
271 }
272);
273
274impl<St, In, T, const N: usize> WebServiceFactory<St, In> for [T; N]
275where
276 St: State,
277 T: WebServiceFactory<St, In> + 'static,
278{
279 fn register(self, config: &mut WebServiceConfig<St, In>) {
280 for t in self {
281 t.register(config);
282 }
283 }
284}
285
286#[allow(non_snake_case, clippy::wildcard_imports)]
287#[rustfmt::skip]
288mod m {
289 use super::*;
290 use variadics_please::all_tuples_enumerated;
291
292 all_tuples_enumerated!(#[doc(fake_variadic)] tuple_web_service, 1, 24, T);
293}
294
295#[cfg(test)]
296mod tests {
297 use std::convert::Infallible;
298
299 use super::*;
300 use crate::http::{Method, StatusCode};
301 use crate::web::test::{TestRequest, init_service};
302 use crate::web::{self, App, DefaultError, HttpResponse, guard};
303
304 #[crate::rt_test]
305 async fn test_service() {
306 let srv = init_service(App::new().service(web::service("/test").name("test").build(
307 async move |req: WebRequest<()>| {
308 Ok::<_, Infallible>(req.into_response(HttpResponse::Ok().build()))
309 },
310 )))
311 .await;
312 let req = TestRequest::with_uri("/test").to_request();
313 let resp = srv.call(req).await.unwrap();
314 assert_eq!(resp.status(), StatusCode::OK);
315
316 let srv = init_service(
317 App::new().service(web::service("/test").guard(guard::Get()).build(
318 async move |req: WebRequest<()>| {
319 Ok::<_, DefaultError>(req.into_response(HttpResponse::Ok().build()))
320 },
321 )),
322 )
323 .await;
324 let req = TestRequest::with_uri("/test")
325 .method(Method::PUT)
326 .to_request();
327 let resp = srv.call(req).await.unwrap();
328 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
329 }
330
331 #[crate::rt_test]
332 async fn test_multi() {
333 let srv = init_service(App::new().service([
334 web::resource("/test1").to(async || HttpResponse::Ok()),
335 web::resource("/test2").to(async || HttpResponse::Ok()),
336 ]))
337 .await;
338 let req = TestRequest::with_uri("/test1").to_request();
339 let resp = srv.call(req).await.unwrap();
340 assert_eq!(resp.status(), StatusCode::OK);
341 let req = TestRequest::with_uri("/test2").to_request();
342 let resp = srv.call(req).await.unwrap();
343 assert_eq!(resp.status(), StatusCode::OK);
344
345 let srv = init_service(App::new().service((
346 web::resource("/test1").to(async || HttpResponse::Ok()),
347 web::resource("/test2").to(async || HttpResponse::Ok()),
348 )))
349 .await;
350 let req = TestRequest::with_uri("/test1").to_request();
351 let resp = srv.call(req).await.unwrap();
352 assert_eq!(resp.status(), StatusCode::OK);
353 let req = TestRequest::with_uri("/test2").to_request();
354 let resp = srv.call(req).await.unwrap();
355 assert_eq!(resp.status(), StatusCode::OK);
356
357 let srv = init_service(App::new().service(vec![
358 web::resource("/test1").to(async || HttpResponse::Ok()),
359 web::resource("/test2").to(async || HttpResponse::Ok()),
360 ]))
361 .await;
362 let req = TestRequest::with_uri("/test1").to_request();
363 let resp = srv.call(req).await.unwrap();
364 assert_eq!(resp.status(), StatusCode::OK);
365 let req = TestRequest::with_uri("/test2").to_request();
366 let resp = srv.call(req).await.unwrap();
367 assert_eq!(resp.status(), StatusCode::OK);
368 }
369
370 #[test]
371 fn test_fmt_debug() {
372 let req = TestRequest::get()
373 .uri("/index.html?test=1")
374 .header("x-test", "111")
375 .to_srv_request();
376 let s = format!("{req:?}");
377 assert!(s.contains("WebRequest"));
378 assert!(s.contains("test=1"));
379 assert!(s.contains("x-test"));
380
381 let res = HttpResponse::Ok().header("x-test", "111").build();
382 let res = TestRequest::post()
383 .uri("/index.html?test=1")
384 .to_srv_response(res);
385
386 let s = format!("{res:?}");
387 assert!(s.contains("WebResponse"));
388 assert!(s.contains("x-test"));
389 }
390
391 #[crate::rt_test]
392 async fn test_service_empty_path_in_scope() {
393 let srv = init_service(App::new().service(web::scope("/app").service(
394 web::service("").build(async move |req: WebRequest<()>| {
395 Ok::<_, Infallible>(req.into_response(HttpResponse::Ok().build()))
396 }),
397 )))
398 .await;
399 let req = TestRequest::with_uri("/app").to_request();
400 let resp = srv.call(req).await.unwrap();
401 assert_eq!(resp.status(), StatusCode::OK);
402
403 let req = TestRequest::with_uri("/app/").to_request();
404 let resp = srv.call(req).await.unwrap();
405 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
406 }
407}