paperclip_actix/
web.rs

1#![allow(clippy::return_self_not_must_use)]
2
3//! Proxy module for [`actix_web::web`](https://docs.rs/actix-web/*/actix_web/web/index.html).
4extern crate actix_service2 as actix_service;
5extern crate actix_web4 as actix_web;
6
7pub use actix_web::{
8    web::{
9        block, service, to, Bytes, BytesMut, Data, Form, FormConfig, Json, JsonConfig, Path,
10        PathConfig, Payload, PayloadConfig, Query, QueryConfig, ReqData,
11    },
12    HttpRequest, HttpResponse,
13};
14
15use crate::Mountable;
16use actix_service::ServiceFactory;
17use actix_web::{
18    body::MessageBody,
19    dev::{AppService, Handler, HttpServiceFactory, ServiceRequest, ServiceResponse, Transform},
20    guard::Guard,
21    http::Method,
22    Error, FromRequest, Responder,
23};
24use paperclip_core::v2::{
25    models::{
26        DefaultOperationRaw, DefaultPathItemRaw, DefaultSchemaRaw, HttpMethod, SecurityScheme,
27    },
28    schema::Apiv2Operation,
29};
30
31use std::{collections::BTreeMap, fmt::Debug, future::Future, mem};
32
33const METHODS: &[Method] = &[
34    Method::GET,
35    Method::PUT,
36    Method::POST,
37    Method::DELETE,
38    Method::OPTIONS,
39    Method::HEAD,
40    Method::PATCH,
41];
42
43/* Resource */
44
45/// Wrapper for [`actix_web::Resource`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html)
46pub struct Resource<R = actix_web::Resource> {
47    path: String,
48    operations: BTreeMap<HttpMethod, DefaultOperationRaw>,
49    definitions: BTreeMap<String, DefaultSchemaRaw>,
50    security: BTreeMap<String, SecurityScheme>,
51    inner: R,
52}
53
54impl Resource {
55    /// Wrapper for [`actix_web::Resource::new`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.new).
56    pub fn new(path: &str) -> Resource {
57        Resource {
58            path: path.into(),
59            operations: BTreeMap::new(),
60            definitions: BTreeMap::new(),
61            security: BTreeMap::new(),
62            inner: actix_web::Resource::new(path),
63        }
64    }
65}
66
67impl<T, B> HttpServiceFactory for Resource<actix_web::Resource<T>>
68where
69    T: ServiceFactory<
70            ServiceRequest,
71            Config = (),
72            Response = ServiceResponse<B>,
73            Error = Error,
74            InitError = (),
75        > + 'static,
76    B: MessageBody + 'static,
77{
78    fn register(self, config: &mut AppService) {
79        self.inner.register(config)
80    }
81}
82
83impl<T> Mountable for Resource<T> {
84    fn path(&self) -> &str {
85        &self.path
86    }
87
88    fn operations(&mut self) -> BTreeMap<HttpMethod, DefaultOperationRaw> {
89        mem::take(&mut self.operations)
90    }
91
92    fn definitions(&mut self) -> BTreeMap<String, DefaultSchemaRaw> {
93        mem::take(&mut self.definitions)
94    }
95
96    fn security_definitions(&mut self) -> BTreeMap<String, SecurityScheme> {
97        mem::take(&mut self.security)
98    }
99}
100
101impl<T> Resource<actix_web::Resource<T>>
102where
103    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
104{
105    /// Proxy for [`actix_web::Resource::name`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.name).
106    ///
107    /// **NOTE:** This doesn't affect spec generation.
108    pub fn name(mut self, name: &str) -> Self {
109        self.inner = self.inner.name(name);
110        self
111    }
112
113    /// Proxy for [`actix_web::Resource::guard`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.guard).
114    ///
115    /// **NOTE:** This doesn't affect spec generation.
116    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
117        self.inner = self.inner.guard(guard);
118        self
119    }
120
121    /// Wrapper for [`actix_web::Resource::route`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.route).
122    pub fn route(mut self, route: Route) -> Self {
123        let w = RouteWrapper::from(&self.path, route);
124        self.operations.extend(w.operations);
125        self.definitions.extend(w.definitions);
126        SecurityScheme::append_map(w.security, &mut self.security);
127        self.inner = self.inner.route(w.inner);
128        self
129    }
130
131    /// Proxy for [`actix_web::Resource::app_data`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.app_data).
132    ///
133    /// **NOTE:** This doesn't affect spec generation.
134    pub fn app_data<U: 'static>(mut self, data: U) -> Self {
135        let w = self.inner.app_data(data);
136        self.inner = w;
137        self
138    }
139
140    /// Wrapper for [`actix_web::Resource::to`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.to).
141    pub fn to<F, Args>(mut self, handler: F) -> Self
142    where
143        F: Handler<Args>,
144        Args: FromRequest + 'static,
145        F::Output: Responder + 'static,
146        F::Future: Apiv2Operation,
147    {
148        self.update_from_handler::<F::Future>();
149        self.inner = self.inner.to(handler);
150        self
151    }
152
153    /// Proxy for [`actix_web::web::Resource::wrap`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.wrap).
154    ///
155    /// **NOTE:** This doesn't affect spec generation.
156    pub fn wrap<M, B>(
157        self,
158        mw: M,
159    ) -> Resource<
160        actix_web::Resource<
161            impl ServiceFactory<
162                ServiceRequest,
163                Config = (),
164                Response = ServiceResponse<B>,
165                Error = Error,
166                InitError = (),
167            >,
168        >,
169    >
170    where
171        B: MessageBody,
172        M: Transform<
173                T::Service,
174                ServiceRequest,
175                Response = ServiceResponse<B>,
176                Error = Error,
177                InitError = (),
178            > + 'static,
179    {
180        Resource {
181            path: self.path,
182            operations: self.operations,
183            definitions: self.definitions,
184            security: self.security,
185            inner: self.inner.wrap(mw),
186        }
187    }
188
189    /// Proxy for [`actix_web::web::Resource::wrap_fn`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.wrap_fn).
190    ///
191    /// **NOTE:** This doesn't affect spec generation.
192    pub fn wrap_fn<F, R, B>(
193        self,
194        mw: F,
195    ) -> Resource<
196        actix_web::Resource<
197            impl ServiceFactory<
198                ServiceRequest,
199                Config = (),
200                Response = ServiceResponse<B>,
201                Error = Error,
202                InitError = (),
203            >,
204        >,
205    >
206    where
207        B: MessageBody,
208        F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
209        R: Future<Output = Result<ServiceResponse<B>, Error>>,
210    {
211        Resource {
212            path: self.path,
213            operations: self.operations,
214            definitions: self.definitions,
215            security: self.security,
216            inner: self.inner.wrap_fn(mw),
217        }
218    }
219
220    /// Proxy for [`actix_web::web::Resource::default_service`](https://docs.rs/actix-web/*/actix_web/struct.Resource.html#method.default_service).
221    ///
222    /// **NOTE:** This doesn't affect spec generation.
223    pub fn default_service<F, U>(mut self, f: F) -> Self
224    where
225        F: actix_service::IntoServiceFactory<U, ServiceRequest>,
226        U: ServiceFactory<
227                ServiceRequest,
228                Config = (),
229                Response = ServiceResponse,
230                Error = Error,
231                InitError = (),
232            > + 'static,
233        U::InitError: Debug,
234    {
235        self.inner = self.inner.default_service(f);
236        self
237    }
238
239    /// Updates this resource using the given handler.
240    fn update_from_handler<U>(&mut self)
241    where
242        U: Apiv2Operation,
243    {
244        let mut op = U::operation();
245        if U::is_visible() {
246            op.set_parameter_names_from_path_template(&self.path);
247            for method in METHODS {
248                self.operations.insert(method.into(), op.clone());
249            }
250
251            self.definitions.extend(U::definitions());
252            SecurityScheme::append_map(U::security_definitions(), &mut self.security);
253        }
254    }
255}
256
257/// Wrapper for [`actix_web::web::resource`](https://docs.rs/actix-web/*/actix_web/web/fn.resource.html).
258pub fn resource(path: &str) -> Resource {
259    Resource::new(path)
260}
261
262/* Scope */
263
264/// Wrapper for [`actix_web::Scope`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html)
265pub struct Scope<S = actix_web::Scope> {
266    path: String,
267    path_map: BTreeMap<String, DefaultPathItemRaw>,
268    definitions: BTreeMap<String, DefaultSchemaRaw>,
269    security: BTreeMap<String, SecurityScheme>,
270    inner: Option<S>,
271}
272
273impl Scope {
274    /// Wrapper for [`actix_web::Scope::new`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.new)
275    pub fn new(path: &str) -> Self {
276        Scope {
277            path: path.into(),
278            path_map: BTreeMap::new(),
279            definitions: BTreeMap::new(),
280            security: BTreeMap::new(),
281            inner: Some(actix_web::Scope::new(path)),
282        }
283    }
284}
285
286impl<T, B> HttpServiceFactory for Scope<actix_web::Scope<T>>
287where
288    T: ServiceFactory<
289            ServiceRequest,
290            Config = (),
291            Response = ServiceResponse<B>,
292            Error = Error,
293            InitError = (),
294        > + 'static,
295    B: MessageBody + 'static,
296{
297    fn register(self, config: &mut AppService) {
298        if let Some(s) = self.inner {
299            s.register(config);
300        }
301    }
302}
303
304impl<T> Scope<actix_web::Scope<T>>
305where
306    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
307{
308    /// Proxy for [`actix_web::Scope::guard`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.guard).
309    ///
310    /// **NOTE:** This doesn't affect spec generation.
311    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
312        self.inner = self.inner.take().map(|s| s.guard(guard));
313        self
314    }
315
316    /// Proxy for [`actix_web::Scope::app_data`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.data).
317    ///
318    /// **NOTE:** This doesn't affect spec generation.
319    pub fn app_data<U: 'static>(mut self, data: U) -> Self {
320        self.inner = self.inner.take().map(|s| s.app_data(data));
321        self
322    }
323
324    /// Wrapper for [`actix_web::Scope::configure`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.configure).
325    pub fn configure<F>(mut self, f: F) -> Self
326    where
327        F: FnOnce(&mut ServiceConfig),
328    {
329        self.inner = self.inner.take().map(|s| {
330            s.configure(|c| {
331                let mut cfg = ServiceConfig::from(c);
332                f(&mut cfg);
333                self.update_from_mountable(&mut cfg);
334            })
335        });
336        self
337    }
338
339    /// Wrapper for [`actix_web::Scope::service`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.service).
340    pub fn service<F>(mut self, mut factory: F) -> Self
341    where
342        F: Mountable + HttpServiceFactory + 'static,
343    {
344        self.update_from_mountable(&mut factory);
345        self.inner = self.inner.take().map(|s| s.service(factory));
346        self
347    }
348
349    /// Wrapper for [`actix_web::Scope::route`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.route).
350    pub fn route(mut self, path: &str, route: Route) -> Self {
351        let mut w = RouteWrapper::from(path, route);
352        self.update_from_mountable(&mut w);
353        self.inner = self.inner.take().map(|s| s.route(path, w.inner));
354        self
355    }
356
357    /// Proxy for [`actix_web::web::Scope::default_service`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.default_service).
358    ///
359    /// **NOTE:** This doesn't affect spec generation.
360    pub fn default_service<F, U>(mut self, f: F) -> Self
361    where
362        F: actix_service::IntoServiceFactory<U, ServiceRequest>,
363        U: ServiceFactory<
364                ServiceRequest,
365                Config = (),
366                Response = ServiceResponse,
367                Error = Error,
368                InitError = (),
369            > + 'static,
370        U::InitError: Debug,
371    {
372        self.inner = self.inner.map(|s| s.default_service(f));
373        self
374    }
375
376    /// Proxy for [`actix_web::web::Scope::wrap`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.wrap).
377    ///
378    /// **NOTE:** This doesn't affect spec generation.
379    pub fn wrap<M, B>(
380        mut self,
381        mw: M,
382    ) -> Scope<
383        actix_web::Scope<
384            impl ServiceFactory<
385                ServiceRequest,
386                Config = (),
387                Response = ServiceResponse<B>,
388                Error = Error,
389                InitError = (),
390            >,
391        >,
392    >
393    where
394        M: Transform<
395                T::Service,
396                ServiceRequest,
397                Response = ServiceResponse<B>,
398                Error = Error,
399                InitError = (),
400            > + 'static,
401        B: MessageBody,
402    {
403        Scope {
404            path: self.path,
405            path_map: self.path_map,
406            definitions: self.definitions,
407            security: self.security,
408            inner: self.inner.take().map(|s| s.wrap(mw)),
409        }
410    }
411
412    /// Proxy for [`actix_web::web::Scope::wrap_fn`](https://docs.rs/actix-web/*/actix_web/struct.Scope.html#method.wrap_fn).
413    ///
414    /// **NOTE:** This doesn't affect spec generation.
415    pub fn wrap_fn<F, R>(
416        mut self,
417        mw: F,
418    ) -> Scope<
419        actix_web::Scope<
420            impl ServiceFactory<
421                ServiceRequest,
422                Config = (),
423                Response = ServiceResponse,
424                Error = Error,
425                InitError = (),
426            >,
427        >,
428    >
429    where
430        F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
431        R: Future<Output = Result<ServiceResponse, Error>>,
432    {
433        Scope {
434            path: self.path,
435            path_map: self.path_map,
436            definitions: self.definitions,
437            security: self.security,
438            inner: self.inner.take().map(|s| s.wrap_fn(mw)),
439        }
440    }
441
442    /// Updates `self` using the given `Mountable` object.
443    fn update_from_mountable<M>(&mut self, factory: &mut M)
444    where
445        M: Mountable,
446    {
447        self.definitions.extend(factory.definitions());
448        let mut path_map = BTreeMap::new();
449        factory.update_operations(&mut path_map);
450        for (path, mut map) in path_map {
451            let p = if !self.path.ends_with('/') && !path.starts_with('/') {
452                self.path.clone() + "/" + &path
453            } else {
454                self.path.clone() + &path
455            };
456            for op in map.methods.values_mut() {
457                op.set_parameter_names_from_path_template(&p);
458            }
459
460            if let Some(existing) = self.path_map.get_mut(&p) {
461                existing.methods.append(&mut map.methods);
462                existing.parameters.append(&mut map.parameters);
463            } else {
464                self.path_map.insert(p.clone(), map);
465            }
466        }
467
468        SecurityScheme::append_map(factory.security_definitions(), &mut self.security);
469    }
470}
471
472impl<T> Mountable for Scope<T> {
473    fn path(&self) -> &str {
474        unimplemented!("Scope has multiple paths. Use `update_operations` object instead.");
475    }
476
477    fn operations(&mut self) -> BTreeMap<HttpMethod, DefaultOperationRaw> {
478        unimplemented!("Scope has multiple operation maps. Use `update_operations` object instead.")
479    }
480
481    fn security_definitions(&mut self) -> BTreeMap<String, SecurityScheme> {
482        mem::take(&mut self.security)
483    }
484
485    fn definitions(&mut self) -> BTreeMap<String, DefaultSchemaRaw> {
486        mem::take(&mut self.definitions)
487    }
488
489    fn update_operations(&mut self, map: &mut BTreeMap<String, DefaultPathItemRaw>) {
490        for (path, item) in mem::take(&mut self.path_map) {
491            let op_map = map.entry(path).or_default();
492            op_map.methods.extend(item.methods);
493        }
494    }
495}
496
497/// Wrapper for [`actix_web::web::scope`](https://docs.rs/actix-web/*/actix_web/web/fn.scope.html).
498pub fn scope(path: &str) -> Scope {
499    Scope::new(path)
500}
501
502/* Route */
503
504/// Wrapper for [`actix_web::Route`](https://docs.rs/actix-web/*/actix_web/struct.Route.html)
505pub struct Route {
506    method: Option<HttpMethod>,
507    operation: Option<DefaultOperationRaw>,
508    definitions: BTreeMap<String, DefaultSchemaRaw>,
509    security: BTreeMap<String, SecurityScheme>,
510    inner: actix_web::Route,
511}
512
513impl ServiceFactory<ServiceRequest> for Route {
514    type Config = ();
515    type Error = Error;
516    type InitError = ();
517    type Service = <actix_web::Route as ServiceFactory<ServiceRequest>>::Service;
518    type Future = <actix_web::Route as ServiceFactory<ServiceRequest>>::Future;
519    type Response =
520        <<actix_web::Route as ServiceFactory<ServiceRequest>>::Service as actix_service::Service<
521            ServiceRequest,
522        >>::Response;
523
524    #[allow(clippy::unit_arg)]
525    fn new_service(&self, cfg: Self::Config) -> Self::Future {
526        self.inner.new_service(cfg)
527    }
528}
529
530impl Route {
531    /// Wrapper for [`actix_web::Route::new`](https://docs.rs/actix-web/*/actix_web/struct.Route.html#method.new)
532    #[allow(clippy::new_without_default)]
533    pub fn new() -> Route {
534        Route {
535            method: None,
536            operation: None,
537            definitions: BTreeMap::new(),
538            security: BTreeMap::new(),
539            inner: actix_web::Route::new(),
540        }
541    }
542
543    /// Wrapper for [`actix_web::Route::method`](https://docs.rs/actix-web/*/actix_web/struct.Route.html#method.method)
544    pub fn method(mut self, method: Method) -> Self {
545        self.method = Some(HttpMethod::from(&method));
546        self.inner = self.inner.method(method);
547        self
548    }
549
550    /// Proxy for [`actix_web::Route::guard`](https://docs.rs/actix-web/*/actix_web/struct.Route.html#method.guard).
551    ///
552    /// **NOTE:** This doesn't affect spec generation.
553    pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
554        self.inner = self.inner.guard(guard);
555        self
556    }
557
558    /// Wrapper for [`actix_web::Route::to`](https://docs.rs/actix-web/*/actix_web/struct.Route.html#method.to)
559    pub fn to<F, Args>(mut self, handler: F) -> Self
560    where
561        F: Handler<Args>,
562        Args: FromRequest + 'static,
563        F::Output: Responder + 'static,
564        F::Future: Apiv2Operation,
565    {
566        if F::Future::is_visible() {
567            self.operation = Some(F::Future::operation());
568            self.definitions = F::Future::definitions();
569            self.security = F::Future::security_definitions();
570        }
571        self.inner = self.inner.to(handler);
572        self
573    }
574}
575
576/// Wrapper for [`actix_web::web::method`](https://docs.rs/actix-web/*/actix_web/web/fn.method.html).
577pub fn method(method: Method) -> Route {
578    Route::new().method(method)
579}
580
581/// Wrapper for [`actix_web::web::get`](https://docs.rs/actix-web/*/actix_web/web/fn.get.html).
582pub fn get() -> Route {
583    method(Method::GET)
584}
585
586/// Wrapper for [`actix_web::web::put`](https://docs.rs/actix-web/*/actix_web/web/fn.put.html).
587pub fn put() -> Route {
588    method(Method::PUT)
589}
590
591/// Wrapper for [`actix_web::web::post`](https://docs.rs/actix-web/*/actix_web/web/fn.post.html).
592pub fn post() -> Route {
593    method(Method::POST)
594}
595
596/// Wrapper for [`actix_web::web::patch`](https://docs.rs/actix-web/*/actix_web/web/fn.patch.html).
597pub fn patch() -> Route {
598    method(Method::PATCH)
599}
600
601/// Wrapper for [`actix_web::web::delete`](https://docs.rs/actix-web/*/actix_web/web/fn.delete.html).
602pub fn delete() -> Route {
603    method(Method::DELETE)
604}
605
606/// Wrapper for [`actix_web::web::options`](https://docs.rs/actix-web/*/actix_web/web/fn.options.html).
607pub fn options() -> Route {
608    method(Method::OPTIONS)
609}
610
611/// Wrapper for [`actix_web::web::head`](https://docs.rs/actix-web/*/actix_web/web/fn.head.html).
612pub fn head() -> Route {
613    method(Method::HEAD)
614}
615
616/// Workaround for issue #17. In actix-web, a method in a route is a guard for that route.
617/// Whenever we call `App::route`, `Scope::route` or `ServiceConfig::route`, actix-web
618/// creates a new resource with a route (by calling `Resource::new(path).route(route)`),
619/// but then it also internally moves the guards to the new entity (manually). This forces
620/// us to call the `.route` method on that entity rather than creating a resource with a
621/// route. This wrapper is `Mountable` and can be used by `App`, `Scope`, etc. when calling
622/// the `.route()` method.
623pub(crate) struct RouteWrapper<S> {
624    path: S,
625    pub(crate) operations: BTreeMap<HttpMethod, DefaultOperationRaw>,
626    pub(crate) definitions: BTreeMap<String, DefaultSchemaRaw>,
627    pub(crate) security: BTreeMap<String, SecurityScheme>,
628    pub(crate) inner: actix_web::Route,
629}
630
631impl<S> RouteWrapper<S>
632where
633    S: AsRef<str>,
634{
635    pub(crate) fn from(path: S, route: Route) -> Self {
636        let mut operations = BTreeMap::new();
637        if let Some(mut op) = route.operation {
638            op.set_parameter_names_from_path_template(path.as_ref());
639
640            if let Some(meth) = route.method {
641                operations.insert(meth, op);
642            } else {
643                for method in METHODS {
644                    operations.insert(method.into(), op.clone());
645                }
646            }
647        }
648
649        RouteWrapper {
650            path,
651            operations,
652            definitions: route.definitions,
653            security: route.security,
654            inner: route.inner,
655        }
656    }
657}
658
659impl<S> Mountable for RouteWrapper<S>
660where
661    S: AsRef<str>,
662{
663    fn path(&self) -> &str {
664        self.path.as_ref()
665    }
666
667    fn operations(&mut self) -> BTreeMap<HttpMethod, DefaultOperationRaw> {
668        mem::take(&mut self.operations)
669    }
670
671    fn security_definitions(&mut self) -> BTreeMap<String, SecurityScheme> {
672        mem::take(&mut self.security)
673    }
674
675    fn definitions(&mut self) -> BTreeMap<String, DefaultSchemaRaw> {
676        mem::take(&mut self.definitions)
677    }
678}
679
680/* Service config */
681
682/// Wrapper for [`actix_web::web::ServiceConfig`](https://docs.rs/actix-web/*/actix_web/web/struct.ServiceConfig.html).
683pub struct ServiceConfig<'a> {
684    path_map: BTreeMap<String, DefaultPathItemRaw>,
685    definitions: BTreeMap<String, DefaultSchemaRaw>,
686    security: BTreeMap<String, SecurityScheme>,
687    inner: &'a mut actix_web::web::ServiceConfig,
688}
689
690impl<'a> From<&'a mut actix_web::web::ServiceConfig> for ServiceConfig<'a> {
691    fn from(cfg: &'a mut actix_web::web::ServiceConfig) -> Self {
692        ServiceConfig {
693            path_map: BTreeMap::new(),
694            definitions: BTreeMap::new(),
695            security: BTreeMap::new(),
696            inner: cfg,
697        }
698    }
699}
700
701impl Mountable for ServiceConfig<'_> {
702    fn path(&self) -> &str {
703        unimplemented!("ServiceConfig has multiple paths. Use `update_operations` object instead.");
704    }
705
706    fn operations(&mut self) -> BTreeMap<HttpMethod, DefaultOperationRaw> {
707        unimplemented!(
708            "ServiceConfig has multiple operation maps. Use `update_operations` object instead."
709        )
710    }
711
712    fn security_definitions(&mut self) -> BTreeMap<String, SecurityScheme> {
713        mem::take(&mut self.security)
714    }
715
716    fn definitions(&mut self) -> BTreeMap<String, DefaultSchemaRaw> {
717        mem::take(&mut self.definitions)
718    }
719
720    fn update_operations(&mut self, map: &mut BTreeMap<String, DefaultPathItemRaw>) {
721        for (path, item) in mem::take(&mut self.path_map) {
722            let op_map = map.entry(path).or_default();
723            op_map.methods.extend(item.methods);
724        }
725    }
726}
727
728impl ServiceConfig<'_> {
729    /// Wrapper for [`actix_web::web::ServiceConfig::route`](https://docs.rs/actix-web/*/actix_web/web/struct.ServiceConfig.html#method.route).
730    pub fn route(&mut self, path: &str, route: Route) -> &mut Self {
731        let mut w = RouteWrapper::from(path, route);
732        self.definitions.extend(w.definitions());
733        w.update_operations(&mut self.path_map);
734        SecurityScheme::append_map(w.security, &mut self.security);
735        self.inner.route(path, w.inner);
736        self
737    }
738
739    /// Wrapper for [`actix_web::web::ServiceConfig::service`](https://docs.rs/actix-web/*/actix_web/web/struct.ServiceConfig.html#method.service).
740    pub fn service<F>(&mut self, mut factory: F) -> &mut Self
741    where
742        F: Mountable + HttpServiceFactory + 'static,
743    {
744        self.definitions.extend(factory.definitions());
745        factory.update_operations(&mut self.path_map);
746        SecurityScheme::append_map(factory.security_definitions(), &mut self.security);
747        self.inner.service(factory);
748        self
749    }
750
751    /// Proxy for [`actix_web::web::ServiceConfig::external_resource`](https://docs.rs/actix-web/*/actix_web/web/struct.ServiceConfig.html#method.external_resource).
752    ///
753    /// **NOTE:** This doesn't affect spec generation.
754    pub fn external_resource<N, U>(&mut self, name: N, url: U) -> &mut Self
755    where
756        N: AsRef<str>,
757        U: AsRef<str>,
758    {
759        self.inner.external_resource(name, url);
760        self
761    }
762
763    /// Proxy for [`actix_web::web::ServiceConfig::app_data`](https://docs.rs/actix-web/4.0.1/actix_web/web/struct.ServiceConfig.html#method.app_data).
764    ///
765    /// **NOTE:** This doesn't affect spec generation.
766    pub fn app_data<U: 'static>(&mut self, data: U) -> &mut Self {
767        self.inner.app_data(data);
768        self
769    }
770}