Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for real generics #1034

Merged
merged 3 commits into from
Sep 10, 2024
Merged

Add support for real generics #1034

merged 3 commits into from
Sep 10, 2024

Conversation

juhaku
Copy link
Owner

@juhaku juhaku commented Sep 8, 2024

This PR adds support for real generics which allows users to use deeply nested generic types as schemas without the alias hassle known to users. This commit will remove the old aliases support and implement completely new generics handling. (Something that many has been longing for long)

This commit further enhances the implementation for full generic support. From now on the full type paths need used to declare the types in the OpenApi and #[utoipa::path] macros.

As further elaborated here #1020 (comment) the name of the schema is now resolved form the type name and possible generic arguments or via as = ... attribute and possible generic arguments. This name is then used across the OpenApi. This makes it a single place to define the name or prefixed name for the type unlike
previously the path of the schema in OpenApi macro or request_body or response body was added to the name.

This PR will also change the schema!(...) macro functionality to correctly generate schemas for arbitrary types even generic ones. Prior to this commit the generics where not resolved correctly. Still all generic arguments must implement ToSchema trait in order to generate schema in first place.

Clean up the code and enhance documentation.

Breaking changes

  • Removed old aliases support
  • Schemas now need to be defined with full type path as shown below in example with all generic if any
  • If defined schema(as = ...) attribute now defines the prefix for the name of the component throughout OpenAPI references thus path that is possibly defined to request_body or responses body will not affect the name of the schema anymore.

Example of new syntax.

#[derive(ToSchema)]
#[schema(as = path::MyType<T>)]
struct Type<T: ToSchema> {
    t: T,
}

#[derive(ToSchema)]
struct Person<'p, T: Sized + ToSchema, P: ToSchema> {
    id: usize,
    name: Option<Cow<'p, str>>,
    field: T,
    t: P,
}

#[derive(ToSchema)]
#[schema(as = path::to::PageList)]
struct Page<T: ToSchema> {
    total: usize,
    page: usize,
    pages: usize,
    items: Vec<T>,
}

#[derive(ToSchema)]
#[schema(as = path::to::Element<T>)]
enum E<T: ToSchema> {
    One(T),
    Many(Vec<T>),
}

#[utoipa::path(
    get,
    path = "/handler",
    request_body = inline(Person<'_, String, Type<i32>>),
    responses(
        (status = OK, body = inline(Page<Person<'_, String, Type<i32>>>)),
        (status = 400, body = Page<Person<'_, String, Type<i32>>>)
    )
)]
async fn handler() {}

#[derive(OpenApi)]
#[openapi(
    components(
        schemas(
            Person::<'_, String, Type<i32>>,
            Page::<Person<'_, String, Type<i32>>>,
            E::<String>,
        )
    ),
    paths(
        handler
    )
)]
struct ApiDoc;

Fixes #703 Fixes #818 Fixes #574 Fixes #566 Fixes #861 Fixes #979 Fixes #503 Fixes #644 Fixes #790 Fixes #835 Fixes #817 Fixes #993 Fixes #961 Fixes #939 Fixes #729 Fixes #862

Closes #1020

Relates #751

This PR adds support for real generics which allows users to use deeply
nested generic types as schemas without the alias hassle known to users.
This commit will remove the old aliases support and implement completely
new generics handling. (Something that many has been longing for long)

This PR will also change the `schema!(...)` macro functionality to
correctly generate schemas for arbitrary types even generic ones. Prior
to this commit the generics where not resolved correctly.

Example of new syntax.
```rust
 #[derive(ToSchema)]
 struct Type<T> {
     t: T,
 }

 #[derive(ToSchema)]
 struct Person<'p, T: Sized, P> {
     id: usize,
     name: Cow<'p, str>,
     field: T,
     t: P,
 }

 #[derive(ToSchema)]
 struct Page<T> {
     total: usize,
     page: usize,
     pages: usize,
     items: Vec<T>,
 }

 #[derive(OpenApi)]
 #[openapi(
     components(
         schemas(
             Person::<'_, String, Type<i32>>,
             Page::<Person<'_, String, Type<i32>>>,
         )
     )
 )]
 struct ApiDoc;
```
This commit further enhances the implementation for full generic
support. From now on the full type paths need used to declare the types
in the `OpenApi` and `#[utoipa::path]` macros.

As further elaborated here #1020 (comment)
the name of the schema is now resolved form the type name and possible
generic arguments or via `as = ...` attribute and possible generic
arguments. This name is then used across the OpenApi. This makes it a
single place to define the name or prefixed name for the type unlike
previously the path of the schema in `OpenApi` macro or `request_body`
or `response body` was added to the name.

This is a breaking change.

Fixes #993 Closes #1020
Clean up the code and enhance documentation.

This commit removes the old `aliases` functionality completely.
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment