diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs index 6a0b9b2c..4de5d4f0 100644 --- a/utoipa-gen/src/component.rs +++ b/utoipa-gen/src/component.rs @@ -1255,8 +1255,18 @@ impl ComponentSchema { schema.to_tokens(tokens); } else { - let index = container.generics.get_generic_type_param_index(type_tree); - // only set schema references tokens for concrete non generic types + let schema_type = SchemaType { + path: Cow::Borrowed(&rewritten_path), + nullable, + }; + let index = + if schema_type.is_primitive() || type_tree.generic_type.is_none() { + container.generics.get_generic_type_param_index(type_tree) + } else { + None + }; + + // forcibly inline primitive type parameters, otherwise use references if index.is_none() { let reference_tokens = if let Some(children) = &type_tree.children { let composed_generics = Self::compose_generics( diff --git a/utoipa-gen/tests/openapi_derive.rs b/utoipa-gen/tests/openapi_derive.rs index e34dc570..8702b071 100644 --- a/utoipa-gen/tests/openapi_derive.rs +++ b/utoipa-gen/tests/openapi_derive.rs @@ -553,6 +553,64 @@ fn derive_nest_openapi_with_tags() { ) } +#[test] +fn openapi_schemas_resolve_generic_enum_schema() { + #![allow(dead_code)] + use utoipa::ToSchema; + + #[derive(ToSchema)] + enum Element { + One(T), + Many(Vec), + } + + #[derive(OpenApi)] + #[openapi(components(schemas(Element)))] + struct ApiDoc; + + let doc = ApiDoc::openapi(); + + let value = serde_json::to_value(&doc).expect("OpenAPI is JSON serializable"); + let schemas = value.pointer("/components/schemas").unwrap(); + let json = serde_json::to_string_pretty(&schemas).expect("OpenAPI is json serializable"); + println!("{json}"); + + assert_json_eq!( + schemas, + json!({ + "Element_String": { + "oneOf": [ + { + "properties": { + "One": { + "type": "string" + } + }, + "required": [ + "One" + ], + "type": "object" + }, + { + "properties": { + "Many": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "Many" + ], + "type": "object" + } + ] + } + }) + ) +} + #[test] fn openapi_schemas_resolve_schema_references() { #![allow(dead_code)] diff --git a/utoipa-gen/tests/path_derive.rs b/utoipa-gen/tests/path_derive.rs index ea5f9b22..6dca8c82 100644 --- a/utoipa-gen/tests/path_derive.rs +++ b/utoipa-gen/tests/path_derive.rs @@ -2655,6 +2655,7 @@ fn derive_path_test_collect_generic_array_request_body() { #[derive(ToSchema)] struct CreateRequest { + #[schema(inline)] value: T, } @@ -2744,6 +2745,7 @@ fn derive_path_test_collect_generic_request_body() { #[derive(ToSchema)] struct CreateRequest { + #[schema(inline)] value: T, } diff --git a/utoipa-gen/tests/schema_generics.rs b/utoipa-gen/tests/schema_generics.rs index cd49a2bd..9d838255 100644 --- a/utoipa-gen/tests/schema_generics.rs +++ b/utoipa-gen/tests/schema_generics.rs @@ -30,6 +30,7 @@ fn generic_schema_full_api() { #[derive(ToSchema)] #[schema(as = path::MyType)] struct Type { + #[schema(inline)] t: T, } @@ -37,7 +38,9 @@ fn generic_schema_full_api() { struct Person<'p, T: Sized, P> { id: usize, name: Option>, + #[schema(inline)] field: T, + #[schema(inline)] t: P, } @@ -47,14 +50,15 @@ fn generic_schema_full_api() { total: usize, page: usize, pages: usize, + #[schema(inline)] items: Vec, } #[derive(ToSchema)] #[schema(as = path::to::Element)] enum E { - One(T), - Many(Vec), + One(#[schema(inline)] T), + Many(#[schema(inline)] Vec), } struct NoToSchema; @@ -107,6 +111,7 @@ fn schema_with_non_generic_root() { #[derive(ToSchema)] struct Bar { + #[schema(inline)] value: T, } @@ -172,6 +177,7 @@ fn derive_generic_schema_collect_recursive_schema_not_inlined() { #[derive(ToSchema)] pub struct FooStruct { + #[schema(inline)] pub foo: B, } @@ -182,6 +188,7 @@ fn derive_generic_schema_collect_recursive_schema_not_inlined() { pub struct Person { name: String, account: Account, + #[schema(inline)] t: T, } @@ -192,6 +199,7 @@ fn derive_generic_schema_collect_recursive_schema_not_inlined() { #[derive(ToSchema, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Ty { + #[schema(inline)] t: T, } @@ -231,6 +239,7 @@ fn high_order_types() { #[derive(ToSchema)] pub struct High { + #[schema(inline)] high: T, } diff --git a/utoipa-gen/tests/testdata/openapi_schemas_resolve_inner_schema_references b/utoipa-gen/tests/testdata/openapi_schemas_resolve_inner_schema_references index 7368bc5b..9e0cd742 100644 --- a/utoipa-gen/tests/testdata/openapi_schemas_resolve_inner_schema_references +++ b/utoipa-gen/tests/testdata/openapi_schemas_resolve_inner_schema_references @@ -40,7 +40,7 @@ "properties": { "Many": { "items": { - "type": "object" + "type": "string" }, "type": "array" } @@ -95,6 +95,32 @@ "properties": { "Many": { "items": { + "properties": { + "accounts": { + "items": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/Account" + } + ] + }, + "type": "array" + }, + "foo_bar": { + "$ref": "#/components/schemas/Foobar" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "foo_bar", + "accounts" + ], "type": "object" }, "type": "array" diff --git a/utoipa-gen/tests/testdata/schema_generic_collect_non_inlined_schema b/utoipa-gen/tests/testdata/schema_generic_collect_non_inlined_schema index ea5021ab..1abb500f 100644 --- a/utoipa-gen/tests/testdata/schema_generic_collect_non_inlined_schema +++ b/utoipa-gen/tests/testdata/schema_generic_collect_non_inlined_schema @@ -140,7 +140,7 @@ }, "t": { "type": "integer", - "format": "int32" + "format": "int64" } } }, diff --git a/utoipa-gen/tests/testdata/schema_generics_openapi b/utoipa-gen/tests/testdata/schema_generics_openapi index 95c429ca..f69eda2f 100644 --- a/utoipa-gen/tests/testdata/schema_generics_openapi +++ b/utoipa-gen/tests/testdata/schema_generics_openapi @@ -193,7 +193,7 @@ "Many": { "type": "array", "items": { - "type": "object" + "type": "string" } } }