Skip to content

Commit

Permalink
Add support for real generics (#1034)
Browse files Browse the repository at this point in the history
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.
```rust
#[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;
```
  • Loading branch information
juhaku authored Sep 10, 2024
1 parent a985d8c commit f13d3d3
Show file tree
Hide file tree
Showing 27 changed files with 1,076 additions and 836 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
Loading

0 comments on commit f13d3d3

Please sign in to comment.