Skip to content

Commit a52e902

Browse files
authored
[RUST] Fix #22356 / Introduce useSerdePathToError option to improve JSON error messages (#22357)
* Add CLI option * Add dep to generated Cargo.toml * Add new `Error::SerdePathToError` error type * Add `serde_path_to_error` invocation to API layer * Add sample for serde-path-to-error * Add arg & docstring to cliOptions as well * Fix sample
1 parent 547eee1 commit a52e902

Some content is hidden

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

77 files changed

+3949
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
generatorName: rust
2+
outputDir: samples/client/petstore/rust/reqwest/petstore-serde-path-to-error
3+
library: reqwest
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+
packageName: petstore-reqwest-serde-path-to-error
8+
useSerdePathToError: true
9+
enumNameMappings:
10+
delivered: shipped

docs/generators/rust.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
3434
|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|
3535
|topLevelApiClient|Creates a top level `Api` trait and `ApiClient` struct that contain all Apis. This option is for 'reqwest-trait' library only| |false|
3636
|useBonBuilder|Use the bon crate for building parameter types. This option is for the 'reqwest-trait' library only| |false|
37+
|useSerdePathToError|If set, use the serde_path_to_error library to enhance serde error messages. This option is for 'reqwest' and 'reqwest-trait' library only| |false|
3738
|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|
3839
|withAWSV4Signature|whether to include AWS v4 signature support| |false|
3940

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
5252
@Setter(AccessLevel.PRIVATE) private boolean useSingleRequestParameter = false;
5353
@Setter(AccessLevel.PRIVATE) private boolean supportAsync = true;
5454
@Setter(AccessLevel.PRIVATE) private boolean supportMiddleware = false;
55+
@Setter(AccessLevel.PRIVATE) private boolean useSerdePathToError = false;
5556
@Setter(AccessLevel.PRIVATE) private boolean supportTokenSource = false;
5657
private boolean supportMultipleResponses = false;
5758
private boolean withAWSV4Signature = false;
@@ -70,6 +71,7 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
7071
public static final String REQWEST_TRAIT_LIBRARY_ATTR = "reqwestTrait";
7172
public static final String SUPPORT_ASYNC = "supportAsync";
7273
public static final String SUPPORT_MIDDLEWARE = "supportMiddleware";
74+
public static final String USE_SERDE_PATH_TO_ERROR = "useSerdePathToError";
7375
public static final String SUPPORT_TOKEN_SOURCE = "supportTokenSource";
7476
public static final String SUPPORT_MULTIPLE_RESPONSES = "supportMultipleResponses";
7577
public static final String PREFER_UNSIGNED_INT = "preferUnsignedInt";
@@ -210,6 +212,8 @@ public RustClientCodegen() {
210212
.defaultValue(Boolean.TRUE.toString()));
211213
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)
212214
.defaultValue(Boolean.FALSE.toString()));
215+
cliOptions.add(new CliOption(USE_SERDE_PATH_TO_ERROR, "If set, use the serde_path_to_error library to enhance serde error messages. This option is for 'reqwest' and 'reqwest-trait' library only", SchemaTypeUtil.BOOLEAN_TYPE)
216+
.defaultValue(Boolean.FALSE.toString()));
213217
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)
214218
.defaultValue(Boolean.FALSE.toString()));
215219
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)
@@ -410,6 +414,11 @@ public void processOpts() {
410414
}
411415
writePropertyBack(SUPPORT_MIDDLEWARE, getSupportMiddleware());
412416

417+
if (additionalProperties.containsKey(USE_SERDE_PATH_TO_ERROR)) {
418+
this.setUseSerdePathToError(convertPropertyToBoolean(USE_SERDE_PATH_TO_ERROR));
419+
}
420+
writePropertyBack(USE_SERDE_PATH_TO_ERROR, getUseSerdePathToError());
421+
413422
if (additionalProperties.containsKey(SUPPORT_TOKEN_SOURCE)) {
414423
this.setSupportTokenSource(convertPropertyToBoolean(SUPPORT_TOKEN_SOURCE));
415424
}
@@ -527,6 +536,10 @@ private boolean getSupportMiddleware() {
527536
return supportMiddleware;
528537
}
529538

