Skip to main content

doxa_docs/
inner_schema.rs

1//! [`InnerToSchema`] — transparent schema-registration trait for
2//! extractor wrappers.
3//!
4//! utoipa's own path macro collects schemas only from `request_body`
5//! and `responses`; schemas referenced transitively by `params(...)`
6//! entries are **not** auto-registered into `components.schemas`.
7//! That produces dangling `$ref`s whenever an `IntoParams`-derived
8//! struct has fields of non-primitive types (enums, nested structs,
9//! etc.).
10//!
11//! This trait closes the gap. Each extractor implements it to report
12//! the schemas its inner payload references, and the method macro's
13//! generated per-handler `IntoParams` struct carries a matching
14//! [`ApidocHandlerSchemas`](crate::ApidocHandlerSchemas) impl that
15//! walks every argument. The extended [`routes!`](crate::routes)
16//! macro calls both, so the schemas land in the final spec
17//! alongside what utoipa collects natively.
18//!
19//! ## Extension
20//!
21//! Transparent wrappers add one blanket impl, just like the role
22//! traits in [`crate::doc_traits`]:
23//!
24//! ```ignore
25//! impl<E: InnerToSchema> InnerToSchema for MyGuard<E> {
26//!     fn inner_schemas(out: &mut Vec<(String, RefOr<Schema>)>) {
27//!         E::inner_schemas(out)
28//!     }
29//! }
30//! ```
31//!
32//! Non-transparent extractors either implement it against their
33//! payload (`Query<T: ToSchema>` → `T::schemas(out)`) or leave it
34//! unimplemented. The macro layer uses autoref specialization so
35//! missing impls no-op rather than failing to compile.
36
37use utoipa::openapi::schema::Schema;
38use utoipa::openapi::RefOr;
39use utoipa::ToSchema;
40
41/// Contributes schemas referenced by an extractor's inner payload
42/// into the OpenAPI document's component registry.
43///
44/// See the module-level docs for the motivation.
45pub trait InnerToSchema {
46    /// Append referenced schemas to `out`. Implementations typically
47    /// forward to `T::schemas(out)` for a `ToSchema`-implementing
48    /// inner type.
49    fn inner_schemas(out: &mut Vec<(String, RefOr<Schema>)>);
50}
51
52impl<T: ToSchema> InnerToSchema for axum::extract::Query<T> {
53    fn inner_schemas(out: &mut Vec<(String, RefOr<Schema>)>) {
54        T::schemas(out);
55    }
56}
57
58impl<T: ToSchema> InnerToSchema for axum::extract::Path<T> {
59    fn inner_schemas(out: &mut Vec<(String, RefOr<Schema>)>) {
60        T::schemas(out);
61    }
62}
63
64impl<T: ToSchema> InnerToSchema for axum::Json<T> {
65    fn inner_schemas(out: &mut Vec<(String, RefOr<Schema>)>) {
66        T::schemas(out);
67    }
68}
69
70// `Header<H>` has a fixed string schema — nothing to register.
71
72// ---------------------------------------------------------------------------
73// Handler-side trait: the per-handler dispatch struct implements this
74// trait, iterating every arg type through the autoref probe.
75// ---------------------------------------------------------------------------
76
77/// Reports the full set of schemas a handler's arguments reference.
78/// The method macro emits an impl for the dispatch struct; the
79/// extended [`routes!`](crate::routes) macro calls it to extend the
80/// OpenAPI router's schema collection before the router is merged.
81pub trait ApidocHandlerSchemas {
82    /// Append all schemas transitively referenced by the handler's
83    /// arguments to `out`.
84    fn collect(out: &mut Vec<(String, RefOr<Schema>)>);
85}