Skip to content

Commit ef025d4

Browse files
committed
Wip proof of concept auto collect schemas
Implement proof of concept auto collect nested schema references and register them in call usage call. Currently implemented for named struct field schemas.
1 parent e0c5aa7 commit ef025d4

File tree

8 files changed

+225
-59
lines changed

8 files changed

+225
-59
lines changed

utoipa-gen/src/component.rs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -572,10 +572,28 @@ impl ToTokens for ComponentDescription<'_> {
572572
}
573573
}
574574

575+
/// Used to store possible inner field schema name and tokens if field contains any schema
576+
/// references. E.g. field: Vec<Foo> should have name: Foo::name() and tokens: Foo::schema()
577+
#[cfg_attr(feature = "debug", derive(Debug))]
578+
#[derive(Default)]
579+
pub struct SchemaReference {
580+
name: TokenStream,
581+
tokens: TokenStream,
582+
}
583+
584+
impl SchemaReference {
585+
/// Check whether `name` or `tokens` is empty. Will happen if token is partially resolved as a
586+
/// reference schema. E.g. inlined schemas does not generated tokens.
587+
fn is_empty(&self) -> bool {
588+
self.name.is_empty() || self.tokens.is_empty()
589+
}
590+
}
591+
575592
#[cfg_attr(feature = "debug", derive(Debug))]
576593
pub struct ComponentSchema {
577594
tokens: TokenStream,
578595
pub name_tokens: TokenStream,
596+
pub schema_reference: Option<SchemaReference>,
579597
}
580598

581599
impl ComponentSchema {
@@ -589,6 +607,7 @@ impl ComponentSchema {
589607
) -> Result<Self, Diagnostics> {
590608
let mut tokens = TokenStream::new();
591609
let mut name_tokens = TokenStream::new();
610+
let mut schema_reference = SchemaReference::default();
592611

593612
match type_tree.generic_type {
594613
Some(GenericType::Map) => ComponentSchema::map_to_tokens(
@@ -601,6 +620,7 @@ impl ComponentSchema {
601620
Some(GenericType::Vec | GenericType::LinkedList | GenericType::Set) => {
602621
ComponentSchema::vec_to_tokens(
603622
&mut tokens,
623+
&mut schema_reference,
604624
container,
605625
features,
606626
type_tree,
@@ -610,6 +630,7 @@ impl ComponentSchema {
610630
#[cfg(feature = "smallvec")]
611631
Some(GenericType::SmallVec) => ComponentSchema::vec_to_tokens(
612632
&mut tokens,
633+
&mut schema_reference,
613634
container,
614635
features,
615636
type_tree,
@@ -623,8 +644,7 @@ impl ComponentSchema {
623644
{
624645
features.push(Nullable::new().into());
625646
}
626-
627-
ComponentSchema::new(ComponentSchemaProps {
647+
let schema = ComponentSchema::new(ComponentSchemaProps {
628648
container,
629649
type_tree: type_tree
630650
.children
@@ -635,8 +655,13 @@ impl ComponentSchema {
635655
.expect("ComponentSchema generic container type should have 1 child"),
636656
features,
637657
description,
638-
})?
639-
.to_tokens(&mut tokens)?;
658+
})?;
659+
schema.to_tokens(&mut tokens)?;
660+
661+
if let Some(reference) = schema.schema_reference {
662+
schema_reference.name = reference.name;
663+
schema_reference.tokens = reference.tokens;
664+
}
640665
}
641666
Some(GenericType::Cow | GenericType::Box | GenericType::RefCell) => {
642667
ComponentSchema::new(ComponentSchemaProps {
@@ -672,6 +697,7 @@ impl ComponentSchema {
672697
None => ComponentSchema::non_generic_to_tokens(
673698
&mut tokens,
674699
&mut name_tokens,
700+
&mut schema_reference,
675701
container,
676702
features,
677703
type_tree,
@@ -682,6 +708,11 @@ impl ComponentSchema {
682708
Ok(Self {
683709
tokens,
684710
name_tokens,
711+
schema_reference: if schema_reference.is_empty() {
712+
None
713+
} else {
714+
Some(schema_reference)
715+
},
685716
})
686717
}
687718

@@ -764,6 +795,7 @@ impl ComponentSchema {
764795

765796
fn vec_to_tokens(
766797
tokens: &mut TokenStream,
798+
schema_reference: &mut SchemaReference,
767799
container: &Container,
768800
mut features: Vec<Feature>,
769801
type_tree: &TypeTree,
@@ -799,8 +831,6 @@ impl ComponentSchema {
799831
child
800832
};
801833

802-
let unique = matches!(type_tree.generic_type, Some(GenericType::Set));
803-
804834
let component_schema = ComponentSchema::new(ComponentSchemaProps {
805835
container,
806836
type_tree: child,
@@ -809,7 +839,12 @@ impl ComponentSchema {
809839
})?;
810840
let component_schema_tokens = as_tokens_or_diagnostics!(&component_schema);
811841

812-
let unique = match unique {
842+
if let Some(reference) = component_schema.schema_reference {
843+
schema_reference.name = reference.name;
844+
schema_reference.tokens = reference.tokens;
845+
};
846+
847+
let unique = match matches!(type_tree.generic_type, Some(GenericType::Set)) {
813848
true => quote! {
814849
.unique_items(true)
815850
},
@@ -865,6 +900,7 @@ impl ComponentSchema {
865900
fn non_generic_to_tokens(
866901
tokens: &mut TokenStream,
867902
name_tokens: &mut TokenStream,
903+
schema_reference: &mut SchemaReference,
868904
container: &Container,
869905
mut features: Vec<Feature>,
870906
type_tree: &TypeTree,
@@ -987,6 +1023,8 @@ impl ComponentSchema {
9871023
}
9881024
name_tokens.extend(quote! { format!("{}{}", < #type_path as utoipa::ToSchema >::name(), #component_name_buffer) });
9891025

1026+
schema_reference.name = name_tokens.clone();
1027+
9901028
if is_inline {
9911029
let default = pop_feature!(features => Feature::Default(_));
9921030
let default_tokens = as_tokens_or_diagnostics!(&default);
@@ -1037,6 +1075,8 @@ impl ComponentSchema {
10371075

10381076
schema.to_tokens(tokens);
10391077
} else {
1078+
schema_reference.tokens =
1079+
quote! {<#type_path as utoipa::PartialSchema>::schema() };
10401080
let default = pop_feature!(features => Feature::Default(_));
10411081
let default_tokens = as_tokens_or_diagnostics!(&default);
10421082

0 commit comments

Comments
 (0)