Skip to content

Commit e9f961e

Browse files
authored
[rust-axum] Split up api trait per tag (#18621)
* [rust-axum] Feat: split up api trait per tag * [rust-axum] Fix: missing types in api files * [rust-axum] Fix: add samples output * [rust-axum] Feat: handle mutli tagged operations * [rust-axum] Fix: spacing between generated operations * [rust-axum] Fix: coding standards
1 parent 57dceae commit e9f961e

62 files changed

Lines changed: 3285 additions & 3131 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public RustAxumServerCodegen() {
134134

135135
importMapping = new HashMap<>();
136136
modelTemplateFiles.clear();
137-
apiTemplateFiles.clear();
137+
apiTemplateFiles.put("apis.mustache", ".rs");
138138

139139
// types
140140
defaultIncludes = new HashSet<>(
@@ -240,6 +240,7 @@ public RustAxumServerCodegen() {
240240
supportingFiles.add(new SupportingFile("types.mustache", "src", "types.rs"));
241241
supportingFiles.add(new SupportingFile("header.mustache", "src", "header.rs"));
242242
supportingFiles.add(new SupportingFile("server-mod.mustache", "src/server", "mod.rs"));
243+
supportingFiles.add(new SupportingFile("apis-mod.mustache", apiPackage().replace('.', File.separatorChar), "mod.rs"));
243244
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
244245
.doNotOverwrite());
245246
}
@@ -320,7 +321,7 @@ private void setPackageVersion(String packageVersion) {
320321

321322
@Override
322323
public String apiPackage() {
323-
return apiPath;
324+
return "src" + File.separator + "apis";
324325
}
325326

326327
@Override
@@ -349,6 +350,11 @@ public String toApiName(String name) {
349350
sanitizeIdentifier(name, CasingType.SNAKE_CASE, "api", "API", true);
350351
}
351352

353+
@Override
354+
public String toApiFilename(String name) {
355+
return toApiName(name);
356+
}
357+
352358
/**
353359
* Location to write api files. You can use the apiPackage() as defined when the class is
354360
* instantiated
@@ -565,6 +571,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
565571
@Override
566572
public OperationsMap postProcessOperationsWithModels(OperationsMap operationsMap, List<ModelMap> allModels) {
567573
OperationMap operations = operationsMap.getOperations();
574+
operations.put("classnamePascalCase", camelize(operations.getClassname()));
568575
List<CodegenOperation> operationList = operations.getOperation();
569576

570577
for (CodegenOperation op : operationList) {
@@ -649,13 +656,23 @@ public boolean isDataTypeFile(final String dataType) {
649656
@Override
650657
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation
651658
op, Map<String, List<CodegenOperation>> operations) {
652-
// only generate operation for the first tag of the tags
659+
// If more than one tag, combine into a single unique group
653660
if (tag != null && op.tags.size() > 1) {
661+
// Skip any tags other than the first one. This is because we
662+
// override the tag with a combined version of all the tags.
654663
String expectedTag = sanitizeTag(op.tags.get(0).getName());
655664
if (!tag.equals(expectedTag)) {
656665
LOGGER.info("generated skip additional tag `{}` with operationId={}", tag, op.operationId);
657666
return;
658667
}
668+
669+
// Get all tags sorted by name
670+
final List<String> tags = op.tags.stream().map(t -> t.getName()).sorted().collect(Collectors.toList());
671+
// Combine into a single group
672+
final String combinedTag = tags.stream().collect(Collectors.joining("-"));
673+
// Add to group
674+
super.addOperationToGroup(combinedTag, resourcePath, operation, op, operations);
675+
return;
659676
}
660677
super.addOperationToGroup(tag, resourcePath, operation, op, operations);
661678
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{{#apiInfo}}
2+
{{#apis}}
3+
pub mod {{classFilename}};
4+
{{/apis}}
5+
{{/apiInfo}}
6+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use async_trait::async_trait;
2+
use axum::extract::*;
3+
use axum_extra::extract::{CookieJar, Multipart};
4+
use bytes::Bytes;
5+
use http::Method;
6+
use serde::{Deserialize, Serialize};
7+
8+
use crate::{models, types::*};
9+
10+
{{#operations}}
11+
{{#operation}}
12+
{{>response}}
13+
{{/operation}}
14+
{{/operations}}
15+
16+
{{#operations}}
17+
/// {{classnamePascalCase}}
18+
#[async_trait]
19+
#[allow(clippy::ptr_arg)]
20+
pub trait {{classnamePascalCase}} {
21+
{{#operation}}
22+
{{#summary}}
23+
/// {{{.}}}.
24+
///
25+
{{/summary}}
26+
{{#vendorExtensions}}
27+
/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}}
28+
async fn {{{x-operation-id}}}(
29+
&self,
30+
method: Method,
31+
host: Host,
32+
cookies: CookieJar,
33+
{{#headerParams.size}}
34+
header_params: models::{{{operationIdCamelCase}}}HeaderParams,
35+
{{/headerParams.size}}
36+
{{#pathParams.size}}
37+
path_params: models::{{{operationIdCamelCase}}}PathParams,
38+
{{/pathParams.size}}
39+
{{#queryParams.size}}
40+
query_params: models::{{{operationIdCamelCase}}}QueryParams,
41+
{{/queryParams.size}}
42+
{{^x-consumes-multipart-related}}
43+
{{^x-consumes-multipart}}
44+
{{#bodyParam}}
45+
{{#vendorExtensions}}
46+
{{^x-consumes-plain-text}}
47+
body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
48+
{{/x-consumes-plain-text}}
49+
{{#x-consumes-plain-text}}
50+
{{#isString}}
51+
body: String,
52+
{{/isString}}
53+
{{^isString}}
54+
body: Bytes,
55+
{{/isString}}
56+
{{/x-consumes-plain-text}}
57+
{{/vendorExtensions}}
58+
{{/bodyParam}}
59+
{{/x-consumes-multipart}}
60+
{{/x-consumes-multipart-related}}
61+
{{#x-consumes-multipart}}
62+
body: Multipart,
63+
{{/x-consumes-multipart}}
64+
{{#x-consumes-multipart-related}}
65+
body: axum::body::Body,
66+
{{/x-consumes-multipart-related}}
67+
) -> Result<{{{operationId}}}Response, String>;
68+
{{/vendorExtensions}}
69+
{{^-last}}
70+
71+
{{/-last}}
72+
{{/operation}}
73+
}
74+
{{/operations}}

modules/openapi-generator/src/main/resources/rust-axum/lib.mustache

Lines changed: 1 addition & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -14,98 +14,17 @@
1414
clippy::too_many_arguments
1515
)]
1616

17-
use async_trait::async_trait;
18-
use axum::extract::*;
19-
use axum_extra::extract::{CookieJar, Multipart};
20-
use bytes::Bytes;
21-
use http::Method;
22-
use serde::{Deserialize, Serialize};
23-
24-
use types::*;
25-
2617
pub const BASE_PATH: &str = "{{{basePathWithoutHost}}}";
2718
{{#appVersion}}
2819
pub const API_VERSION: &str = "{{{.}}}";
2920
{{/appVersion}}
3021

31-
{{#apiInfo}}
32-
{{#apis}}
33-
{{#operations}}
34-
{{#operation}}
35-
{{>response}}
36-
{{/operation}}
37-
{{/operations}}
38-
{{/apis}}
39-
{{/apiInfo}}
40-
41-
/// API
42-
#[async_trait]
43-
#[allow(clippy::ptr_arg)]
44-
pub trait Api {
45-
{{#apiInfo}}
46-
{{#apis}}
47-
{{#operations}}
48-
{{#operation}}
49-
50-
{{#summary}}
51-
/// {{{.}}}.
52-
///
53-
{{/summary}}
54-
{{#vendorExtensions}}
55-
/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}}
56-
async fn {{{x-operation-id}}}(
57-
&self,
58-
method: Method,
59-
host: Host,
60-
cookies: CookieJar,
61-
{{#headerParams.size}}
62-
header_params: models::{{{operationIdCamelCase}}}HeaderParams,
63-
{{/headerParams.size}}
64-
{{#pathParams.size}}
65-
path_params: models::{{{operationIdCamelCase}}}PathParams,
66-
{{/pathParams.size}}
67-
{{#queryParams.size}}
68-
query_params: models::{{{operationIdCamelCase}}}QueryParams,
69-
{{/queryParams.size}}
70-
{{^x-consumes-multipart-related}}
71-
{{^x-consumes-multipart}}
72-
{{#bodyParam}}
73-
{{#vendorExtensions}}
74-
{{^x-consumes-plain-text}}
75-
body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
76-
{{/x-consumes-plain-text}}
77-
{{#x-consumes-plain-text}}
78-
{{#isString}}
79-
body: String,
80-
{{/isString}}
81-
{{^isString}}
82-
body: Bytes,
83-
{{/isString}}
84-
{{/x-consumes-plain-text}}
85-
{{/vendorExtensions}}
86-
{{/bodyParam}}
87-
{{/x-consumes-multipart}}
88-
{{/x-consumes-multipart-related}}
89-
{{#x-consumes-multipart}}
90-
body: Multipart,
91-
{{/x-consumes-multipart}}
92-
{{#x-consumes-multipart-related}}
93-
body: axum::body::Body,
94-
{{/x-consumes-multipart-related}}
95-
) -> Result<{{{operationId}}}Response, String>;
96-
{{/vendorExtensions}}
97-
98-
{{/operation}}
99-
{{/operations}}
100-
{{/apis}}
101-
{{/apiInfo}}
102-
}
103-
10422
#[cfg(feature = "server")]
10523
pub mod server;
10624

10725
pub mod models;
10826
pub mod types;
27+
pub mod apis;
10928

11029
#[cfg(feature = "server")]
11130
pub(crate) mod header;

modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ use validator::{Validate, ValidationErrors};
1010
use crate::{header, types::*};
1111

1212
#[allow(unused_imports)]
13-
use crate::models;
13+
use crate::{apis, models};

modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
{{>server-imports}}
2-
use crate::{Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
3-
{{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
4-
};
52

63
{{>server-route}}
74
{{#apiInfo}}

modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A>(
4747
) -> Result<Response, StatusCode>
4848
where
4949
I: AsRef<A> + Send + Sync,
50-
A: Api,
50+
A: apis::{{classFilename}}::{{classnamePascalCase}},
5151
{
5252
{{#headerParams}}
5353
{{#-first}}
@@ -187,7 +187,7 @@ where
187187
let resp = match result {
188188
Ok(rsp) => match rsp {
189189
{{#responses}}
190-
{{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}}
190+
apis::{{classFilename}}::{{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}}
191191
{{#dataType}}
192192
{{^headers}}
193193
(body)

modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pub fn new<I, A>(api_impl: I) -> Router
33
where
44
I: AsRef<A> + Clone + Send + Sync + 'static,
5-
A: Api + 'static,
5+
A: {{#apiInfo}}{{#apis}}{{#operations}}apis::{{classFilename}}::{{classnamePascalCase}} + {{/operations}}{{/apis}}{{/apiInfo}}'static,
66
{
77
// build our application with a route
88
Router::new()

samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/FILES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
.gitignore
22
Cargo.toml
33
README.md
4+
src/apis/default.rs
5+
src/apis/mod.rs
46
src/header.rs
57
src/lib.rs
68
src/models.rs
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use async_trait::async_trait;
2+
use axum::extract::*;
3+
use axum_extra::extract::{CookieJar, Multipart};
4+
use bytes::Bytes;
5+
use http::Method;
6+
use serde::{Deserialize, Serialize};
7+
8+
use crate::{models, types::*};
9+
10+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
11+
#[must_use]
12+
#[allow(clippy::large_enum_variant)]
13+
pub enum MultipartRelatedRequestPostResponse {
14+
/// OK
15+
Status201_OK,
16+
}
17+
18+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
19+
#[must_use]
20+
#[allow(clippy::large_enum_variant)]
21+
pub enum MultipartRequestPostResponse {
22+
/// OK
23+
Status201_OK,
24+
}
25+
26+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
27+
#[must_use]
28+
#[allow(clippy::large_enum_variant)]
29+
pub enum MultipleIdenticalMimeTypesPostResponse {
30+
/// OK
31+
Status200_OK,
32+
}
33+
34+
/// Default
35+
#[async_trait]
36+
#[allow(clippy::ptr_arg)]
37+
pub trait Default {
38+
/// MultipartRelatedRequestPost - POST /multipart_related_request
39+
async fn multipart_related_request_post(
40+
&self,
41+
method: Method,
42+
host: Host,
43+
cookies: CookieJar,
44+
body: axum::body::Body,
45+
) -> Result<MultipartRelatedRequestPostResponse, String>;
46+
47+
/// MultipartRequestPost - POST /multipart_request
48+
async fn multipart_request_post(
49+
&self,
50+
method: Method,
51+
host: Host,
52+
cookies: CookieJar,
53+
body: Multipart,
54+
) -> Result<MultipartRequestPostResponse, String>;
55+
56+
/// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types
57+
async fn multiple_identical_mime_types_post(
58+
&self,
59+
method: Method,
60+
host: Host,
61+
cookies: CookieJar,
62+
body: axum::body::Body,
63+
) -> Result<MultipleIdenticalMimeTypesPostResponse, String>;
64+
}

0 commit comments

Comments
 (0)