juhaku / utoipa

Simple, Fast, Code first and Compile time generated OpenAPI documentation for Rust
Apache License 2.0
2.48k stars 194 forks source link

Add support for real generics #1034

Closed juhaku closed 2 months ago

juhaku commented 2 months ago

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 https://github.com/juhaku/utoipa/pull/1020#issue-2503474650 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

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