539+
private boolean getUseSerdePathToError() {
540+
return useSerdePathToError;
541+
}
542+
530543
private boolean getSupportTokenSource() {
531544
return supportTokenSource;
532545
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ serde_with = { version = "^3.8", default-features = false, features = ["base64",
3838
{{/serdeWith}}
3939
serde_json = "^1.0"
4040
serde_repr = "^0.1"
41+
{{#useSerdePathToError}}
42+
serde_path_to_error = "^0.1"
43+
{{/useSerdePathToError}}
4144
url = "^2.5"
4245
{{#hasUUIDs}}
4346
uuid = { version = "^1.8", features = ["serde", "v4"] }

modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,12 @@ impl {{classname}} for {{classname}}Client {
488488
{{/returnType}}
489489
{{#returnType}}
490490
match local_var_content_type {
491+
{{^useSerdePathToError}}
491492
ContentType::Json => serde_json::from_str(&local_var_content).map_err(Error::from),
493+
{{/useSerdePathToError}}
494+
{{#useSerdePathToError}}
495+
ContentType::Json => serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_str(&local_var_content)).map_err(Error::from),
496+
{{/useSerdePathToError}}
492497
{{#vendorExtensions.x-supports-plain-text}}
493498
ContentType::Text => return Ok(local_var_content),
494499
{{/vendorExtensions.x-supports-plain-text}}

modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,12 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration:
528528
{{#returnType}}
529529
let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?;
530530
match content_type {
531+
{{^useSerdePathToError}}
531532
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
533+
{{/useSerdePathToError}}
534+
{{#useSerdePathToError}}
535+
ContentType::Json => serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_str(&content)).map_err(Error::from),
536+
{{/useSerdePathToError}}
532537
{{#vendorExtensions.x-supports-plain-text}}
533538
ContentType::Text => return Ok(content),
534539
{{/vendorExtensions.x-supports-plain-text}}

modules/openapi-generator/src/main/resources/rust/reqwest/api_mod.mustache

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub enum Error<T> {
1818
ReqwestMiddleware(reqwest_middleware::Error),
1919
{{/supportMiddleware}}
2020
Serde(serde_json::Error),
21+
{{#useSerdePathToError}}
22+
SerdePathToError(serde_path_to_error::Error<serde_json::Error>),
23+
{{/useSerdePathToError}}
2124
Io(std::io::Error),
2225
ResponseError(ResponseContent<T>),
2326
{{#withAWSV4Signature}}
@@ -38,6 +41,9 @@ impl <T> fmt::Display for Error<T> {
3841
Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()),
3942
{{/supportMiddleware}}
4043
Error::Serde(e) => ("serde", e.to_string()),
44+
{{#useSerdePathToError}}
45+
Error::SerdePathToError(e) => ("serde", format!("{}: {}", e.path().to_string(), e.inner().to_string())),
46+
{{/useSerdePathToError}}
4147
Error::Io(e) => ("IO", e.to_string()),
4248
Error::ResponseError(e) => ("response", format!("status code {}", e.status)),
4349
{{#withAWSV4Signature}}
@@ -61,6 +67,9 @@ impl <T: fmt::Debug> error::Error for Error<T> {
6167
Error::ReqwestMiddleware(e) => e,
6268
{{/supportMiddleware}}
6369
Error::Serde(e) => e,
70+
{{#useSerdePathToError}}
71+
Error::SerdePathToError(e) => e,
72+
{{/useSerdePathToError}}
6473
Error::Io(e) => e,
6574
Error::ResponseError(_) => return None,
6675
{{#withAWSV4Signature}}
@@ -95,6 +104,14 @@ impl <T> From<serde_json::Error> for Error<T> {
95104
}
96105
}
97106

107+
{{#useSerdePathToError}}
108+
impl<T> From<serde_path_to_error::Error<serde_json::Error>> for Error<T> {
109+
fn from(e: serde_path_to_error::Error<serde_json::Error>) -> Self {
110+
Error::SerdePathToError(e)
111+
}
112+
}
113+
114+
{{/useSerdePathToError}}
98115
impl <T> From<std::io::Error> for Error<T> {
99116
fn from(e: std::io::Error) -> Self {
100117
Error::Io(e)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target/
2+
**/*.rs.bk
3+
Cargo.lock
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.gitignore
2+
.travis.yml
3+
Cargo.toml
4+
README.md
5+
docs/ActionContainer.md
6+
docs/AnyTypeTest.md
7+
docs/ApiResponse.md
8+
docs/ArrayItemRefTest.md
9+
docs/Baz.md
10+
docs/Category.md
11+
docs/EnumArrayTesting.md
12+
docs/FakeApi.md
13+
docs/NullableArray.md
14+
docs/NumericEnumTesting.md
15+
docs/OptionalTesting.md
16+
docs/Order.md
17+
docs/Page.md
18+
docs/Person.md
19+
docs/Pet.md
20+
docs/PetApi.md
21+
docs/PropertyTest.md
22+
docs/Ref.md
23+
docs/Return.md
24+
docs/StoreApi.md
25+
docs/Tag.md
26+
docs/TestAllOfWithMultiMetadataOnly.md
27+
docs/TestingApi.md
28+
docs/TypeTesting.md
29+
docs/UniqueItemArrayTesting.md
30+
docs/User.md
31+
docs/UserApi.md
32+
docs/Vehicle.md
33+
docs/WithInnerOneOf.md
34+
git_push.sh
35+
src/apis/configuration.rs
36+
src/apis/fake_api.rs
37+
src/apis/mod.rs
38+
src/apis/pet_api.rs
39+
src/apis/store_api.rs
40+
src/apis/testing_api.rs
41+
src/apis/user_api.rs
42+
src/lib.rs
43+
src/models/action_container.rs
44+
src/models/any_type_test.rs
45+
src/models/api_response.rs
46+
src/models/array_item_ref_test.rs
47+
src/models/baz.rs
48+
src/models/category.rs
49+
src/models/enum_array_testing.rs
50+
src/models/mod.rs
51+
src/models/model_ref.rs
52+
src/models/model_return.rs
53+
src/models/nullable_array.rs
54+
src/models/numeric_enum_testing.rs
55+
src/models/optional_testing.rs
56+
src/models/order.rs
57+
src/models/page.rs
58+
src/models/person.rs
59+
src/models/pet.rs
60+
src/models/property_test.rs
61+
src/models/tag.rs
62+
src/models/test_all_of_with_multi_metadata_only.rs
63+
src/models/type_testing.rs
64+
src/models/unique_item_array_testing.rs
65+
src/models/user.rs
66+
src/models/vehicle.rs
67+
src/models/with_inner_one_of.rs

0 commit comments

Comments
 (0)