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