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

[REQ] [Rust-Axum] Solution to split implementation across files #18442

Closed
kalvinpearce opened this issue Apr 20, 2024 · 7 comments · Fixed by #18621
Closed

[REQ] [Rust-Axum] Solution to split implementation across files #18442

kalvinpearce opened this issue Apr 20, 2024 · 7 comments · Fixed by #18621

Comments

@kalvinpearce
Copy link
Contributor

Is your feature request related to a problem? Please describe.

The way the generator currently works means you are forced to implement all functions for the api in a single block. While this works for small simple projects, it becomes quite unwieldy for large apis.

Describe the solution you'd like

I wonder if potentially the Api trait that is generated could instead be split up, perhaps for each endpoint. Each trait could contain just a single function. This would allow you to impl each endpoint seperately and thus allow them to be organised into seperate files. For example if you had an api that contained foo, bar and baz, rather than generating a single Api trait with all functions, it could instead generate ApiFoo, ApiBar and ApiBaz. server::new() could be trait bound to all of the generated traits rather than just the single Api trait which would still ensure the final server implements all the required functionality.

Describe alternatives you've considered

Another option would be to potentially split the traits up by tags.

Additional context

I have not attempted trait bounds at such a large scale so I am not sure if there are any limitations or overheads at such a scale. I feel like perhaps this would be best as a config option rather than the default.

@kalvinpearce
Copy link
Contributor Author

I have done some initial testing here (fork) on this to see how feasible this approach is. I have used a config option splitTraits which needs to be set to true to test this approach.

So far it seems to be working quite well in my testing. I have tried it on some of my fairly large specs and although rust-analyzer seems to take longer to process, it continues to work well once it has parsed the crate.

I would love for some more eyes on this if anyone has the time, as I am conscious that this may not be the best solution to the problem.

@wing328
Copy link
Member

wing328 commented May 8, 2024

I wonder if potentially the Api trait that is generated could instead be split up, perhaps for each endpoint. Each trait could contain just a single function.

Is this idea/feature request similar to what the rule `SET_TAGS_TO_OPERATIONID in openapi normalizer tries to achieve?

ref: https://github.com/openapitools/openapi-generator/blob/master/docs/customization.md#openapi-normalizer

@kalvinpearce
Copy link
Contributor Author

Thanks, I was unaware of this option. I did some experimenting with that option and it seems that due to the way the rust-axum genreator is set up, that option will have no effect. This is because it will always create a single Api trait that holds all operation, regardless of tags (see here).

Maybe the more ideal solution to this would be for the rust-axum generator to have a unique trait for each tag, similar to what other generators appear to be doing. I will have a crack at this tomorrow.

@wing328
Copy link
Member

wing328 commented May 8, 2024

Ah ok. I agree with you.

To put all operations in a single API file, one can use the openapi normalizer rule SET_TAGS_FOR_ALL_OPERATIONS.

cc @linxGnu

@linxGnu
Copy link
Contributor

linxGnu commented May 9, 2024

I am a fan of SOLID Principle, especially interface segregation.

Breaking trait into smaller one is a great idea to me. However, breaking by endpoint is not. Some people don't want a huge amount of traits in their source code. We should think about this carefully.

There are two ways to think about:

  1. OpenAPI 3.0 Specs: grouping operations with tags
  2. Cutom-extensions, similar to deepmap/oapi-codegen for Golang. In this way, you define a custom field to group endpoint into single trait:
paths:
  /users/{id}:
    summary: Represents a user
    x-group: {TRAIT}

This will be complex and need support from open-api engine underlying. However, possible to do so.

Each solution has pros and cons. Using tags might aerror prone. For example, one endpoint has 2 tags:

tags:
     - pets
     - animals

HDYT @wing328 @kalvinpearce

@kalvinpearce
Copy link
Contributor Author

I have just opened #18621 which aims to solve this with tags. However, you raise a good point about handling when a single endpoint has multiple tags. How do other generators handle this situation?

@linxGnu
Copy link
Contributor

linxGnu commented May 10, 2024

@kalvinpearce
I don't know how other generators doing, but we could think in Rust.

How about sort & group tags into a single tag (a.k.a, a composite Trait). For example:

tags:
  - TraitX

tags:
  - TraitY

tags:
  - TraitX
  - TraitY
impl<A> TraitX for Server<A>
where A: TransactionTrait
{}

impl<A> TraitY for Server<A>
where A: ConnectionTrait
{}

impl<A> TraitZ for Server<A> // here, TraitZ is composite of X and Y
where A: TransactionTrait + ConnectionTrait // specify different contract for A
{}

In this way, we leverage Rust best practice, keep trait small and focus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants