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