diff --git a/utoipa-gen/CHANGELOG.md b/utoipa-gen/CHANGELOG.md index 043cc3df..d3fb8173 100644 --- a/utoipa-gen/CHANGELOG.md +++ b/utoipa-gen/CHANGELOG.md @@ -50,6 +50,7 @@ ### Changed +* Move `schemas` into `ToSchema` for schemas (https://github.com/juhaku/utoipa/pull/1112) * Refactor `KnownFormat` * Add path rewrite support (https://github.com/juhaku/utoipa/pull/1110) * Fix broken tests diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs index 9ecea9b3..4901717d 100644 --- a/utoipa-gen/src/component.rs +++ b/utoipa-gen/src/component.rs @@ -1209,7 +1209,8 @@ impl ComponentSchema { } }; object_schema_reference.tokens = items_tokens.clone(); - object_schema_reference.references = quote! { <#rewritten_path as utoipa::__dev::SchemaReferences>::schemas(schemas) }; + object_schema_reference.references = + quote! { <#rewritten_path as utoipa::ToSchema>::schemas(schemas) }; let description_tokens = description_stream.to_token_stream(); let schema = if default.is_some() @@ -1242,7 +1243,8 @@ impl ComponentSchema { quote! { <#rewritten_path as utoipa::PartialSchema>::schema() } }; object_schema_reference.tokens = reference_tokens; - object_schema_reference.references = quote! { <#rewritten_path as utoipa::__dev::SchemaReferences>::schemas(schemas) }; + object_schema_reference.references = + quote! { <#rewritten_path as utoipa::ToSchema>::schemas(schemas) }; } let composed_or_ref = |item_tokens: TokenStream| -> TokenStream { if let Some(index) = &index { @@ -1414,7 +1416,7 @@ impl ComponentSchema { Ok(ChildRefIter::Once(std::iter::once(SchemaReference { name: quote! { String::from(< #rewritten_path as utoipa::ToSchema >::name().as_ref()) }, tokens: quote! { <#rewritten_path as utoipa::PartialSchema>::schema() }, - references: quote !{ <#rewritten_path as utoipa::__dev::SchemaReferences>::schemas(schemas) }, + references: quote !{ <#rewritten_path as utoipa::ToSchema>::schemas(schemas) }, })) ) } else { diff --git a/utoipa-gen/src/component/schema.rs b/utoipa-gen/src/component/schema.rs index d942b951..50faf241 100644 --- a/utoipa-gen/src/component/schema.rs +++ b/utoipa-gen/src/component/schema.rs @@ -138,9 +138,7 @@ impl ToTokensDiagnostics for Schema<'_> { fn name() -> std::borrow::Cow<'static, str> { std::borrow::Cow::Borrowed(#name) } - } - impl #impl_generics utoipa::__dev::SchemaReferences for #ident #ty_generics #where_clause { fn schemas(schemas: &mut Vec<(String, utoipa::openapi::RefOr)>) { schemas.extend(#schema_refs); #references; diff --git a/utoipa-gen/src/openapi.rs b/utoipa-gen/src/openapi.rs index 6aeecfe2..db0afcc9 100644 --- a/utoipa-gen/src/openapi.rs +++ b/utoipa-gen/src/openapi.rs @@ -656,7 +656,7 @@ impl crate::ToTokensDiagnostics for Components { components.extend(quote! { .schemas_from_iter( { let mut schemas = Vec::<(String, utoipa::openapi::RefOr)>::new(); - <#type_path as utoipa::__dev::SchemaReferences>::schemas(&mut schemas); + <#type_path as utoipa::ToSchema>::schemas(&mut schemas); schemas } )}); components.extend(quote! { .schema(#name, #schema) }); diff --git a/utoipa-gen/tests/schema_derive_test.rs b/utoipa-gen/tests/schema_derive_test.rs index c914a60e..a51d6b0c 100644 --- a/utoipa-gen/tests/schema_derive_test.rs +++ b/utoipa-gen/tests/schema_derive_test.rs @@ -5816,3 +5816,41 @@ fn derive_struct_inline_with_description() { }) ); } + +#[test] +fn schema_manual_impl() { + #![allow(unused)] + + struct Newtype(String); + + impl ToSchema for Newtype { + fn name() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed("Newtype") + } + } + + impl utoipa::PartialSchema for Newtype { + fn schema() -> utoipa::openapi::RefOr { + String::schema() + } + } + + let value = api_doc! { + struct Dto { + customer: Newtype + } + }; + + assert_json_eq!( + value, + json!({ + "properties": { + "customer": { + "$ref": "#/components/schemas/Newtype" + } + }, + "required": ["customer"], + "type": "object" + }) + ) +} diff --git a/utoipa/CHANGELOG.md b/utoipa/CHANGELOG.md index c8537559..7a41e5d2 100644 --- a/utoipa/CHANGELOG.md +++ b/utoipa/CHANGELOG.md @@ -40,6 +40,7 @@ to look into changes introduced to **`utoipa-gen`**. ### Changed +* Move `schemas` into `ToSchema` for schemas (https://github.com/juhaku/utoipa/pull/1112) * List only `utoipa` related changes in `utoipa` CHANGELOG * Remove commit commit id from changelogs (https://github.com/juhaku/utoipa/pull/1077) * Update to rc diff --git a/utoipa/src/lib.rs b/utoipa/src/lib.rs index 04d0befb..292b02f3 100644 --- a/utoipa/src/lib.rs +++ b/utoipa/src/lib.rs @@ -443,6 +443,58 @@ pub trait ToSchema: PartialSchema { .unwrap_or(type_name_without_generic); Cow::Borrowed(type_name) } + + /// Implement reference [`utoipa::openapi::schema::Schema`]s for this type. + /// + /// When [`ToSchema`] is being derived this is implemented automatically but if one needs to + /// manually implement [`ToSchema`] trait then this is needed for `utoipa` to know + /// referencing schemas that need to be present in the resulting OpenAPI spec. + /// + /// The implementation should push to `schemas` [`Vec`] all such field and variant types that + /// implement `ToSchema` and then call `::schemas(schemas)` on that type + /// to forward the recursive reference collection call on that type. + /// + /// # Examples + /// + /// _**Implement `ToSchema` manually with references.**_ + /// + /// ```rust + /// # use utoipa::{ToSchema, PartialSchema}; + /// # + /// #[derive(ToSchema)] + /// struct Owner { + /// name: String + /// } + /// + /// struct Pet { + /// owner: Owner, + /// name: String + /// } + /// impl PartialSchema for Pet { + /// fn schema() -> utoipa::openapi::RefOr { + /// utoipa::openapi::schema::Object::builder() + /// .property("owner", Owner::schema()) + /// .property("name", String::schema()) + /// .into() + /// } + /// } + /// impl ToSchema for Pet { + /// fn schemas(schemas: + /// &mut Vec<(String, utoipa::openapi::RefOr)>) { + /// schemas.push((Owner::name().into(), Owner::schema())); + /// ::schemas(schemas); + /// } + /// } + /// ``` + #[allow(unused)] + fn schemas( + schemas: &mut Vec<( + String, + utoipa::openapi::RefOr, + )>, + ) { + // nothing by default + } } impl From for openapi::RefOr { @@ -1332,6 +1384,7 @@ pub mod __dev { } } + // For types not implementing `ToSchema` pub trait SchemaReferences { fn schemas( schemas: &mut Vec<(