1#![doc = include_str!("../docs/lib.md")]
2#![doc(html_favicon_url = "https://salvo.rs/favicon-32x32.png")]
3#![doc(html_logo_url = "https://salvo.rs/images/logo.svg")]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![cfg_attr(test, allow(clippy::unwrap_used))]
6
7#[macro_use]
8mod cfg;
9
10mod openapi;
11pub use openapi::*;
12
13#[doc = include_str!("../docs/endpoint.md")]
14pub mod endpoint;
15pub use endpoint::{Endpoint, EndpointArgRegister, EndpointOutRegister, EndpointRegistry};
16pub mod extract;
17mod routing;
18pub use routing::RouterExt;
19pub mod naming;
21
22cfg_feature! {
23 #![feature ="swagger-ui"]
24 pub mod swagger_ui;
25}
26cfg_feature! {
27 #![feature ="scalar"]
28 pub mod scalar;
29}
30cfg_feature! {
31 #![feature ="rapidoc"]
32 pub mod rapidoc;
33}
34cfg_feature! {
35 #![feature ="redoc"]
36 pub mod redoc;
37}
38
39use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList};
40use std::marker::PhantomData;
41
42use salvo_core::extract::Extractible;
43use salvo_core::http::StatusError;
44use salvo_core::writing;
45#[doc = include_str!("../docs/derive_to_parameters.md")]
46pub use salvo_oapi_macros::ToParameters;
47#[doc = include_str!("../docs/derive_to_response.md")]
48pub use salvo_oapi_macros::ToResponse;
49#[doc = include_str!("../docs/derive_to_responses.md")]
50pub use salvo_oapi_macros::ToResponses;
51#[doc = include_str!("../docs/derive_to_schema.md")]
52pub use salvo_oapi_macros::ToSchema;
53#[doc = include_str!("../docs/endpoint.md")]
54pub use salvo_oapi_macros::endpoint;
55pub(crate) use salvo_oapi_macros::schema;
56
57use crate::oapi::openapi::schema::OneOf;
58
59extern crate self as salvo_oapi;
61
62pub trait ToSchema {
128 fn to_schema(components: &mut Components) -> RefOr<schema::Schema>;
131}
132
133pub trait ComposeSchema {
170 fn compose(
176 components: &mut Components,
177 generics: Vec<RefOr<schema::Schema>>,
178 ) -> RefOr<schema::Schema>;
179}
180
181#[derive(Debug, Clone, Default)]
186pub struct SchemaReference {
187 pub name: std::borrow::Cow<'static, str>,
189 pub inline: bool,
191 pub references: Vec<Self>,
193}
194
195impl SchemaReference {
196 pub fn new(name: impl Into<std::borrow::Cow<'static, str>>) -> Self {
198 Self {
199 name: name.into(),
200 inline: false,
201 references: Vec::new(),
202 }
203 }
204
205 #[must_use]
207 pub fn inline(mut self, inline: bool) -> Self {
208 self.inline = inline;
209 self
210 }
211
212 #[must_use]
214 pub fn reference(mut self, reference: Self) -> Self {
215 self.references.push(reference);
216 self
217 }
218
219 #[must_use]
223 pub fn compose_name(&self) -> String {
224 if self.references.is_empty() {
225 self.name.to_string()
226 } else {
227 let generic_names: Vec<String> =
228 self.references.iter().map(|r| r.compose_name()).collect();
229 format!("{}<{}>", self.name, generic_names.join(", "))
230 }
231 }
232
233 #[must_use]
235 pub fn compose_generics(&self) -> &[Self] {
236 &self.references
237 }
238
239 #[must_use]
241 pub fn compose_child_references(&self) -> Vec<&Self> {
242 let mut result = Vec::new();
243 for reference in &self.references {
244 result.push(reference);
245 result.extend(reference.compose_child_references());
246 }
247 result
248 }
249}
250
251pub type TupleUnit = ();
257
258impl ToSchema for TupleUnit {
259 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
260 schema::empty().into()
261 }
262}
263impl ComposeSchema for TupleUnit {
264 fn compose(
265 _components: &mut Components,
266 _generics: Vec<RefOr<schema::Schema>>,
267 ) -> RefOr<schema::Schema> {
268 schema::empty().into()
269 }
270}
271
272macro_rules! impl_to_schema {
273 ($ty:path) => {
274 impl_to_schema!( @impl_schema $ty );
275 };
276 (&$ty:path) => {
277 impl_to_schema!( @impl_schema &$ty );
278 };
279 (@impl_schema $($tt:tt)*) => {
280 impl ToSchema for $($tt)* {
281 fn to_schema(_components: &mut Components) -> crate::RefOr<crate::schema::Schema> {
282 schema!( $($tt)* ).into()
283 }
284 }
285 impl ComposeSchema for $($tt)* {
286 fn compose(_components: &mut Components, _generics: Vec<crate::RefOr<crate::schema::Schema>>) -> crate::RefOr<crate::schema::Schema> {
287 schema!( $($tt)* ).into()
288 }
289 }
290 };
291}
292
293macro_rules! impl_to_schema_primitive {
294 ($($tt:path),*) => {
295 $( impl_to_schema!( $tt ); )*
296 };
297}
298
299#[doc(hidden)]
302pub mod oapi {
303 pub use super::*;
304}
305
306#[doc(hidden)]
307pub mod __private {
308 pub use inventory;
309 pub use serde_json;
310}
311
312#[rustfmt::skip]
313impl_to_schema_primitive!(
314 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
315);
316impl_to_schema!(&str);
317
318impl_to_schema!(std::net::Ipv4Addr);
319impl_to_schema!(std::net::Ipv6Addr);
320
321impl ToSchema for std::net::IpAddr {
322 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
323 crate::RefOr::Type(Schema::OneOf(
324 OneOf::default()
325 .item(std::net::Ipv4Addr::to_schema(components))
326 .item(std::net::Ipv6Addr::to_schema(components)),
327 ))
328 }
329}
330impl ComposeSchema for std::net::IpAddr {
331 fn compose(
332 components: &mut Components,
333 _generics: Vec<RefOr<schema::Schema>>,
334 ) -> RefOr<schema::Schema> {
335 Self::to_schema(components)
336 }
337}
338
339#[cfg(feature = "chrono")]
340impl_to_schema_primitive!(chrono::NaiveDate, chrono::Duration, chrono::NaiveDateTime);
341#[cfg(feature = "chrono")]
342impl<T: chrono::TimeZone> ToSchema for chrono::DateTime<T> {
343 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
344 schema!(#[inline] DateTime<T>).into()
345 }
346}
347#[cfg(feature = "chrono")]
348impl<T: chrono::TimeZone> ComposeSchema for chrono::DateTime<T> {
349 fn compose(
350 _components: &mut Components,
351 _generics: Vec<RefOr<schema::Schema>>,
352 ) -> RefOr<schema::Schema> {
353 schema!(#[inline] DateTime<T>).into()
354 }
355}
356#[cfg(feature = "compact_str")]
357impl_to_schema_primitive!(compact_str::CompactString);
358#[cfg(any(feature = "decimal", feature = "decimal-float"))]
359impl_to_schema!(rust_decimal::Decimal);
360#[cfg(feature = "url")]
361impl_to_schema!(url::Url);
362#[cfg(feature = "uuid")]
363impl_to_schema!(uuid::Uuid);
364#[cfg(feature = "ulid")]
365impl_to_schema!(ulid::Ulid);
366#[cfg(feature = "time")]
367impl_to_schema_primitive!(
368 time::Date,
369 time::PrimitiveDateTime,
370 time::OffsetDateTime,
371 time::Duration
372);
373#[cfg(feature = "smallvec")]
374impl<T: ToSchema + smallvec::Array> ToSchema for smallvec::SmallVec<T> {
375 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
376 schema!(#[inline] smallvec::SmallVec<T>).into()
377 }
378}
379#[cfg(feature = "smallvec")]
380impl<T: ComposeSchema + smallvec::Array> ComposeSchema for smallvec::SmallVec<T> {
381 fn compose(
382 components: &mut Components,
383 generics: Vec<RefOr<schema::Schema>>,
384 ) -> RefOr<schema::Schema> {
385 let t_schema = generics
386 .first()
387 .cloned()
388 .unwrap_or_else(|| T::compose(components, vec![]));
389 schema::Array::new().items(t_schema).into()
390 }
391}
392#[cfg(feature = "indexmap")]
393impl<K: ToSchema, V: ToSchema> ToSchema for indexmap::IndexMap<K, V> {
394 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
395 schema!(#[inline] indexmap::IndexMap<K, V>).into()
396 }
397}
398#[cfg(feature = "indexmap")]
399impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for indexmap::IndexMap<K, V> {
400 fn compose(
401 components: &mut Components,
402 generics: Vec<RefOr<schema::Schema>>,
403 ) -> RefOr<schema::Schema> {
404 let v_schema = generics
405 .get(1)
406 .cloned()
407 .unwrap_or_else(|| V::compose(components, vec![]));
408 schema::Object::new().additional_properties(v_schema).into()
409 }
410}
411
412impl<T: ToSchema> ToSchema for Vec<T> {
413 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
414 schema!(#[inline] Vec<T>).into()
415 }
416}
417impl<T: ComposeSchema> ComposeSchema for Vec<T> {
418 fn compose(
419 components: &mut Components,
420 generics: Vec<RefOr<schema::Schema>>,
421 ) -> RefOr<schema::Schema> {
422 let t_schema = generics
423 .first()
424 .cloned()
425 .unwrap_or_else(|| T::compose(components, vec![]));
426 schema::Array::new().items(t_schema).into()
427 }
428}
429
430impl<T: ToSchema> ToSchema for LinkedList<T> {
431 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
432 schema!(#[inline] LinkedList<T>).into()
433 }
434}
435impl<T: ComposeSchema> ComposeSchema for LinkedList<T> {
436 fn compose(
437 components: &mut Components,
438 generics: Vec<RefOr<schema::Schema>>,
439 ) -> RefOr<schema::Schema> {
440 let t_schema = generics
441 .first()
442 .cloned()
443 .unwrap_or_else(|| T::compose(components, vec![]));
444 schema::Array::new().items(t_schema).into()
445 }
446}
447
448impl<T: ToSchema> ToSchema for HashSet<T> {
449 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
450 schema::Array::new()
451 .items(T::to_schema(components))
452 .unique_items(true)
453 .into()
454 }
455}
456impl<T: ComposeSchema> ComposeSchema for HashSet<T> {
457 fn compose(
458 components: &mut Components,
459 generics: Vec<RefOr<schema::Schema>>,
460 ) -> RefOr<schema::Schema> {
461 let t_schema = generics
462 .first()
463 .cloned()
464 .unwrap_or_else(|| T::compose(components, vec![]));
465 schema::Array::new()
466 .items(t_schema)
467 .unique_items(true)
468 .into()
469 }
470}
471
472impl<T: ToSchema> ToSchema for BTreeSet<T> {
473 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
474 schema::Array::new()
475 .items(T::to_schema(components))
476 .unique_items(true)
477 .into()
478 }
479}
480impl<T: ComposeSchema> ComposeSchema for BTreeSet<T> {
481 fn compose(
482 components: &mut Components,
483 generics: Vec<RefOr<schema::Schema>>,
484 ) -> RefOr<schema::Schema> {
485 let t_schema = generics
486 .first()
487 .cloned()
488 .unwrap_or_else(|| T::compose(components, vec![]));
489 schema::Array::new()
490 .items(t_schema)
491 .unique_items(true)
492 .into()
493 }
494}
495
496#[cfg(feature = "indexmap")]
497impl<T: ToSchema> ToSchema for indexmap::IndexSet<T> {
498 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
499 schema::Array::new()
500 .items(T::to_schema(components))
501 .unique_items(true)
502 .into()
503 }
504}
505#[cfg(feature = "indexmap")]
506impl<T: ComposeSchema> ComposeSchema for indexmap::IndexSet<T> {
507 fn compose(
508 components: &mut Components,
509 generics: Vec<RefOr<schema::Schema>>,
510 ) -> RefOr<schema::Schema> {
511 let t_schema = generics
512 .first()
513 .cloned()
514 .unwrap_or_else(|| T::compose(components, vec![]));
515 schema::Array::new()
516 .items(t_schema)
517 .unique_items(true)
518 .into()
519 }
520}
521
522impl<T: ToSchema> ToSchema for Box<T> {
523 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
524 T::to_schema(components)
525 }
526}
527impl<T: ComposeSchema> ComposeSchema for Box<T> {
528 fn compose(
529 components: &mut Components,
530 generics: Vec<RefOr<schema::Schema>>,
531 ) -> RefOr<schema::Schema> {
532 T::compose(components, generics)
533 }
534}
535
536impl<T: ToSchema + ToOwned> ToSchema for std::borrow::Cow<'_, T> {
537 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
538 T::to_schema(components)
539 }
540}
541impl<T: ComposeSchema + ToOwned> ComposeSchema for std::borrow::Cow<'_, T> {
542 fn compose(
543 components: &mut Components,
544 generics: Vec<RefOr<schema::Schema>>,
545 ) -> RefOr<schema::Schema> {
546 T::compose(components, generics)
547 }
548}
549
550impl<T: ToSchema> ToSchema for std::cell::RefCell<T> {
551 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
552 T::to_schema(components)
553 }
554}
555impl<T: ComposeSchema> ComposeSchema for std::cell::RefCell<T> {
556 fn compose(
557 components: &mut Components,
558 generics: Vec<RefOr<schema::Schema>>,
559 ) -> RefOr<schema::Schema> {
560 T::compose(components, generics)
561 }
562}
563
564impl<T: ToSchema> ToSchema for std::rc::Rc<T> {
565 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
566 T::to_schema(components)
567 }
568}
569impl<T: ComposeSchema> ComposeSchema for std::rc::Rc<T> {
570 fn compose(
571 components: &mut Components,
572 generics: Vec<RefOr<schema::Schema>>,
573 ) -> RefOr<schema::Schema> {
574 T::compose(components, generics)
575 }
576}
577
578impl<T: ToSchema> ToSchema for std::sync::Arc<T> {
579 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
580 T::to_schema(components)
581 }
582}
583impl<T: ComposeSchema> ComposeSchema for std::sync::Arc<T> {
584 fn compose(
585 components: &mut Components,
586 generics: Vec<RefOr<schema::Schema>>,
587 ) -> RefOr<schema::Schema> {
588 T::compose(components, generics)
589 }
590}
591
592impl<T: ToSchema> ToSchema for [T] {
593 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
594 schema!(
595 #[inline]
596 [T]
597 )
598 .into()
599 }
600}
601impl<T: ComposeSchema> ComposeSchema for [T] {
602 fn compose(
603 components: &mut Components,
604 generics: Vec<RefOr<schema::Schema>>,
605 ) -> RefOr<schema::Schema> {
606 let t_schema = generics
607 .first()
608 .cloned()
609 .unwrap_or_else(|| T::compose(components, vec![]));
610 schema::Array::new().items(t_schema).into()
611 }
612}
613
614impl<T: ToSchema, const N: usize> ToSchema for [T; N] {
615 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
616 schema!(
617 #[inline]
618 [T; N]
619 )
620 .into()
621 }
622}
623impl<T: ComposeSchema, const N: usize> ComposeSchema for [T; N] {
624 fn compose(
625 components: &mut Components,
626 generics: Vec<RefOr<schema::Schema>>,
627 ) -> RefOr<schema::Schema> {
628 let t_schema = generics
629 .first()
630 .cloned()
631 .unwrap_or_else(|| T::compose(components, vec![]));
632 schema::Array::new().items(t_schema).into()
633 }
634}
635
636impl<T: ToSchema> ToSchema for &[T] {
637 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
638 schema!(
639 #[inline]
640 &[T]
641 )
642 .into()
643 }
644}
645impl<T: ComposeSchema> ComposeSchema for &[T] {
646 fn compose(
647 components: &mut Components,
648 generics: Vec<RefOr<schema::Schema>>,
649 ) -> RefOr<schema::Schema> {
650 let t_schema = generics
651 .first()
652 .cloned()
653 .unwrap_or_else(|| T::compose(components, vec![]));
654 schema::Array::new().items(t_schema).into()
655 }
656}
657
658impl<T: ToSchema> ToSchema for Option<T> {
659 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
660 schema!(#[inline] Option<T>).into()
661 }
662}
663impl<T: ComposeSchema> ComposeSchema for Option<T> {
664 fn compose(
665 components: &mut Components,
666 generics: Vec<RefOr<schema::Schema>>,
667 ) -> RefOr<schema::Schema> {
668 let t_schema = generics
669 .first()
670 .cloned()
671 .unwrap_or_else(|| T::compose(components, vec![]));
672 schema::OneOf::new()
673 .item(t_schema)
674 .item(schema::Object::new().schema_type(schema::BasicType::Null))
675 .into()
676 }
677}
678
679impl<T> ToSchema for PhantomData<T> {
680 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
681 Schema::Object(Box::default()).into()
682 }
683}
684impl<T> ComposeSchema for PhantomData<T> {
685 fn compose(
686 _components: &mut Components,
687 _generics: Vec<RefOr<schema::Schema>>,
688 ) -> RefOr<schema::Schema> {
689 Schema::Object(Box::default()).into()
690 }
691}
692
693impl<K: ToSchema, V: ToSchema> ToSchema for BTreeMap<K, V> {
694 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
695 schema!(#[inline]BTreeMap<K, V>).into()
696 }
697}
698impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for BTreeMap<K, V> {
699 fn compose(
700 components: &mut Components,
701 generics: Vec<RefOr<schema::Schema>>,
702 ) -> RefOr<schema::Schema> {
703 let v_schema = generics
704 .get(1)
705 .cloned()
706 .unwrap_or_else(|| V::compose(components, vec![]));
707 schema::Object::new().additional_properties(v_schema).into()
708 }
709}
710
711impl<K: ToSchema, V: ToSchema> ToSchema for HashMap<K, V> {
712 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
713 schema!(#[inline]HashMap<K, V>).into()
714 }
715}
716impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for HashMap<K, V> {
717 fn compose(
718 components: &mut Components,
719 generics: Vec<RefOr<schema::Schema>>,
720 ) -> RefOr<schema::Schema> {
721 let v_schema = generics
722 .get(1)
723 .cloned()
724 .unwrap_or_else(|| V::compose(components, vec![]));
725 schema::Object::new().additional_properties(v_schema).into()
726 }
727}
728
729impl ToSchema for StatusError {
730 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
731 let name = crate::naming::assign_name::<Self>(Default::default());
732 let ref_or = crate::RefOr::Ref(crate::Ref::new(format!("#/components/schemas/{name}")));
733 if !components.schemas.contains_key(&name) {
734 components.schemas.insert(name.clone(), ref_or.clone());
735 let schema = Schema::from(
736 Object::new()
737 .property("code", u16::to_schema(components))
738 .required("code")
739 .required("name")
740 .property("name", String::to_schema(components))
741 .required("brief")
742 .property("brief", String::to_schema(components))
743 .required("detail")
744 .property("detail", String::to_schema(components))
745 .property("cause", String::to_schema(components)),
746 );
747 components.schemas.insert(name, schema);
748 }
749 ref_or
750 }
751}
752impl ComposeSchema for StatusError {
753 fn compose(
754 components: &mut Components,
755 _generics: Vec<RefOr<schema::Schema>>,
756 ) -> RefOr<schema::Schema> {
757 Self::to_schema(components)
758 }
759}
760
761impl ToSchema for salvo_core::Error {
762 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
763 StatusError::to_schema(components)
764 }
765}
766impl ComposeSchema for salvo_core::Error {
767 fn compose(
768 components: &mut Components,
769 _generics: Vec<RefOr<schema::Schema>>,
770 ) -> RefOr<schema::Schema> {
771 Self::to_schema(components)
772 }
773}
774
775impl<T, E> ToSchema for Result<T, E>
776where
777 T: ToSchema,
778 E: ToSchema,
779{
780 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
781 let name = crate::naming::assign_name::<StatusError>(Default::default());
782 let ref_or = crate::RefOr::Ref(crate::Ref::new(format!("#/components/schemas/{name}")));
783 if !components.schemas.contains_key(&name) {
784 components.schemas.insert(name.clone(), ref_or.clone());
785 let schema = OneOf::new()
786 .item(T::to_schema(components))
787 .item(E::to_schema(components));
788 components.schemas.insert(name, schema);
789 }
790 ref_or
791 }
792}
793impl<T, E> ComposeSchema for Result<T, E>
794where
795 T: ComposeSchema,
796 E: ComposeSchema,
797{
798 fn compose(
799 components: &mut Components,
800 generics: Vec<RefOr<schema::Schema>>,
801 ) -> RefOr<schema::Schema> {
802 let t_schema = generics
803 .first()
804 .cloned()
805 .unwrap_or_else(|| T::compose(components, vec![]));
806 let e_schema = generics
807 .get(1)
808 .cloned()
809 .unwrap_or_else(|| E::compose(components, vec![]));
810 OneOf::new().item(t_schema).item(e_schema).into()
811 }
812}
813
814impl ToSchema for serde_json::Value {
815 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
816 Schema::Object(Box::default()).into()
817 }
818}
819impl ComposeSchema for serde_json::Value {
820 fn compose(
821 _components: &mut Components,
822 _generics: Vec<RefOr<schema::Schema>>,
823 ) -> RefOr<schema::Schema> {
824 Schema::Object(Box::default()).into()
825 }
826}
827
828impl ToSchema for serde_json::Map<String, serde_json::Value> {
829 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
830 Schema::Object(Box::new(schema::Object::new())).into()
831 }
832}
833impl ComposeSchema for serde_json::Map<String, serde_json::Value> {
834 fn compose(
835 _components: &mut Components,
836 _generics: Vec<RefOr<schema::Schema>>,
837 ) -> RefOr<schema::Schema> {
838 Schema::Object(Box::new(schema::Object::new())).into()
839 }
840}
841
842pub trait ToParameters<'de>: Extractible<'de> {
941 fn to_parameters(components: &mut Components) -> Parameters;
944}
945
946pub trait ToParameter {
948 fn to_parameter(components: &mut Components) -> Parameter;
950}
951
952pub trait ToRequestBody {
985 fn to_request_body(components: &mut Components) -> RequestBody;
987}
988
989pub trait ToResponses {
1013 fn to_responses(components: &mut Components) -> Responses;
1015}
1016
1017impl<C> ToResponses for writing::Json<C>
1018where
1019 C: ToSchema,
1020{
1021 fn to_responses(components: &mut Components) -> Responses {
1022 Responses::new().response(
1023 "200",
1024 Response::new("Response json format data")
1025 .add_content("application/json", Content::new(C::to_schema(components))),
1026 )
1027 }
1028}
1029
1030impl ToResponses for StatusError {
1031 fn to_responses(components: &mut Components) -> Responses {
1032 let mut responses = Responses::new();
1033 let errors = vec![
1034 Self::bad_request(),
1035 Self::unauthorized(),
1036 Self::payment_required(),
1037 Self::forbidden(),
1038 Self::not_found(),
1039 Self::method_not_allowed(),
1040 Self::not_acceptable(),
1041 Self::proxy_authentication_required(),
1042 Self::request_timeout(),
1043 Self::conflict(),
1044 Self::gone(),
1045 Self::length_required(),
1046 Self::precondition_failed(),
1047 Self::payload_too_large(),
1048 Self::uri_too_long(),
1049 Self::unsupported_media_type(),
1050 Self::range_not_satisfiable(),
1051 Self::expectation_failed(),
1052 Self::im_a_teapot(),
1053 Self::misdirected_request(),
1054 Self::unprocessable_entity(),
1055 Self::locked(),
1056 Self::failed_dependency(),
1057 Self::upgrade_required(),
1058 Self::precondition_required(),
1059 Self::too_many_requests(),
1060 Self::request_header_fields_toolarge(),
1061 Self::unavailable_for_legalreasons(),
1062 Self::internal_server_error(),
1063 Self::not_implemented(),
1064 Self::bad_gateway(),
1065 Self::service_unavailable(),
1066 Self::gateway_timeout(),
1067 Self::http_version_not_supported(),
1068 Self::variant_also_negotiates(),
1069 Self::insufficient_storage(),
1070 Self::loop_detected(),
1071 Self::not_extended(),
1072 Self::network_authentication_required(),
1073 ];
1074 for Self { code, brief, .. } in errors {
1075 responses.insert(
1076 code.as_str(),
1077 Response::new(brief).add_content(
1078 "application/json",
1079 Content::new(Self::to_schema(components)),
1080 ),
1081 )
1082 }
1083 responses
1084 }
1085}
1086impl ToResponses for salvo_core::Error {
1087 fn to_responses(components: &mut Components) -> Responses {
1088 StatusError::to_responses(components)
1089 }
1090}
1091
1092pub trait ToResponse {
1112 fn to_response(components: &mut Components) -> RefOr<crate::Response>;
1114}
1115
1116impl<C> ToResponse for writing::Json<C>
1117where
1118 C: ToSchema,
1119{
1120 fn to_response(components: &mut Components) -> RefOr<Response> {
1121 let schema = <C as ToSchema>::to_schema(components);
1122 Response::new("Response with json format data")
1123 .add_content("application/json", Content::new(schema))
1124 .into()
1125 }
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130 use assert_json_diff::assert_json_eq;
1131 use serde_json::json;
1132
1133 use super::*;
1134
1135 #[test]
1136 fn test_primitive_schema() {
1137 let mut components = Components::new();
1138
1139 let non_strict = cfg!(feature = "non-strict-integers");
1143
1144 for (name, schema, value) in [
1145 (
1146 "i8",
1147 i8::to_schema(&mut components),
1148 if non_strict {
1149 json!({"type": "integer", "format": "int8"})
1150 } else {
1151 json!({"type": "integer", "format": "int32"})
1152 },
1153 ),
1154 (
1155 "i16",
1156 i16::to_schema(&mut components),
1157 if non_strict {
1158 json!({"type": "integer", "format": "int16"})
1159 } else {
1160 json!({"type": "integer", "format": "int32"})
1161 },
1162 ),
1163 (
1164 "i32",
1165 i32::to_schema(&mut components),
1166 json!({"type": "integer", "format": "int32"}),
1167 ),
1168 (
1169 "i64",
1170 i64::to_schema(&mut components),
1171 json!({"type": "integer", "format": "int64"}),
1172 ),
1173 (
1174 "i128",
1175 i128::to_schema(&mut components),
1176 json!({"type": "integer"}),
1177 ),
1178 (
1179 "isize",
1180 isize::to_schema(&mut components),
1181 json!({"type": "integer"}),
1182 ),
1183 (
1184 "u8",
1185 u8::to_schema(&mut components),
1186 if non_strict {
1187 json!({"type": "integer", "format": "uint8", "minimum": 0})
1188 } else {
1189 json!({"type": "integer", "format": "int32", "minimum": 0})
1190 },
1191 ),
1192 (
1193 "u16",
1194 u16::to_schema(&mut components),
1195 if non_strict {
1196 json!({"type": "integer", "format": "uint16", "minimum": 0})
1197 } else {
1198 json!({"type": "integer", "format": "int32", "minimum": 0})
1199 },
1200 ),
1201 (
1202 "u32",
1203 u32::to_schema(&mut components),
1204 if non_strict {
1205 json!({"type": "integer", "format": "uint32", "minimum": 0})
1206 } else {
1207 json!({"type": "integer", "format": "int32", "minimum": 0})
1208 },
1209 ),
1210 (
1211 "u64",
1212 u64::to_schema(&mut components),
1213 if non_strict {
1214 json!({"type": "integer", "format": "uint64", "minimum": 0})
1215 } else {
1216 json!({"type": "integer", "format": "int64", "minimum": 0})
1217 },
1218 ),
1219 (
1220 "u128",
1221 u128::to_schema(&mut components),
1222 json!({"type": "integer", "minimum": 0}),
1223 ),
1224 (
1225 "usize",
1226 usize::to_schema(&mut components),
1227 json!({"type": "integer", "minimum": 0}),
1228 ),
1229 (
1230 "bool",
1231 bool::to_schema(&mut components),
1232 json!({"type": "boolean"}),
1233 ),
1234 (
1235 "str",
1236 str::to_schema(&mut components),
1237 json!({"type": "string"}),
1238 ),
1239 (
1240 "String",
1241 String::to_schema(&mut components),
1242 json!({"type": "string"}),
1243 ),
1244 (
1245 "char",
1246 char::to_schema(&mut components),
1247 json!({"type": "string"}),
1248 ),
1249 (
1250 "f32",
1251 f32::to_schema(&mut components),
1252 json!({"type": "number", "format": "float"}),
1253 ),
1254 (
1255 "f64",
1256 f64::to_schema(&mut components),
1257 json!({"type": "number", "format": "double"}),
1258 ),
1259 ] {
1260 println!(
1261 "{name}: {json}",
1262 json = serde_json::to_string(&schema).unwrap()
1263 );
1264 let schema = serde_json::to_value(schema).unwrap();
1265 assert_json_eq!(schema, value);
1266 }
1267 }
1268}