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_to_schema_primitive!(
328 std::ffi::OsStr,
329 std::ffi::OsString,
330 std::path::Path,
331 std::path::PathBuf
332);
333impl_to_schema!(&std::ffi::OsStr);
334impl_to_schema!(&std::path::Path);
335
336impl ToSchema for std::net::IpAddr {
337 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
338 crate::RefOr::Type(Schema::OneOf(
339 OneOf::default()
340 .item(std::net::Ipv4Addr::to_schema(components))
341 .item(std::net::Ipv6Addr::to_schema(components)),
342 ))
343 }
344}
345impl ComposeSchema for std::net::IpAddr {
346 fn compose(
347 components: &mut Components,
348 _generics: Vec<RefOr<schema::Schema>>,
349 ) -> RefOr<schema::Schema> {
350 Self::to_schema(components)
351 }
352}
353
354#[cfg(feature = "chrono")]
355impl_to_schema_primitive!(chrono::NaiveDate, chrono::Duration, chrono::NaiveDateTime);
356#[cfg(feature = "chrono")]
357impl<T: chrono::TimeZone> ToSchema for chrono::DateTime<T> {
358 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
359 schema!(#[inline] DateTime<T>).into()
360 }
361}
362#[cfg(feature = "chrono")]
363impl<T: chrono::TimeZone> ComposeSchema for chrono::DateTime<T> {
364 fn compose(
365 _components: &mut Components,
366 _generics: Vec<RefOr<schema::Schema>>,
367 ) -> RefOr<schema::Schema> {
368 schema!(#[inline] DateTime<T>).into()
369 }
370}
371#[cfg(feature = "compact_str")]
372impl_to_schema_primitive!(compact_str::CompactString);
373#[cfg(any(feature = "decimal", feature = "decimal-float"))]
374impl_to_schema!(rust_decimal::Decimal);
375#[cfg(feature = "url")]
376impl_to_schema!(url::Url);
377#[cfg(feature = "uuid")]
378impl_to_schema!(uuid::Uuid);
379#[cfg(feature = "ulid")]
380impl_to_schema!(ulid::Ulid);
381#[cfg(feature = "time")]
382impl_to_schema_primitive!(
383 time::Date,
384 time::PrimitiveDateTime,
385 time::OffsetDateTime,
386 time::Duration
387);
388#[cfg(feature = "smallvec")]
389impl<T: ToSchema + smallvec::Array> ToSchema for smallvec::SmallVec<T> {
390 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
391 schema!(#[inline] smallvec::SmallVec<T>).into()
392 }
393}
394#[cfg(feature = "smallvec")]
395impl<T: ComposeSchema + smallvec::Array> ComposeSchema for smallvec::SmallVec<T> {
396 fn compose(
397 components: &mut Components,
398 generics: Vec<RefOr<schema::Schema>>,
399 ) -> RefOr<schema::Schema> {
400 let t_schema = generics
401 .first()
402 .cloned()
403 .unwrap_or_else(|| T::compose(components, vec![]));
404 schema::Array::new().items(t_schema).into()
405 }
406}
407#[cfg(feature = "indexmap")]
408impl<K: ToSchema, V: ToSchema> ToSchema for indexmap::IndexMap<K, V> {
409 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
410 schema!(#[inline] indexmap::IndexMap<K, V>).into()
411 }
412}
413#[cfg(feature = "indexmap")]
414impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for indexmap::IndexMap<K, V> {
415 fn compose(
416 components: &mut Components,
417 generics: Vec<RefOr<schema::Schema>>,
418 ) -> RefOr<schema::Schema> {
419 let v_schema = generics
420 .get(1)
421 .cloned()
422 .unwrap_or_else(|| V::compose(components, vec![]));
423 schema::Object::new().additional_properties(v_schema).into()
424 }
425}
426
427impl<T: ToSchema> ToSchema for Vec<T> {
428 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
429 schema!(#[inline] Vec<T>).into()
430 }
431}
432impl<T: ComposeSchema> ComposeSchema for Vec<T> {
433 fn compose(
434 components: &mut Components,
435 generics: Vec<RefOr<schema::Schema>>,
436 ) -> RefOr<schema::Schema> {
437 let t_schema = generics
438 .first()
439 .cloned()
440 .unwrap_or_else(|| T::compose(components, vec![]));
441 schema::Array::new().items(t_schema).into()
442 }
443}
444
445impl<T: ToSchema> ToSchema for LinkedList<T> {
446 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
447 schema!(#[inline] LinkedList<T>).into()
448 }
449}
450impl<T: ComposeSchema> ComposeSchema for LinkedList<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().items(t_schema).into()
460 }
461}
462
463impl<T: ToSchema> ToSchema for HashSet<T> {
464 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
465 schema::Array::new()
466 .items(T::to_schema(components))
467 .unique_items(true)
468 .into()
469 }
470}
471impl<T: ComposeSchema> ComposeSchema for HashSet<T> {
472 fn compose(
473 components: &mut Components,
474 generics: Vec<RefOr<schema::Schema>>,
475 ) -> RefOr<schema::Schema> {
476 let t_schema = generics
477 .first()
478 .cloned()
479 .unwrap_or_else(|| T::compose(components, vec![]));
480 schema::Array::new()
481 .items(t_schema)
482 .unique_items(true)
483 .into()
484 }
485}
486
487impl<T: ToSchema> ToSchema for BTreeSet<T> {
488 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
489 schema::Array::new()
490 .items(T::to_schema(components))
491 .unique_items(true)
492 .into()
493 }
494}
495impl<T: ComposeSchema> ComposeSchema for BTreeSet<T> {
496 fn compose(
497 components: &mut Components,
498 generics: Vec<RefOr<schema::Schema>>,
499 ) -> RefOr<schema::Schema> {
500 let t_schema = generics
501 .first()
502 .cloned()
503 .unwrap_or_else(|| T::compose(components, vec![]));
504 schema::Array::new()
505 .items(t_schema)
506 .unique_items(true)
507 .into()
508 }
509}
510
511#[cfg(feature = "indexmap")]
512impl<T: ToSchema> ToSchema for indexmap::IndexSet<T> {
513 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
514 schema::Array::new()
515 .items(T::to_schema(components))
516 .unique_items(true)
517 .into()
518 }
519}
520#[cfg(feature = "indexmap")]
521impl<T: ComposeSchema> ComposeSchema for indexmap::IndexSet<T> {
522 fn compose(
523 components: &mut Components,
524 generics: Vec<RefOr<schema::Schema>>,
525 ) -> RefOr<schema::Schema> {
526 let t_schema = generics
527 .first()
528 .cloned()
529 .unwrap_or_else(|| T::compose(components, vec![]));
530 schema::Array::new()
531 .items(t_schema)
532 .unique_items(true)
533 .into()
534 }
535}
536
537impl<T: ToSchema> ToSchema for Box<T> {
538 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
539 T::to_schema(components)
540 }
541}
542impl<T: ComposeSchema> ComposeSchema for Box<T> {
543 fn compose(
544 components: &mut Components,
545 generics: Vec<RefOr<schema::Schema>>,
546 ) -> RefOr<schema::Schema> {
547 T::compose(components, generics)
548 }
549}
550
551impl<T: ToSchema + ToOwned> ToSchema for std::borrow::Cow<'_, T> {
552 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
553 T::to_schema(components)
554 }
555}
556impl<T: ComposeSchema + ToOwned> ComposeSchema for std::borrow::Cow<'_, T> {
557 fn compose(
558 components: &mut Components,
559 generics: Vec<RefOr<schema::Schema>>,
560 ) -> RefOr<schema::Schema> {
561 T::compose(components, generics)
562 }
563}
564
565impl<T: ToSchema> ToSchema for std::cell::RefCell<T> {
566 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
567 T::to_schema(components)
568 }
569}
570impl<T: ComposeSchema> ComposeSchema for std::cell::RefCell<T> {
571 fn compose(
572 components: &mut Components,
573 generics: Vec<RefOr<schema::Schema>>,
574 ) -> RefOr<schema::Schema> {
575 T::compose(components, generics)
576 }
577}
578
579impl<T: ToSchema> ToSchema for std::rc::Rc<T> {
580 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
581 T::to_schema(components)
582 }
583}
584impl<T: ComposeSchema> ComposeSchema for std::rc::Rc<T> {
585 fn compose(
586 components: &mut Components,
587 generics: Vec<RefOr<schema::Schema>>,
588 ) -> RefOr<schema::Schema> {
589 T::compose(components, generics)
590 }
591}
592
593impl<T: ToSchema> ToSchema for std::sync::Arc<T> {
594 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
595 T::to_schema(components)
596 }
597}
598impl<T: ComposeSchema> ComposeSchema for std::sync::Arc<T> {
599 fn compose(
600 components: &mut Components,
601 generics: Vec<RefOr<schema::Schema>>,
602 ) -> RefOr<schema::Schema> {
603 T::compose(components, generics)
604 }
605}
606
607impl<T: ToSchema> ToSchema for [T] {
608 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
609 schema!(
610 #[inline]
611 [T]
612 )
613 .into()
614 }
615}
616impl<T: ComposeSchema> ComposeSchema for [T] {
617 fn compose(
618 components: &mut Components,
619 generics: Vec<RefOr<schema::Schema>>,
620 ) -> RefOr<schema::Schema> {
621 let t_schema = generics
622 .first()
623 .cloned()
624 .unwrap_or_else(|| T::compose(components, vec![]));
625 schema::Array::new().items(t_schema).into()
626 }
627}
628
629impl<T: ToSchema, const N: usize> ToSchema for [T; N] {
630 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
631 schema!(
632 #[inline]
633 [T; N]
634 )
635 .into()
636 }
637}
638impl<T: ComposeSchema, const N: usize> ComposeSchema for [T; N] {
639 fn compose(
640 components: &mut Components,
641 generics: Vec<RefOr<schema::Schema>>,
642 ) -> RefOr<schema::Schema> {
643 let t_schema = generics
644 .first()
645 .cloned()
646 .unwrap_or_else(|| T::compose(components, vec![]));
647 schema::Array::new().items(t_schema).into()
648 }
649}
650
651impl<T: ToSchema> ToSchema for &[T] {
652 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
653 schema!(
654 #[inline]
655 &[T]
656 )
657 .into()
658 }
659}
660impl<T: ComposeSchema> ComposeSchema for &[T] {
661 fn compose(
662 components: &mut Components,
663 generics: Vec<RefOr<schema::Schema>>,
664 ) -> RefOr<schema::Schema> {
665 let t_schema = generics
666 .first()
667 .cloned()
668 .unwrap_or_else(|| T::compose(components, vec![]));
669 schema::Array::new().items(t_schema).into()
670 }
671}
672
673impl<T: ToSchema> ToSchema for Option<T> {
674 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
675 schema!(#[inline] Option<T>).into()
676 }
677}
678impl<T: ComposeSchema> ComposeSchema for Option<T> {
679 fn compose(
680 components: &mut Components,
681 generics: Vec<RefOr<schema::Schema>>,
682 ) -> RefOr<schema::Schema> {
683 let t_schema = generics
684 .first()
685 .cloned()
686 .unwrap_or_else(|| T::compose(components, vec![]));
687 schema::OneOf::new()
688 .item(t_schema)
689 .item(schema::Object::new().schema_type(schema::BasicType::Null))
690 .into()
691 }
692}
693
694impl<T> ToSchema for PhantomData<T> {
695 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
696 Schema::Object(Box::default()).into()
697 }
698}
699impl<T> ComposeSchema for PhantomData<T> {
700 fn compose(
701 _components: &mut Components,
702 _generics: Vec<RefOr<schema::Schema>>,
703 ) -> RefOr<schema::Schema> {
704 Schema::Object(Box::default()).into()
705 }
706}
707
708impl<K: ToSchema, V: ToSchema> ToSchema for BTreeMap<K, V> {
709 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
710 schema!(#[inline]BTreeMap<K, V>).into()
711 }
712}
713impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for BTreeMap<K, V> {
714 fn compose(
715 components: &mut Components,
716 generics: Vec<RefOr<schema::Schema>>,
717 ) -> RefOr<schema::Schema> {
718 let v_schema = generics
719 .get(1)
720 .cloned()
721 .unwrap_or_else(|| V::compose(components, vec![]));
722 schema::Object::new().additional_properties(v_schema).into()
723 }
724}
725
726impl<K: ToSchema, V: ToSchema> ToSchema for HashMap<K, V> {
727 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
728 schema!(#[inline]HashMap<K, V>).into()
729 }
730}
731impl<K: ComposeSchema, V: ComposeSchema> ComposeSchema for HashMap<K, V> {
732 fn compose(
733 components: &mut Components,
734 generics: Vec<RefOr<schema::Schema>>,
735 ) -> RefOr<schema::Schema> {
736 let v_schema = generics
737 .get(1)
738 .cloned()
739 .unwrap_or_else(|| V::compose(components, vec![]));
740 schema::Object::new().additional_properties(v_schema).into()
741 }
742}
743
744impl ToSchema for StatusError {
745 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
746 let name = crate::naming::assign_name::<Self>(Default::default());
747 let ref_or = crate::RefOr::Ref(crate::Ref::new(format!("#/components/schemas/{name}")));
748 if !components.schemas.contains_key(&name) {
749 components.schemas.insert(name.clone(), ref_or.clone());
750 let schema = Schema::from(
751 Object::new()
752 .property("code", u16::to_schema(components))
753 .required("code")
754 .required("name")
755 .property("name", String::to_schema(components))
756 .required("brief")
757 .property("brief", String::to_schema(components))
758 .required("detail")
759 .property("detail", String::to_schema(components))
760 .property("cause", String::to_schema(components)),
761 );
762 components.schemas.insert(name, schema);
763 }
764 ref_or
765 }
766}
767impl ComposeSchema for StatusError {
768 fn compose(
769 components: &mut Components,
770 _generics: Vec<RefOr<schema::Schema>>,
771 ) -> RefOr<schema::Schema> {
772 Self::to_schema(components)
773 }
774}
775
776impl ToSchema for salvo_core::Error {
777 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
778 StatusError::to_schema(components)
779 }
780}
781impl ComposeSchema for salvo_core::Error {
782 fn compose(
783 components: &mut Components,
784 _generics: Vec<RefOr<schema::Schema>>,
785 ) -> RefOr<schema::Schema> {
786 Self::to_schema(components)
787 }
788}
789
790impl<T, E> ToSchema for Result<T, E>
791where
792 T: ToSchema,
793 E: ToSchema,
794{
795 fn to_schema(components: &mut Components) -> RefOr<schema::Schema> {
796 let name = crate::naming::assign_name::<StatusError>(Default::default());
797 let ref_or = crate::RefOr::Ref(crate::Ref::new(format!("#/components/schemas/{name}")));
798 if !components.schemas.contains_key(&name) {
799 components.schemas.insert(name.clone(), ref_or.clone());
800 let schema = OneOf::new()
801 .item(T::to_schema(components))
802 .item(E::to_schema(components));
803 components.schemas.insert(name, schema);
804 }
805 ref_or
806 }
807}
808impl<T, E> ComposeSchema for Result<T, E>
809where
810 T: ComposeSchema,
811 E: ComposeSchema,
812{
813 fn compose(
814 components: &mut Components,
815 generics: Vec<RefOr<schema::Schema>>,
816 ) -> RefOr<schema::Schema> {
817 let t_schema = generics
818 .first()
819 .cloned()
820 .unwrap_or_else(|| T::compose(components, vec![]));
821 let e_schema = generics
822 .get(1)
823 .cloned()
824 .unwrap_or_else(|| E::compose(components, vec![]));
825 OneOf::new().item(t_schema).item(e_schema).into()
826 }
827}
828
829impl ToSchema for serde_json::Value {
830 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
831 Schema::Object(Box::default()).into()
832 }
833}
834impl ComposeSchema for serde_json::Value {
835 fn compose(
836 _components: &mut Components,
837 _generics: Vec<RefOr<schema::Schema>>,
838 ) -> RefOr<schema::Schema> {
839 Schema::Object(Box::default()).into()
840 }
841}
842
843impl ToSchema for serde_json::Map<String, serde_json::Value> {
844 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
845 Schema::Object(Box::new(schema::Object::new())).into()
846 }
847}
848impl ComposeSchema for serde_json::Map<String, serde_json::Value> {
849 fn compose(
850 _components: &mut Components,
851 _generics: Vec<RefOr<schema::Schema>>,
852 ) -> RefOr<schema::Schema> {
853 Schema::Object(Box::new(schema::Object::new())).into()
854 }
855}
856
857impl ToSchema for serde_json::value::RawValue {
858 fn to_schema(_components: &mut Components) -> RefOr<schema::Schema> {
859 Schema::Object(Box::default()).into()
860 }
861}
862impl ComposeSchema for serde_json::value::RawValue {
863 fn compose(
864 _components: &mut Components,
865 _generics: Vec<RefOr<schema::Schema>>,
866 ) -> RefOr<schema::Schema> {
867 Schema::Object(Box::default()).into()
868 }
869}
870
871pub trait ToParameters<'de>: Extractible<'de> {
970 fn to_parameters(components: &mut Components) -> Parameters;
973}
974
975pub trait ToParameter {
977 fn to_parameter(components: &mut Components) -> Parameter;
979}
980
981pub trait ToRequestBody {
1014 fn to_request_body(components: &mut Components) -> RequestBody;
1016}
1017
1018pub trait ToResponses {
1042 fn to_responses(components: &mut Components) -> Responses;
1044}
1045
1046impl<C> ToResponses for writing::Json<C>
1047where
1048 C: ToSchema,
1049{
1050 fn to_responses(components: &mut Components) -> Responses {
1051 Responses::new().response(
1052 "200",
1053 Response::new("JSON response body")
1054 .add_content("application/json", Content::new(C::to_schema(components))),
1055 )
1056 }
1057}
1058
1059impl ToResponses for StatusError {
1060 fn to_responses(components: &mut Components) -> Responses {
1061 let mut responses = Responses::new();
1062 let errors = vec![
1063 Self::bad_request(),
1064 Self::unauthorized(),
1065 Self::payment_required(),
1066 Self::forbidden(),
1067 Self::not_found(),
1068 Self::method_not_allowed(),
1069 Self::not_acceptable(),
1070 Self::proxy_authentication_required(),
1071 Self::request_timeout(),
1072 Self::conflict(),
1073 Self::gone(),
1074 Self::length_required(),
1075 Self::precondition_failed(),
1076 Self::payload_too_large(),
1077 Self::uri_too_long(),
1078 Self::unsupported_media_type(),
1079 Self::range_not_satisfiable(),
1080 Self::expectation_failed(),
1081 Self::im_a_teapot(),
1082 Self::misdirected_request(),
1083 Self::unprocessable_entity(),
1084 Self::locked(),
1085 Self::failed_dependency(),
1086 Self::upgrade_required(),
1087 Self::precondition_required(),
1088 Self::too_many_requests(),
1089 Self::request_header_fields_too_large(),
1090 Self::unavailable_for_legal_reasons(),
1091 Self::internal_server_error(),
1092 Self::not_implemented(),
1093 Self::bad_gateway(),
1094 Self::service_unavailable(),
1095 Self::gateway_timeout(),
1096 Self::http_version_not_supported(),
1097 Self::variant_also_negotiates(),
1098 Self::insufficient_storage(),
1099 Self::loop_detected(),
1100 Self::not_extended(),
1101 Self::network_authentication_required(),
1102 ];
1103 for Self { code, brief, .. } in errors {
1104 responses.insert(
1105 code.as_str(),
1106 Response::new(brief).add_content(
1107 "application/json",
1108 Content::new(Self::to_schema(components)),
1109 ),
1110 )
1111 }
1112 responses
1113 }
1114}
1115impl ToResponses for salvo_core::Error {
1116 fn to_responses(components: &mut Components) -> Responses {
1117 StatusError::to_responses(components)
1118 }
1119}
1120
1121pub trait ToResponse {
1141 fn to_response(components: &mut Components) -> RefOr<crate::Response>;
1143}
1144
1145impl<C> ToResponse for writing::Json<C>
1146where
1147 C: ToSchema,
1148{
1149 fn to_response(components: &mut Components) -> RefOr<Response> {
1150 let schema = <C as ToSchema>::to_schema(components);
1151 Response::new("Response with json format data")
1152 .add_content("application/json", Content::new(schema))
1153 .into()
1154 }
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use assert_json_diff::assert_json_eq;
1160 use serde_json::json;
1161
1162 use super::*;
1163
1164 #[test]
1165 fn test_primitive_schema() {
1166 let mut components = Components::new();
1167
1168 let non_strict = cfg!(feature = "non-strict-integers");
1172
1173 for (name, schema, value) in [
1174 (
1175 "i8",
1176 i8::to_schema(&mut components),
1177 if non_strict {
1178 json!({"type": "integer", "format": "int8"})
1179 } else {
1180 json!({"type": "integer", "format": "int32"})
1181 },
1182 ),
1183 (
1184 "i16",
1185 i16::to_schema(&mut components),
1186 if non_strict {
1187 json!({"type": "integer", "format": "int16"})
1188 } else {
1189 json!({"type": "integer", "format": "int32"})
1190 },
1191 ),
1192 (
1193 "i32",
1194 i32::to_schema(&mut components),
1195 json!({"type": "integer", "format": "int32"}),
1196 ),
1197 (
1198 "i64",
1199 i64::to_schema(&mut components),
1200 json!({"type": "integer", "format": "int64"}),
1201 ),
1202 (
1203 "i128",
1204 i128::to_schema(&mut components),
1205 json!({"type": "integer"}),
1206 ),
1207 (
1208 "isize",
1209 isize::to_schema(&mut components),
1210 json!({"type": "integer"}),
1211 ),
1212 (
1213 "u8",
1214 u8::to_schema(&mut components),
1215 if non_strict {
1216 json!({"type": "integer", "format": "uint8", "minimum": 0})
1217 } else {
1218 json!({"type": "integer", "format": "int32", "minimum": 0})
1219 },
1220 ),
1221 (
1222 "u16",
1223 u16::to_schema(&mut components),
1224 if non_strict {
1225 json!({"type": "integer", "format": "uint16", "minimum": 0})
1226 } else {
1227 json!({"type": "integer", "format": "int32", "minimum": 0})
1228 },
1229 ),
1230 (
1231 "u32",
1232 u32::to_schema(&mut components),
1233 if non_strict {
1234 json!({"type": "integer", "format": "uint32", "minimum": 0})
1235 } else {
1236 json!({"type": "integer", "format": "int32", "minimum": 0})
1237 },
1238 ),
1239 (
1240 "u64",
1241 u64::to_schema(&mut components),
1242 if non_strict {
1243 json!({"type": "integer", "format": "uint64", "minimum": 0})
1244 } else {
1245 json!({"type": "integer", "format": "int64", "minimum": 0})
1246 },
1247 ),
1248 (
1249 "u128",
1250 u128::to_schema(&mut components),
1251 json!({"type": "integer", "minimum": 0}),
1252 ),
1253 (
1254 "usize",
1255 usize::to_schema(&mut components),
1256 json!({"type": "integer", "minimum": 0}),
1257 ),
1258 (
1259 "bool",
1260 bool::to_schema(&mut components),
1261 json!({"type": "boolean"}),
1262 ),
1263 (
1264 "str",
1265 str::to_schema(&mut components),
1266 json!({"type": "string"}),
1267 ),
1268 (
1269 "String",
1270 String::to_schema(&mut components),
1271 json!({"type": "string"}),
1272 ),
1273 (
1274 "char",
1275 char::to_schema(&mut components),
1276 json!({"type": "string"}),
1277 ),
1278 (
1279 "OsStr",
1280 std::ffi::OsStr::to_schema(&mut components),
1281 json!({"type": "string"}),
1282 ),
1283 (
1284 "&OsStr",
1285 <&std::ffi::OsStr>::to_schema(&mut components),
1286 json!({"type": "string"}),
1287 ),
1288 (
1289 "OsString",
1290 std::ffi::OsString::to_schema(&mut components),
1291 json!({"type": "string"}),
1292 ),
1293 (
1294 "Path",
1295 std::path::Path::to_schema(&mut components),
1296 json!({"type": "string"}),
1297 ),
1298 (
1299 "&Path",
1300 <&std::path::Path>::to_schema(&mut components),
1301 json!({"type": "string"}),
1302 ),
1303 (
1304 "PathBuf",
1305 std::path::PathBuf::to_schema(&mut components),
1306 json!({"type": "string"}),
1307 ),
1308 (
1309 "f32",
1310 f32::to_schema(&mut components),
1311 json!({"type": "number", "format": "float"}),
1312 ),
1313 (
1314 "f64",
1315 f64::to_schema(&mut components),
1316 json!({"type": "number", "format": "double"}),
1317 ),
1318 ] {
1319 println!(
1320 "{name}: {json}",
1321 json = serde_json::to_string(&schema).unwrap()
1322 );
1323 let schema = serde_json::to_value(schema).unwrap();
1324 assert_json_eq!(schema, value);
1325 }
1326 }
1327}