Skip to content

Commit 7d10516

Browse files
authored
Chore filter const generics (#1118)
This commit will filter out const generics from being added to the schema composing because the value is not used in anyway. Perhaps in some future release the real support of const generics could be experimented. Fixes #1115
1 parent 449b164 commit 7d10516

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

utoipa-gen/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
### Fixed
2828

29+
* Chore filter const generics (https://github.com/juhaku/utoipa/pull/1118)
2930
* Fix impl `ToSchema` for container types (https://github.com/juhaku/utoipa/pull/1107)
3031
* Fix description on `inline` field (https://github.com/juhaku/utoipa/pull/1102)
3132
* Fix `title` on unnamed struct and references (https://github.com/juhaku/utoipa/pull/1101)

utoipa-gen/src/component.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,10 @@ impl ComponentSchema {
11841184
let mut object_schema_reference = SchemaReference::default();
11851185

11861186
if let Some(children) = &type_tree.children {
1187-
let children_name = Self::compose_name(children)?;
1187+
let children_name = Self::compose_name(
1188+
Self::filter_const_generics(children, container.generics),
1189+
container.generics,
1190+
)?;
11881191
name_tokens.extend(quote! { std::borrow::Cow::Owned(format!("{}_{}", < #rewritten_path as utoipa::ToSchema >::name(), #children_name)) });
11891192
} else {
11901193
name_tokens.extend(
@@ -1205,7 +1208,8 @@ impl ComponentSchema {
12051208
schema_references.extend(Self::compose_child_references(children)?);
12061209

12071210
let composed_generics =
1208-
Self::compose_generics(children)?.collect::<Array<_>>();
1211+
Self::compose_generics(children, container.generics)?
1212+
.collect::<Array<_>>();
12091213
quote_spanned! {type_path.span()=>
12101214
<#rewritten_path as utoipa::__dev::ComposeSchema>::compose(#composed_generics.to_vec())
12111215
}
@@ -1242,8 +1246,11 @@ impl ComponentSchema {
12421246
// only set schema references tokens for concrete non generic types
12431247
if index.is_none() {
12441248
let reference_tokens = if let Some(children) = &type_tree.children {
1245-
let composed_generics =
1246-
Self::compose_generics(children)?.collect::<Array<_>>();
1249+
let composed_generics = Self::compose_generics(
1250+
Self::filter_const_generics(children, container.generics),
1251+
container.generics,
1252+
)?
1253+
.collect::<Array<_>>();
12471254
quote! { <#rewritten_path as utoipa::__dev::ComposeSchema>::compose(#composed_generics.to_vec()) }
12481255
} else {
12491256
quote! { <#rewritten_path as utoipa::PartialSchema>::schema() }
@@ -1356,7 +1363,10 @@ impl ComponentSchema {
13561363
Ok(())
13571364
}
13581365

1359-
fn compose_name<'tr, I>(children: I) -> Result<TokenStream, Diagnostics>
1366+
fn compose_name<'tr, I>(
1367+
children: I,
1368+
generics: &'tr Generics,
1369+
) -> Result<TokenStream, Diagnostics>
13601370
where
13611371
I: IntoIterator<Item = &'tr TypeTree<'tr>>,
13621372
{
@@ -1365,12 +1375,12 @@ impl ComponentSchema {
13651375
.map(|type_tree| {
13661376
let name = type_tree
13671377
.path
1368-
.as_ref()
1378+
.as_deref()
13691379
.expect("Generic ValueType::Object must have path");
1370-
let rewritten_name = name.as_ref().rewrite_path()?;
1380+
let rewritten_name = name.rewrite_path()?;
13711381

13721382
if let Some(children) = &type_tree.children {
1373-
let children_name = Self::compose_name(children)?;
1383+
let children_name = Self::compose_name(Self::filter_const_generics(children, generics), generics)?;
13741384

13751385
Ok(quote! { std::borrow::Cow::Owned(format!("{}_{}", <#rewritten_name as utoipa::ToSchema>::name(), #children_name)) })
13761386
} else {
@@ -1384,6 +1394,7 @@ impl ComponentSchema {
13841394

13851395
fn compose_generics<'v, I: IntoIterator<Item = &'v TypeTree<'v>>>(
13861396
children: I,
1397+
generics: &'v Generics,
13871398
) -> Result<impl Iterator<Item = TokenStream> + 'v, Diagnostics>
13881399
where
13891400
<I as std::iter::IntoIterator>::IntoIter: 'v,
@@ -1395,7 +1406,7 @@ impl ComponentSchema {
13951406
.expect("inline TypeTree ValueType::Object must have child path if generic");
13961407
let rewritten_path = path.rewrite_path()?;
13971408
if let Some(children) = &child.children {
1398-
let items = Self::compose_generics(children)?.collect::<Array<_>>();
1409+
let items = Self::compose_generics(Self::filter_const_generics(children, generics), generics)?.collect::<Array<_>>();
13991410
Ok(quote! { <#rewritten_path as utoipa::__dev::ComposeSchema>::compose(#items.to_vec()) })
14001411
} else {
14011412
Ok(quote! { <#rewritten_path as utoipa::PartialSchema>::schema() })
@@ -1406,6 +1417,31 @@ impl ComponentSchema {
14061417
Ok(iter)
14071418
}
14081419

1420+
fn filter_const_generics<'v, I: IntoIterator<Item = &'v TypeTree<'v>>>(
1421+
children: I,
1422+
generics: &'v Generics,
1423+
) -> impl IntoIterator<Item = &'v TypeTree<'v>> + 'v
1424+
where
1425+
<I as std::iter::IntoIterator>::IntoIter: 'v,
1426+
{
1427+
children.into_iter().filter(|type_tree| {
1428+
let path = type_tree
1429+
.path
1430+
.as_deref()
1431+
.expect("child TypeTree must have a Path, did you call this on array or tuple?");
1432+
let is_const = path
1433+
.get_ident()
1434+
.map(|path_ident| {
1435+
generics.params.iter().any(
1436+
|param| matches!(param, GenericParam::Const(ty) if ty.ident == *path_ident),
1437+
)
1438+
})
1439+
.unwrap_or(false);
1440+
1441+
!is_const
1442+
})
1443+
}
1444+
14091445
fn compose_child_references<'a, I: IntoIterator<Item = &'a TypeTree<'a>> + 'a>(
14101446
children: I,
14111447
) -> Result<impl Iterator<Item = SchemaReference> + 'a, Diagnostics> {
@@ -1416,8 +1452,8 @@ impl ComponentSchema {
14161452
} else if type_tree.value_type == ValueType::Object {
14171453
let type_path = type_tree
14181454
.path
1419-
.as_ref()
1420-
.expect("Object TypePath must have type path, compose child references").as_ref();
1455+
.as_deref()
1456+
.expect("Object TypePath must have type path, compose child references");
14211457

14221458
let rewritten_path = type_path.rewrite_path()?;
14231459

utoipa-gen/tests/schema_derive_test.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5854,3 +5854,35 @@ fn schema_manual_impl() {
58545854
})
58555855
)
58565856
}
5857+
5858+
#[test]
5859+
fn const_generic_test() {
5860+
#![allow(unused)]
5861+
5862+
#[derive(ToSchema)]
5863+
pub struct ArrayResponse<T: ToSchema, const N: usize> {
5864+
array: [T; N],
5865+
}
5866+
5867+
#[derive(ToSchema)]
5868+
struct CombinedResponse<T: ToSchema, const N: usize> {
5869+
pub array_response: ArrayResponse<T, N>,
5870+
}
5871+
5872+
use utoipa::PartialSchema;
5873+
let schema = <CombinedResponse<String, 1> as PartialSchema>::schema();
5874+
let value = serde_json::to_value(schema).expect("schema is JSON serializable");
5875+
5876+
assert_json_eq! {
5877+
value,
5878+
json!({
5879+
"properties": {
5880+
"array_response": {
5881+
"$ref": "#/components/schemas/ArrayResponse_String"
5882+
}
5883+
},
5884+
"required": ["array_response"],
5885+
"type": "object"
5886+
})
5887+
}
5888+
}

0 commit comments

Comments
 (0)