Skip to content

Commit 4a21dea

Browse files
Added support for a trait based reqwest Rust client (#19788)
* Added Rust Reqwest trait based client * Fixed Cargo imports for reqwest trait template * Added support for mockall to Rust reqwest trait library * Added MockApiClient when mockall and topLevelClient are enabled * Added missing flags to Rust generator documentation * feat: add support for single argument and bon builder Adds support for single argument and bon building for the new reqwest-trait generator * Rebuilt rust samples --------- Co-authored-by: Troy Benson <[email protected]>
1 parent 03c29e7 commit 4a21dea

Some content is hidden

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

65 files changed

+4269
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
generatorName: rust
2+
outputDir: samples/client/petstore/rust/reqwest-trait/petstore
3+
library: reqwest-trait
4+
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/rust
6+
additionalProperties:
7+
topLevelApiClient: true
8+
packageName: petstore-reqwest
9+
mockall: true
10+
enumNameMappings:
11+
delivered: shipped

docs/generators/rust.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ These options may be applied as additional-properties (cli) or configOptions (pl
2222
|bestFitInt|Use best fitting integer type where minimum or maximum is set| |false|
2323
|enumNameSuffix|Suffix that will be appended to all enum names.| ||
2424
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
25-
|library|library template (sub-template) to use.|<dl><dt>**hyper**</dt><dd>HTTP client: Hyper (v1.x).</dd><dt>**hyper0x**</dt><dd>HTTP client: Hyper (v0.x).</dd><dt>**reqwest**</dt><dd>HTTP client: Reqwest.</dd></dl>|reqwest|
25+
|library|library template (sub-template) to use.|<dl><dt>**hyper**</dt><dd>HTTP client: Hyper (v1.x).</dd><dt>**hyper0x**</dt><dd>HTTP client: Hyper (v0.x).</dd><dt>**reqwest**</dt><dd>HTTP client: Reqwest.</dd><dt>**reqwest-trait**</dt><dd>HTTP client: Reqwest (trait based).</dd></dl>|reqwest|
26+
|mockall|Adds `#[automock]` from the mockall crate to api traits. This option is for 'reqwest-trait' library only| |false|
2627
|packageName|Rust package name (convention: lowercase).| |openapi|
2728
|packageVersion|Rust package version.| |1.0.0|
2829
|preferUnsignedInt|Prefer unsigned integers where minimum value is &gt;= 0| |false|
2930
|supportAsync|If set, generate async function call instead. This option is for 'reqwest' library only| |true|
30-
|supportMiddleware|If set, add support for reqwest-middleware. This option is for 'reqwest' library only| |false|
31-
|supportMultipleResponses|If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' library only| |false|
32-
|supportTokenSource|If set, add support for google-cloud-token. This option is for 'reqwest' library only and requires the 'supportAsync' option| |false|
31+
|supportMiddleware|If set, add support for reqwest-middleware. This option is for 'reqwest' and 'reqwest-trait' library only| |false|
32+
|supportMultipleResponses|If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' and 'reqwest-trait' library only| |false|
33+
|supportTokenSource|If set, add support for google-cloud-token. This option is for 'reqwest' and 'reqwest-trait' library only and requires the 'supportAsync' option| |false|
34+
|topLevelApiClient|Creates a top level `Api` trait and `ApiClient` struct that contain all Apis. This option is for 'reqwest-trait' library only| |false|
35+
|useBonBuilder|Use the bon crate for building parameter types. This option is for the 'reqwest-trait' library only| |false|
3336
|useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.| |false|
3437
|withAWSV4Signature|whether to include AWS v4 signature support| |false|
3538

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

+59-21
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,37 @@
1717

1818
package org.openapitools.codegen.languages;
1919

20-
import com.samskivert.mustache.Mustache;
21-
import com.samskivert.mustache.Template;
22-
import io.swagger.v3.oas.models.media.*;
23-
import io.swagger.v3.parser.util.SchemaTypeUtil;
24-
import joptsimple.internal.Strings;
25-
import lombok.AccessLevel;
26-
import lombok.Setter;
27-
import org.openapitools.codegen.*;
28-
import org.openapitools.codegen.meta.features.*;
20+
import java.io.File;
21+
import java.io.IOException;
22+
import java.io.Writer;
23+
import java.math.BigDecimal;
24+
import java.math.BigInteger;
25+
import java.util.Arrays;
26+
import java.util.Collections;
27+
import java.util.EnumSet;
28+
import java.util.HashSet;
29+
import java.util.List;
30+
import java.util.Locale;
31+
import java.util.Map;
32+
import java.util.Objects;
33+
import java.util.Optional;
34+
import java.util.stream.Collectors;
35+
36+
import org.openapitools.codegen.CliOption;
37+
import org.openapitools.codegen.CodegenConfig;
38+
import org.openapitools.codegen.CodegenConstants;
39+
import org.openapitools.codegen.CodegenModel;
40+
import org.openapitools.codegen.CodegenOperation;
41+
import org.openapitools.codegen.CodegenProperty;
42+
import org.openapitools.codegen.CodegenType;
43+
import org.openapitools.codegen.SupportingFile;
44+
import org.openapitools.codegen.meta.features.ClientModificationFeature;
45+
import org.openapitools.codegen.meta.features.DocumentationFeature;
46+
import org.openapitools.codegen.meta.features.GlobalFeature;
47+
import org.openapitools.codegen.meta.features.ParameterFeature;
48+
import org.openapitools.codegen.meta.features.SchemaSupportFeature;
49+
import org.openapitools.codegen.meta.features.SecurityFeature;
50+
import org.openapitools.codegen.meta.features.WireFormatFeature;
2951
import org.openapitools.codegen.model.ModelMap;
3052
import org.openapitools.codegen.model.ModelsMap;
3153
import org.openapitools.codegen.model.OperationMap;
@@ -35,13 +57,15 @@
3557
import org.slf4j.Logger;
3658
import org.slf4j.LoggerFactory;
3759

38-
import java.io.File;
39-
import java.io.IOException;
40-
import java.io.Writer;
41-
import java.math.BigDecimal;
42-
import java.math.BigInteger;
43-
import java.util.*;
44-
import java.util.stream.Collectors;
60+
import com.samskivert.mustache.Mustache;
61+
import com.samskivert.mustache.Template;
62+
63+
import io.swagger.v3.oas.models.media.Discriminator;
64+
import io.swagger.v3.oas.models.media.Schema;
65+
import io.swagger.v3.parser.util.SchemaTypeUtil;
66+
import joptsimple.internal.Strings;
67+
import lombok.AccessLevel;
68+
import lombok.Setter;
4569

4670
public class RustClientCodegen extends AbstractRustCodegen implements CodegenConfig {
4771
private final Logger LOGGER = LoggerFactory.getLogger(RustClientCodegen.class);
@@ -61,13 +85,18 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
6185
public static final String HYPER_LIBRARY = "hyper";
6286
public static final String HYPER0X_LIBRARY = "hyper0x";
6387
public static final String REQWEST_LIBRARY = "reqwest";
88+
public static final String REQWEST_TRAIT_LIBRARY = "reqwest-trait";
89+
public static final String REQWEST_TRAIT_LIBRARY_ATTR = "reqwestTrait";
6490
public static final String SUPPORT_ASYNC = "supportAsync";
6591
public static final String SUPPORT_MIDDLEWARE = "supportMiddleware";
6692
public static final String SUPPORT_TOKEN_SOURCE = "supportTokenSource";
6793
public static final String SUPPORT_MULTIPLE_RESPONSES = "supportMultipleResponses";
6894
public static final String PREFER_UNSIGNED_INT = "preferUnsignedInt";
6995
public static final String BEST_FIT_INT = "bestFitInt";
7096
public static final String AVOID_BOXED_MODELS = "avoidBoxedModels";
97+
public static final String TOP_LEVEL_API_CLIENT = "topLevelApiClient";
98+
public static final String MOCKALL = "mockall";
99+
public static final String BON_BUILDER = "useBonBuilder";
71100

72101
@Setter protected String packageName = "openapi";
73102
@Setter protected String packageVersion = "1.0.0";
@@ -192,11 +221,11 @@ public RustClientCodegen() {
192221
.defaultValue(Boolean.FALSE.toString()));
193222
cliOptions.add(new CliOption(SUPPORT_ASYNC, "If set, generate async function call instead. This option is for 'reqwest' library only", SchemaTypeUtil.BOOLEAN_TYPE)
194223
.defaultValue(Boolean.TRUE.toString()));
195-
cliOptions.add(new CliOption(SUPPORT_MIDDLEWARE, "If set, add support for reqwest-middleware. This option is for 'reqwest' library only", SchemaTypeUtil.BOOLEAN_TYPE)
224+
cliOptions.add(new CliOption(SUPPORT_MIDDLEWARE, "If set, add support for reqwest-middleware. This option is for 'reqwest' and 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
196225
.defaultValue(Boolean.FALSE.toString()));
197-
cliOptions.add(new CliOption(SUPPORT_TOKEN_SOURCE, "If set, add support for google-cloud-token. This option is for 'reqwest' library only and requires the 'supportAsync' option", SchemaTypeUtil.BOOLEAN_TYPE)
226+
cliOptions.add(new CliOption(SUPPORT_TOKEN_SOURCE, "If set, add support for google-cloud-token. This option is for 'reqwest' and 'reqwest-trait' library only and requires the 'supportAsync' option", SchemaTypeUtil.BOOLEAN_TYPE)
198227
.defaultValue(Boolean.FALSE.toString()));
199-
cliOptions.add(new CliOption(SUPPORT_MULTIPLE_RESPONSES, "If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' library only", SchemaTypeUtil.BOOLEAN_TYPE)
228+
cliOptions.add(new CliOption(SUPPORT_MULTIPLE_RESPONSES, "If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' and 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
200229
.defaultValue(Boolean.FALSE.toString()));
201230
cliOptions.add(new CliOption(CodegenConstants.ENUM_NAME_SUFFIX, CodegenConstants.ENUM_NAME_SUFFIX_DESC).defaultValue(this.enumSuffix));
202231
cliOptions.add(new CliOption(CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT, CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT_DESC, SchemaTypeUtil.BOOLEAN_TYPE)
@@ -207,10 +236,17 @@ public RustClientCodegen() {
207236
.defaultValue(Boolean.FALSE.toString()));
208237
cliOptions.add(new CliOption(AVOID_BOXED_MODELS, "If set, `Box<T>` will not be used for models", SchemaTypeUtil.BOOLEAN_TYPE)
209238
.defaultValue(Boolean.FALSE.toString()));
239+
cliOptions.add(new CliOption(MOCKALL, "Adds `#[automock]` from the mockall crate to api traits. This option is for 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
240+
.defaultValue(Boolean.FALSE.toString()));
241+
cliOptions.add(new CliOption(TOP_LEVEL_API_CLIENT, "Creates a top level `Api` trait and `ApiClient` struct that contain all Apis. This option is for 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
242+
.defaultValue(Boolean.FALSE.toString()));
243+
cliOptions.add(new CliOption(BON_BUILDER, "Use the bon crate for building parameter types. This option is for the 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
244+
.defaultValue(Boolean.FALSE.toString()));
210245

211246
supportedLibraries.put(HYPER_LIBRARY, "HTTP client: Hyper (v1.x).");
212247
supportedLibraries.put(HYPER0X_LIBRARY, "HTTP client: Hyper (v0.x).");
213248
supportedLibraries.put(REQWEST_LIBRARY, "HTTP client: Reqwest.");
249+
supportedLibraries.put(REQWEST_TRAIT_LIBRARY, "HTTP client: Reqwest (trait based).");
214250

215251
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use.");
216252
libraryOption.setEnum(supportedLibraries);
@@ -389,6 +425,8 @@ public void processOpts() {
389425
additionalProperties.put(HYPER0X_LIBRARY, "true");
390426
} else if (REQWEST_LIBRARY.equals(getLibrary())) {
391427
additionalProperties.put(REQWEST_LIBRARY, "true");
428+
} else if (REQWEST_TRAIT_LIBRARY.equals(getLibrary())) {
429+
additionalProperties.put(REQWEST_TRAIT_LIBRARY_ATTR, "true");
392430
} else {
393431
LOGGER.error("Unknown library option (-l/--library): {}", getLibrary());
394432
}
@@ -449,7 +487,7 @@ private boolean getSupportAsync() {
449487
private boolean getSupportMiddleware() {
450488
return supportMiddleware;
451489
}
452-
490+
453491
private boolean getSupportTokenSource() {
454492
return supportTokenSource;
455493
}
@@ -569,7 +607,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
569607
// http method verb conversion, depending on client library (e.g. Hyper: PUT => Put, Reqwest: PUT => put)
570608
if (HYPER_LIBRARY.equals(getLibrary())) {
571609
operation.httpMethod = StringUtils.camelize(operation.httpMethod.toLowerCase(Locale.ROOT));
572-
} else if (REQWEST_LIBRARY.equals(getLibrary())) {
610+
} else if (REQWEST_LIBRARY.equals(getLibrary()) || REQWEST_TRAIT_LIBRARY.equals(getLibrary())) {
573611
operation.httpMethod = operation.httpMethod.toUpperCase(Locale.ROOT);
574612
}
575613

modules/openapi-generator/src/main/resources/rust/Cargo.mustache

+24
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,27 @@ google-cloud-token = "^0.1"
7878
{{/supportTokenSource}}
7979
{{/supportAsync}}
8080
{{/reqwest}}
81+
{{#reqwestTrait}}
82+
async-trait = "^0.1"
83+
reqwest = { version = "^0.12", features = ["json", "multipart"] }
84+
{{#supportMiddleware}}
85+
reqwest-middleware = { version = "^0.3", features = ["json", "multipart"] }
86+
{{/supportMiddleware}}
87+
{{#supportTokenSource}}
88+
# TODO: propose to Yoshidan to externalize this as non google related crate, so that it can easily be extended for other cloud providers.
89+
google-cloud-token = "^0.1"
90+
{{/supportTokenSource}}
91+
{{#mockall}}
92+
mockall = { version = "^0.13", optional = true}
93+
{{/mockall}}
94+
{{#useBonBuilder}}
95+
bon = { version = "2.3", optional = true }
96+
{{/useBonBuilder}}
97+
[features]
98+
{{#mockall}}
99+
mockall = ["dep:mockall"]
100+
{{/mockall}}
101+
{{#useBonBuilder}}
102+
bon = ["dep:bon"]
103+
{{/useBonBuilder}}
104+
{{/reqwestTrait}}

0 commit comments

Comments
 (0)