Skip to content

Commit

Permalink
Full generics support
Browse files Browse the repository at this point in the history
Clean up the code and enhance documentation.

This commit removes the old `aliases` functionality completely.
  • Loading branch information
juhaku committed Sep 10, 2024
1 parent 524e298 commit 815d387
Show file tree
Hide file tree
Showing 21 changed files with 125 additions and 367 deletions.
7 changes: 3 additions & 4 deletions examples/generics-actix/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ fn get_coord_schema<T: PartialSchema>() -> Object {
}

#[derive(Serialize, ToSchema)]
#[aliases(MyFloatObject = MyObject<f64>, MyIntegerObject = MyObject<u64>)]
pub struct MyObject<T: geo_types::CoordNum + PartialSchema> {
#[schema(schema_with=get_coord_schema::<T>)]
at: geo_types::Coord<T>,
Expand All @@ -45,7 +44,7 @@ pub struct FloatParams {
FloatParams
),
responses(
(status = 200, description = "OK", body = MyFloatObject),
(status = 200, description = "OK", body = MyObject<f64>),
),
security(
("api_key" = [])
Expand Down Expand Up @@ -75,7 +74,7 @@ pub struct IntegerParams {
IntegerParams,
),
responses(
(status = 200, description = "OK", body = MyIntegerObject),
(status = 200, description = "OK", body = MyObject<u64>),
),
security(
("api_key" = [])
Expand All @@ -99,7 +98,7 @@ async fn main() -> Result<(), impl Error> {
#[derive(OpenApi)]
#[openapi(
paths(coord_f64, coord_u64),
components(schemas(MyFloatObject, MyIntegerObject))
components(schemas(MyObject<f64>, MyObject<u64>))
)]
struct ApiDoc;

Expand Down
94 changes: 12 additions & 82 deletions utoipa-gen/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ use crate::{
};
use crate::{schema_type::SchemaType, Deprecated};

use self::features::attributes::{As, Description, Nullable};
use self::features::attributes::{Description, Nullable};
use self::features::validation::Minimum;
use self::features::{
pop_feature, Feature, FeaturesExt, IntoInner, IsInline, ToTokensExt, Validatable,
};
use self::schema::format_path_ref;
use self::serde::{RenameRule, SerdeContainer, SerdeValue};

pub mod into_params;
Expand Down Expand Up @@ -334,24 +333,6 @@ impl<'t> TypeTree<'t> {
is
}

fn find_mut(&mut self, type_tree: &TypeTree) -> Option<&mut Self> {
let is = self
.path
.as_mut()
.map(|p| matches!(&type_tree.path, Some(path) if path.as_ref() == p.as_ref()))
.unwrap_or(false);

if is {
Some(self)
} else {
self.children.as_mut().and_then(|children| {
children
.iter_mut()
.find_map(|child| Self::find_mut(child, type_tree))
})
}
}

/// `Object` virtual type is used when generic object is required in OpenAPI spec. Typically used
/// with `value_type` attribute to hinder the actual type.
pub fn is_object(&self) -> bool {
Expand All @@ -374,31 +355,8 @@ impl<'t> TypeTree<'t> {
matches!(self.generic_type, Some(GenericType::Map))
}

pub fn match_ident(&self, ident: &Ident) -> bool {
let Some(ref path) = self.path else {
return false;
};

let matches = path
.segments
.iter()
.last()
.map(|segment| &segment.ident == ident)
.unwrap_or_default();

matches
|| self
.children
.iter()
.flatten()
.any(|child| child.match_ident(ident))
}

/// Get path type `Ident` and `Generics` of the `TypeTree` path value.
pub fn get_path_type_and_generics(
&self,
generic_arguments: GenericArguments,
) -> syn::Result<(&Ident, Generics)> {
/// Get [`syn::Generics`] for current [`TypeTree`]'s [`syn::Path`].
pub fn get_path_generics(&self) -> syn::Result<Generics> {
let mut generics = Generics::default();
let segment = self
.path
Expand All @@ -408,10 +366,7 @@ impl<'t> TypeTree<'t> {
.last()
.expect("Path must have segments");

fn type_to_generic_params(
ty: &Type,
generic_arguments: &GenericArguments,
) -> Vec<GenericParam> {
fn type_to_generic_params(ty: &Type) -> Vec<GenericParam> {
match &ty {
Type::Path(path) => {
let mut params_vec: Vec<GenericParam> = Vec::new();
Expand All @@ -423,38 +378,22 @@ impl<'t> TypeTree<'t> {
let ident = &last_segment.ident;
params_vec.push(syn::parse_quote!(#ident));

if matches!(generic_arguments, GenericArguments::All) {
// we are only interested of angle bracket arguments
if let PathArguments::AngleBracketed(ref args) = last_segment.arguments {
params_vec.extend(angle_bracket_args_to_params(args, generic_arguments))
}
}
params_vec
}
Type::Reference(reference) => {
type_to_generic_params(reference.elem.as_ref(), generic_arguments)
}
Type::Reference(reference) => type_to_generic_params(reference.elem.as_ref()),
_ => Vec::new(),
}
}

fn angle_bracket_args_to_params<'a>(
args: &'a AngleBracketedGenericArguments,
generic_arguments: &'a GenericArguments,
) -> impl Iterator<Item = GenericParam> + 'a {
fn angle_bracket_args_to_params(
args: &AngleBracketedGenericArguments,
) -> impl Iterator<Item = GenericParam> + '_ {
args.args
.iter()
.filter_map(move |generic_argument| {
match generic_argument {
GenericArgument::Type(ty) => {
Some(type_to_generic_params(ty, generic_arguments))
}
GenericArgument::Lifetime(life)
if matches!(
generic_arguments,
GenericArguments::CurrentTypeOnly | GenericArguments::All
) =>
{
GenericArgument::Type(ty) => Some(type_to_generic_params(ty)),
GenericArgument::Lifetime(life) => {
Some(vec![GenericParam::Lifetime(syn::parse_quote!(#life))])
}
_ => None, // other wise ignore
Expand All @@ -465,22 +404,14 @@ impl<'t> TypeTree<'t> {

if let PathArguments::AngleBracketed(angle_bracketed_args) = &segment.arguments {
generics.lt_token = Some(angle_bracketed_args.lt_token);
generics.params =
angle_bracket_args_to_params(angle_bracketed_args, &generic_arguments).collect();
generics.params = angle_bracket_args_to_params(angle_bracketed_args).collect();
generics.gt_token = Some(angle_bracketed_args.gt_token);
};

Ok((&segment.ident, generics))
Ok(generics)
}
}

#[allow(unused)]
pub enum GenericArguments {
All,
CurrentTypeOnly,
CurrentOnlyNoLifetimes,
}

impl PartialEq for TypeTree<'_> {
#[cfg(feature = "debug")]
fn eq(&self, other: &Self) -> bool {
Expand Down Expand Up @@ -585,7 +516,6 @@ impl Rename for FieldRename {

#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Container<'c> {
pub ident: &'c Ident,
pub generics: &'c Generics,
}

Expand Down
5 changes: 2 additions & 3 deletions utoipa-gen/src/component/into_params.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;

use proc_macro2::{Span, TokenStream};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
parse::Parse, punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, Field,
Expand All @@ -25,7 +25,7 @@ use crate::{
FieldRename,
},
doc_comment::CommentAttributes,
Array, Diagnostics, GenericsExt, OptionExt, Required, ToTokensDiagnostics,
Array, Diagnostics, OptionExt, Required, ToTokensDiagnostics,
};

use super::{
Expand Down Expand Up @@ -461,7 +461,6 @@ impl ToTokensDiagnostics for Param<'_> {
description: None,
deprecated: None,
container: &Container {
ident: &Ident::new("empty_param", Span::call_site()),
generics: self.generics,
},
})?;
Expand Down
Loading

0 comments on commit 815d387

Please sign in to comment.