1#[cfg(feature = "actix3-validator")]
2extern crate actix_web_validator2 as actix_web_validator;
3#[cfg(feature = "actix4-validator")]
4extern crate actix_web_validator3 as actix_web_validator;
5
6#[cfg(feature = "actix-multipart")]
7use super::schema::TypedData;
8use super::{
9 models::{
10 DefaultOperationRaw, DefaultSchemaRaw, Either, Items, Parameter, ParameterIn, Response,
11 SecurityScheme,
12 },
13 schema::{Apiv2Errors, Apiv2Operation, Apiv2Schema},
14};
15#[cfg(not(feature = "actix4"))]
16use crate::util::{ready, Ready};
17#[cfg(any(feature = "actix3", feature = "actix4"))]
18use actix_web::web::ReqData;
19#[cfg(not(feature = "actix4"))]
20use actix_web::Error;
21#[cfg(feature = "actix4")]
22use actix_web::{body::BoxBody, ResponseError};
23use actix_web::{
24 http::StatusCode,
25 web::{Bytes, Data, Form, Json, Path, Payload, Query},
26 HttpRequest, HttpResponse, Responder,
27};
28
29use pin_project_lite::pin_project;
30
31#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
32use actix_web_validator::{
33 Json as ValidatedJson, Path as ValidatedPath, QsQuery as ValidatedQsQuery,
34 Query as ValidatedQuery,
35};
36use serde::Serialize;
37#[cfg(feature = "serde_qs")]
38use serde_qs::actix::QsQuery;
39
40use std::{
41 collections::BTreeMap,
42 fmt,
43 future::Future,
44 pin::Pin,
45 task::{Context, Poll},
46};
47
48pub trait OperationModifier: Apiv2Schema + Sized {
51 fn update_parameter(op: &mut DefaultOperationRaw) {
53 update_parameter::<Self>(op);
54 }
55
56 fn update_response(_op: &mut DefaultOperationRaw) {}
58
59 fn update_definitions(map: &mut BTreeMap<String, DefaultSchemaRaw>) {
61 update_definitions_from_schema_type::<Self>(map);
62 }
63
64 fn update_security(op: &mut DefaultOperationRaw) {
66 update_security::<Self>(op);
67 }
68
69 fn update_security_definitions(map: &mut BTreeMap<String, SecurityScheme>) {
71 update_security_definitions::<Self>(map);
72 }
73}
74
75#[cfg(feature = "nightly")]
77impl<T> OperationModifier for T
78where
79 T: Apiv2Schema,
80{
81 default fn update_parameter(op: &mut DefaultOperationRaw) {
82 update_parameter::<Self>(op);
83 }
84
85 default fn update_response(_op: &mut DefaultOperationRaw) {}
86
87 default fn update_definitions(map: &mut BTreeMap<String, DefaultSchemaRaw>) {
88 update_definitions_from_schema_type::<Self>(map);
89 }
90
91 default fn update_security(op: &mut DefaultOperationRaw) {
92 update_security::<Self>(op);
93 }
94
95 default fn update_security_definitions(map: &mut BTreeMap<String, SecurityScheme>) {
96 update_security_definitions::<Self>(map);
97 }
98}
99
100impl<T> OperationModifier for Option<T>
101where
102 T: OperationModifier,
103{
104 fn update_parameter(op: &mut DefaultOperationRaw) {
105 T::update_parameter(op);
106 }
107
108 fn update_response(op: &mut DefaultOperationRaw) {
109 T::update_response(op);
110 }
111
112 fn update_definitions(map: &mut BTreeMap<String, DefaultSchemaRaw>) {
113 T::update_definitions(map);
114 }
115
116 fn update_security(op: &mut DefaultOperationRaw) {
117 T::update_security(op);
118 }
119
120 fn update_security_definitions(map: &mut BTreeMap<String, SecurityScheme>) {
121 T::update_security_definitions(map);
122 }
123}
124
125#[cfg(feature = "nightly")]
126impl<T, E> OperationModifier for Result<T, E>
127where
128 T: OperationModifier,
129{
130 default fn update_parameter(op: &mut DefaultOperationRaw) {
131 T::update_parameter(op);
132 }
133
134 default fn update_response(op: &mut DefaultOperationRaw) {
135 T::update_response(op);
136 }
137
138 default fn update_definitions(map: &mut BTreeMap<String, DefaultSchemaRaw>) {
139 T::update_definitions(map);
140 }
141
142 default fn update_security_definitions(map: &mut BTreeMap<String, SecurityScheme>) {
143 T::update_security_definitions(map);
144 }
145}
146
147impl<T, E> OperationModifier for Result<T, E>
148where
149 T: OperationModifier,
150 E: Apiv2Errors,
151{
152 fn update_parameter(op: &mut DefaultOperationRaw) {
153 T::update_parameter(op);
154 }
155
156 fn update_response(op: &mut DefaultOperationRaw) {
157 T::update_response(op);
158 E::update_error_definitions(op);
159 }
160
161 fn update_definitions(map: &mut BTreeMap<String, DefaultSchemaRaw>) {
162 T::update_definitions(map);
163 E::update_definitions(map);
164 }
165
166 fn update_security_definitions(map: &mut BTreeMap<String, SecurityScheme>) {
167 T::update_security_definitions(map);
168 }
169}
170
171impl<T> Apiv2Schema for Data<T> {}
174#[cfg(not(feature = "nightly"))]
175impl<T> OperationModifier for Data<T> {}
176#[cfg(any(feature = "actix3", feature = "actix4"))]
177impl<T: std::clone::Clone> Apiv2Schema for ReqData<T> {}
178#[cfg(not(feature = "nightly"))]
179#[cfg(any(feature = "actix3", feature = "actix4"))]
180impl<T: std::clone::Clone> OperationModifier for ReqData<T> {}
181
182macro_rules! impl_empty({ $($ty:ty),+ } => {
183 $(
184 impl Apiv2Schema for $ty {}
185 #[cfg(not(feature = "nightly"))]
186 impl OperationModifier for $ty {}
187 )+
188});
189
190#[cfg(feature = "actix4")]
191pub struct HttpResponseWrapper(pub HttpResponse);
208
209#[cfg(feature = "actix4")]
210impl Responder for HttpResponseWrapper {
211 type Body = <HttpResponse as Responder>::Body;
212
213 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
214 self.0.respond_to(req)
215 }
216}
217
218#[cfg(feature = "actix4")]
219impl<F> Apiv2Operation for F
220where
221 F: Future<Output = HttpResponseWrapper>,
222{
223 fn operation() -> DefaultOperationRaw {
224 Default::default()
225 }
226
227 fn security_definitions() -> BTreeMap<String, SecurityScheme> {
228 Default::default()
229 }
230
231 fn definitions() -> BTreeMap<String, DefaultSchemaRaw> {
232 Default::default()
233 }
234}
235
236#[cfg(not(feature = "actix4"))]
237impl Apiv2Operation for HttpResponse {
238 fn operation() -> DefaultOperationRaw {
239 Default::default()
240 }
241
242 fn security_definitions() -> BTreeMap<String, SecurityScheme> {
243 Default::default()
244 }
245
246 fn definitions() -> BTreeMap<String, DefaultSchemaRaw> {
247 Default::default()
248 }
249}
250
251impl_empty!(HttpRequest, HttpResponse, Bytes, Payload);
252
253#[cfg(not(feature = "nightly"))]
254mod manual_impl {
255 use super::OperationModifier;
256
257 impl OperationModifier for &str {}
258 impl<T: OperationModifier> OperationModifier for &[T] {}
259
260 macro_rules! impl_simple({ $ty:ty } => {
261 impl OperationModifier for $ty {}
262 });
263
264 impl_simple!(char);
265 impl_simple!(String);
266 impl_simple!(bool);
267 impl_simple!(f32);
268 impl_simple!(f64);
269 impl_simple!(i8);
270 impl_simple!(i16);
271 impl_simple!(i32);
272 impl_simple!(u8);
273 impl_simple!(u16);
274 impl_simple!(u32);
275 impl_simple!(i64);
276 impl_simple!(i128);
277 impl_simple!(isize);
278 impl_simple!(u64);
279 impl_simple!(u128);
280 impl_simple!(usize);
281 #[cfg(feature = "chrono")]
282 impl_simple!(chrono::NaiveDateTime);
283 #[cfg(feature = "jiff01")]
284 impl_simple!(jiff::Timestamp);
285 #[cfg(feature = "jiff01")]
286 impl_simple!(jiff::Zoned);
287 #[cfg(feature = "rust_decimal")]
288 impl_simple!(rust_decimal::Decimal);
289 #[cfg(feature = "url")]
290 impl_simple!(url::Url);
291 #[cfg(feature = "uuid0")]
292 impl_simple!(uuid0_dep::Uuid);
293 #[cfg(feature = "uuid1")]
294 impl_simple!(uuid1_dep::Uuid);
295}
296
297#[cfg(feature = "chrono")]
298impl<T: chrono::TimeZone> OperationModifier for chrono::DateTime<T> {}
299
300#[cfg(feature = "nightly")]
303impl<T> Apiv2Schema for Json<T> {
304 default fn name() -> Option<String> {
305 None
306 }
307
308 default fn raw_schema() -> DefaultSchemaRaw {
309 Default::default()
310 }
311}
312
313impl<T: Apiv2Schema> Apiv2Schema for Json<T> {
315 fn name() -> Option<String> {
316 T::name()
317 }
318
319 fn raw_schema() -> DefaultSchemaRaw {
320 T::raw_schema()
321 }
322}
323
324impl<T> OperationModifier for Json<T>
325where
326 T: Apiv2Schema,
327{
328 fn update_parameter(op: &mut DefaultOperationRaw) {
329 op.parameters.push(Either::Right(Parameter {
330 description: None,
331 in_: ParameterIn::Body,
332 name: "body".into(),
333 required: true,
334 schema: Some({
335 let mut def = T::schema_with_ref();
336 def.retain_ref();
337 def
338 }),
339 ..Default::default()
340 }));
341 }
342
343 fn update_response(op: &mut DefaultOperationRaw) {
344 op.responses.insert(
345 "200".into(),
346 Either::Right(Response {
347 description: Some("OK".into()),
349 schema: Some({
350 let mut def = T::schema_with_ref();
351 def.retain_ref();
352 def
353 }),
354 ..Default::default()
355 }),
356 );
357 }
358}
359
360#[cfg(all(
361 any(feature = "actix4-validator", feature = "actix3-validator"),
362 feature = "nightly"
363))]
364impl<T> Apiv2Schema for ValidatedJson<T> {
365 fn name() -> Option<String> {
366 None
367 }
368
369 default fn raw_schema() -> DefaultSchemaRaw {
370 Default::default()
371 }
372}
373
374#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
375impl<T: Apiv2Schema> Apiv2Schema for ValidatedJson<T> {
376 fn name() -> Option<String> {
377 T::name()
378 }
379
380 fn raw_schema() -> DefaultSchemaRaw {
381 T::raw_schema()
382 }
383}
384
385#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
386impl<T> OperationModifier for ValidatedJson<T>
387where
388 T: Apiv2Schema,
389{
390 fn update_parameter(op: &mut DefaultOperationRaw) {
391 Json::<T>::update_parameter(op);
392 }
393
394 fn update_response(op: &mut DefaultOperationRaw) {
395 Json::<T>::update_response(op);
396 }
397}
398
399#[cfg(feature = "actix-multipart")]
400impl OperationModifier for actix_multipart::Multipart {
401 fn update_parameter(op: &mut DefaultOperationRaw) {
402 op.parameters.push(Either::Right(Parameter {
403 description: None,
404 in_: ParameterIn::FormData,
405 name: "file_data".into(),
406 required: true,
407 data_type: Some(<actix_multipart::Multipart as TypedData>::data_type()),
408 format: <actix_multipart::Multipart as TypedData>::format(),
409 ..Default::default()
410 }));
411 }
412}
413
414#[cfg(feature = "actix-session")]
415impl OperationModifier for actix_session::Session {
416 fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
417}
418
419#[cfg(feature = "actix-identity")]
420impl OperationModifier for actix_identity::Identity {
421 fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
422}
423
424#[cfg(feature = "actix-files")]
425impl OperationModifier for actix_files::NamedFile {
426 fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
427}
428
429macro_rules! impl_param_extractor ({ $ty:ty => $container:ident } => {
430 #[cfg(feature = "nightly")]
431 impl<T> Apiv2Schema for $ty {
432 default fn name() -> Option<String> {
433 None
434 }
435
436 default fn raw_schema() -> DefaultSchemaRaw {
437 Default::default()
438 }
439 }
440
441 #[cfg(not(feature = "nightly"))]
442 impl<T: Apiv2Schema> Apiv2Schema for $ty {}
443
444 impl<T: Apiv2Schema> OperationModifier for $ty {
445 fn update_parameter(op: &mut DefaultOperationRaw) {
446 let def = T::raw_schema();
447 if def.properties.is_empty() && ParameterIn::$container == ParameterIn::Path {
450 op.parameters.push(Either::Right(Parameter {
451 name: String::new(),
452 in_: ParameterIn::Path,
453 required: true,
454 data_type: def.data_type,
455 format: def.format,
456 enum_: def.enum_,
457 description: def.description,
458 ..Default::default()
459 }));
460 }
461 for (k, v) in def.properties {
462 op.parameters.push(Either::Right(Parameter {
463 in_: ParameterIn::$container,
464 required: def.required.contains(&k),
465 data_type: v.data_type,
466 format: v.format,
467 enum_: v.enum_,
468 description: v.description,
469 collection_format: None, items: v.items.as_deref().map(map_schema_to_items),
471 name: k,
472 ..Default::default()
473 }));
474 }
475 }
476
477 fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
480 }
481});
482
483fn map_schema_to_items(schema: &DefaultSchemaRaw) -> Items {
484 Items {
485 data_type: schema.data_type,
486 format: schema.format.clone(),
487 collection_format: None, enum_: schema.enum_.clone(),
489 items: schema
490 .items
491 .as_deref()
492 .map(|schema| Box::new(map_schema_to_items(schema))),
493 ..Default::default() }
495}
496
497#[cfg(feature = "nightly")]
499impl<T: Apiv2Schema> Apiv2Schema for Form<T> {
500 fn name() -> Option<String> {
501 T::name()
502 }
503
504 fn raw_schema() -> DefaultSchemaRaw {
505 T::raw_schema()
506 }
507}
508
509impl_param_extractor!(Path<T> => Path);
510impl_param_extractor!(Query<T> => Query);
511impl_param_extractor!(Form<T> => FormData);
512#[cfg(feature = "serde_qs")]
513impl_param_extractor!(QsQuery<T> => Query);
514#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
515impl_param_extractor!(ValidatedPath<T> => Path);
516#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
517impl_param_extractor!(ValidatedQuery<T> => Query);
518#[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
519impl_param_extractor!(ValidatedQsQuery<T> => Query);
520
521macro_rules! impl_path_tuple ({ $($ty:ident),+ } => {
522 #[cfg(all(any(feature = "actix4-validator", feature = "actix3-validator"), feature = "nightly"))]
523 impl<$($ty,)+> Apiv2Schema for ValidatedPath<($($ty,)+)> {}
524
525 #[cfg(all(not(feature = "nightly"), any(feature = "actix4-validator", feature = "actix3-validator")))]
526 impl<$($ty: Apiv2Schema,)+> Apiv2Schema for ValidatedPath<($($ty,)+)> {}
527
528 #[cfg(any(feature = "actix4-validator", feature = "actix3-validator"))]
529 impl<$($ty,)+> OperationModifier for ValidatedPath<($($ty,)+)>
530 where $($ty: Apiv2Schema,)+
531 {
532 fn update_parameter(op: &mut DefaultOperationRaw) {
533 $(
534 Path::<$ty>::update_parameter(op);
535 )+
536 }
537 }
538
539 #[cfg(feature = "nightly")]
540 impl<$($ty,)+> Apiv2Schema for Path<($($ty,)+)> {}
541
542 #[cfg(not(feature = "nightly"))]
543 impl<$($ty: Apiv2Schema,)+> Apiv2Schema for Path<($($ty,)+)> {}
544
545 impl<$($ty,)+> OperationModifier for Path<($($ty,)+)>
546 where $($ty: Apiv2Schema,)+
547 {
548 fn update_parameter(op: &mut DefaultOperationRaw) {
549 $(
550 let def = $ty::raw_schema();
551 if def.properties.is_empty() {
552 op.parameters.push(Either::Right(Parameter {
553 name: String::new(),
556 in_: ParameterIn::Path,
557 required: true,
558 data_type: def.data_type,
559 format: def.format,
560 enum_: def.enum_,
561 description: def.description,
562 ..Default::default()
563 }));
564 }
565 for (k, v) in def.properties {
566 op.parameters.push(Either::Right(Parameter {
567 in_: ParameterIn::Path,
568 required: def.required.contains(&k),
569 data_type: v.data_type,
570 format: v.format,
571 enum_: v.enum_,
572 description: v.description,
573 collection_format: None, items: v.items.as_deref().map(map_schema_to_items),
575 name: String::new(),
576 ..Default::default()
577 }));
578 }
579 )+
580 }
581 }
582});
583
584impl_path_tuple!(A);
585impl_path_tuple!(A, B);
586impl_path_tuple!(A, B, C);
587impl_path_tuple!(A, B, C, D);
588impl_path_tuple!(A, B, C, D, E);
589impl_path_tuple!(A, B, C, D, E, F);
590impl_path_tuple!(A, B, C, D, E, F, G);
591impl_path_tuple!(A, B, C, D, E, F, G, H);
592impl_path_tuple!(A, B, C, D, E, F, G, H, I);
593impl_path_tuple!(A, B, C, D, E, F, G, H, I, J);
594impl_path_tuple!(A, B, C, D, E, F, G, H, I, J, K);
595impl_path_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
596impl_path_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
597
598pub struct ResponderWrapper<T>(pub T);
600
601#[cfg(feature = "nightly")]
602impl<T: Responder> Apiv2Schema for ResponderWrapper<T> {
603 default fn name() -> Option<String> {
604 None
605 }
606
607 default fn raw_schema() -> DefaultSchemaRaw {
608 DefaultSchemaRaw::default()
609 }
610}
611
612#[cfg(not(feature = "nightly"))]
613impl<T: Responder> Apiv2Schema for ResponderWrapper<T> {}
614
615#[cfg(not(feature = "nightly"))]
616impl<T: Responder> OperationModifier for ResponderWrapper<T> {}
617
618#[cfg(feature = "actix4")]
619impl Apiv2Schema for actix_web::dev::Response<actix_web::body::BoxBody> {}
620
621#[cfg(feature = "actix4")]
622impl OperationModifier for actix_web::dev::Response<actix_web::body::BoxBody> {}
623
624#[cfg(feature = "actix4")]
625impl<T: Responder> Responder for ResponderWrapper<T> {
626 type Body = T::Body;
627
628 #[inline]
629 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
630 self.0.respond_to(req)
631 }
632}
633
634#[cfg(not(feature = "actix4"))]
635impl<T: Responder> Responder for ResponderWrapper<T> {
636 type Error = T::Error;
637 type Future = T::Future;
638
639 #[inline]
640 fn respond_to(self, req: &HttpRequest) -> Self::Future {
641 self.0.respond_to(req)
642 }
643}
644
645pin_project! {
649 pub struct ResponseWrapper<T, H> {
650 #[pin]
651 pub responder: T,
652 pub operations: H,
653 }
654}
655
656#[cfg(feature = "actix4")]
657impl<T: Responder, H> Responder for ResponseWrapper<T, H> {
658 type Body = T::Body;
659
660 #[inline]
661 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
662 self.responder.respond_to(req)
663 }
664}
665
666#[cfg(not(feature = "actix4"))]
667impl<T: Responder, H> Responder for ResponseWrapper<T, H> {
668 type Error = <T as Responder>::Error;
669 type Future = <T as Responder>::Future;
670
671 #[inline]
672 fn respond_to(self, req: &HttpRequest) -> Self::Future {
673 self.responder.respond_to(req)
674 }
675}
676
677impl<F, T, H> Future for ResponseWrapper<F, H>
678where
679 F: Future<Output = T>,
680 T: OperationModifier + Responder,
681 H: Apiv2Operation,
682{
683 type Output = T;
684
685 #[inline]
686 fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
687 let this = self.as_mut().project();
688 this.responder.poll(ctx)
689 }
690}
691
692impl<F, T, H> Apiv2Operation for ResponseWrapper<F, H>
693where
694 F: Future<Output = T>,
695 T: OperationModifier + Responder,
696 H: Apiv2Operation,
697{
698 fn operation() -> DefaultOperationRaw {
699 H::operation()
700 }
701
702 fn security_definitions() -> BTreeMap<String, SecurityScheme> {
703 H::security_definitions()
704 }
705
706 fn definitions() -> BTreeMap<String, DefaultSchemaRaw> {
707 H::definitions()
708 }
709
710 fn is_visible() -> bool {
711 H::is_visible()
712 }
713}
714
715fn update_definitions_from_schema_type<T>(map: &mut BTreeMap<String, DefaultSchemaRaw>)
717where
718 T: Apiv2Schema,
719{
720 let mut schema = T::schema_with_ref();
721 loop {
722 if let Some(s) = schema.items {
723 schema = *s;
724 continue;
725 } else if let Some(Either::Right(s)) = schema.extra_props {
726 schema = *s;
727 continue;
728 } else if let Some(n) = schema.name.take() {
729 schema.remove_refs();
730 map.insert(n, schema);
731 }
732
733 break;
734 }
735}
736
737fn update_parameter<T>(op: &mut DefaultOperationRaw)
738where
739 T: Apiv2Schema,
740{
741 for parameter in T::header_parameter_schema() {
742 op.parameters.push(Either::Right(parameter))
743 }
744}
745
746fn update_security<T>(op: &mut DefaultOperationRaw)
748where
749 T: Apiv2Schema,
750{
751 if let (Some(name), Some(scheme)) = (T::name(), T::security_scheme()) {
752 let mut security_map = BTreeMap::new();
753 let scopes = scheme.scopes.keys().map(String::clone).collect();
754 security_map.insert(name, scopes);
755 op.security.push(security_map);
756 }
757}
758
759fn update_security_definitions<T>(map: &mut BTreeMap<String, SecurityScheme>)
761where
762 T: Apiv2Schema,
763{
764 if let (Some(name), Some(new)) = (T::name(), T::security_scheme()) {
765 new.update_definitions(&name, map);
766 }
767}
768
769macro_rules! json_with_status {
770 ($name:ident => $status:expr) => {
771 pub struct $name<T: Serialize + Apiv2Schema>(pub T);
772
773 impl<T> fmt::Debug for $name<T>
774 where
775 T: fmt::Debug + Serialize + Apiv2Schema,
776 {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 let status: StatusCode = $status;
779 let status_str = status.canonical_reason().unwrap_or(status.as_str());
780 write!(f, "{} Json: {:?}", status_str, self.0)
781 }
782 }
783
784 impl<T> fmt::Display for $name<T>
785 where
786 T: fmt::Display + Serialize + Apiv2Schema,
787 {
788 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789 fmt::Display::fmt(&self.0, f)
790 }
791 }
792
793 #[cfg(feature = "actix4")]
794 impl<T> Responder for $name<T>
795 where
796 T: Serialize + Apiv2Schema,
797 {
798 type Body = BoxBody;
799
800 fn respond_to(self, _: &HttpRequest) -> HttpResponse<BoxBody> {
801 let status: StatusCode = $status;
802 let body = match serde_json::to_string(&self.0) {
803 Ok(body) => body,
804 Err(e) => return e.error_response(),
805 };
806
807 HttpResponse::build(status)
808 .content_type("application/json")
809 .body(body)
810 }
811 }
812
813 #[cfg(not(feature = "actix4"))]
814 impl<T> Responder for $name<T>
815 where
816 T: Serialize + Apiv2Schema,
817 {
818 type Error = Error;
819 type Future = Ready<Result<HttpResponse, Error>>;
820
821 fn respond_to(self, _: &HttpRequest) -> Self::Future {
822 let status: StatusCode = $status;
823 let body = match serde_json::to_string(&self.0) {
824 Ok(body) => body,
825 Err(e) => return ready(Err(e.into())),
826 };
827
828 ready(Ok(HttpResponse::build(status)
829 .content_type("application/json")
830 .body(body)))
831 }
832 }
833
834 impl<T> Apiv2Schema for $name<T>
835 where
836 T: Serialize + Apiv2Schema,
837 {
838 fn name() -> Option<String> {
839 T::name()
840 }
841
842 fn raw_schema() -> DefaultSchemaRaw {
843 T::raw_schema()
844 }
845 }
846
847 impl<T> OperationModifier for $name<T>
848 where
849 T: Serialize + Apiv2Schema,
850 {
851 fn update_response(op: &mut DefaultOperationRaw) {
852 let status: StatusCode = $status;
853 op.responses.insert(
854 status.as_str().into(),
855 Either::Right(Response {
856 description: status.canonical_reason().map(ToString::to_string),
857 schema: Some({
858 let mut def = T::schema_with_ref();
859 def.retain_ref();
860 def
861 }),
862 ..Default::default()
863 }),
864 );
865 }
866 }
867 };
868}
869
870json_with_status!(CreatedJson => StatusCode::CREATED);
871json_with_status!(AcceptedJson => StatusCode::ACCEPTED);
872
873#[derive(Debug)]
874pub struct NoContent;
875
876impl fmt::Display for NoContent {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 f.write_str("No Content")
879 }
880}
881
882#[cfg(feature = "actix4")]
883impl Responder for NoContent {
884 type Body = BoxBody;
885
886 fn respond_to(self, _: &HttpRequest) -> HttpResponse<BoxBody> {
887 HttpResponse::build(StatusCode::NO_CONTENT)
888 .content_type("application/json")
889 .finish()
890 }
891}
892
893#[cfg(not(feature = "actix4"))]
894impl Responder for NoContent {
895 type Error = Error;
896 type Future = Ready<Result<HttpResponse, Error>>;
897
898 fn respond_to(self, _: &HttpRequest) -> Self::Future {
899 ready(Ok(HttpResponse::build(StatusCode::NO_CONTENT)
900 .content_type("application/json")
901 .finish()))
902 }
903}
904
905impl Apiv2Schema for NoContent {}
906
907impl OperationModifier for NoContent {
908 fn update_response(op: &mut DefaultOperationRaw) {
909 let status = StatusCode::NO_CONTENT;
910 op.responses.insert(
911 status.as_str().into(),
912 Either::Right(Response {
913 description: status.canonical_reason().map(ToString::to_string),
914 schema: None,
915 ..Default::default()
916 }),
917 );
918 }
919}