Skip to content

Commit d7b808e

Browse files
whirmElric Milon
andauthored
[Rust] Add support for base64-encoded byte arrays (#18469)
Co-authored-by: Elric Milon <whirm@gmx.com>
1 parent efb7e56 commit d7b808e

File tree

22 files changed

+89
-19
lines changed

22 files changed

+89
-19
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,10 @@ public CodegenModel fromModel(String name, Schema model) {
270270

271271
@Override
272272
public ModelsMap postProcessModels(ModelsMap objs) {
273-
// Remove the discriminator field from the model, serde will take care of this
274273
for (ModelMap model : objs.getModels()) {
275274
CodegenModel cm = model.getModel();
276275

276+
// Remove the discriminator field from the model, serde will take care of this
277277
if (cm.discriminator != null) {
278278
String reserved_var_name = cm.discriminator.getPropertyBaseName();
279279

@@ -284,6 +284,14 @@ public ModelsMap postProcessModels(ModelsMap objs) {
284284
}
285285
}
286286
}
287+
288+
// Flag structs with byteArrays in them so that we can annotate them with the serde_as macro
289+
for (CodegenProperty cp : cm.vars) {
290+
if (cp.isByteArray) {
291+
cm.vendorExtensions.put("x-rust-has-byte-array", true);
292+
break;
293+
}
294+
}
287295
}
288296
// process enum in models
289297
return postProcessModelsEnum(objs);
@@ -537,6 +545,11 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
537545
if (property.isNullable && !property.required) {
538546
additionalProperties.put("serdeWith", true);
539547
}
548+
549+
// If a property is a base64-encoded byte array, use `serde_with` for deserialization.
550+
if (property.isByteArray) {
551+
additionalProperties.put("serdeWith", true);
552+
}
540553
}
541554

542555
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ homepage = "{{.}}
3535
serde = "^1.0"
3636
serde_derive = "^1.0"
3737
{{#serdeWith}}
38-
serde_with = "^2.0"
38+
serde_with = { version = "^3", default-features = false, features = ["base64", "std", "macros"] }
3939
{{/serdeWith}}
4040
serde_json = "^1.0"
4141
url = "^2.2"

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
use crate::models;
33
{{#models}}
44
{{#model}}
5-
5+
{{^isEnum}}{{#vendorExtensions.x-rust-has-byte-array}}
6+
use serde_with::serde_as;
7+
{{/vendorExtensions.x-rust-has-byte-array}}{{/isEnum}}
68
{{#description}}
79
/// {{{classname}}} : {{{description}}}
810
{{/description}}
@@ -85,23 +87,26 @@ impl Default for {{classname}} {
8587
{{!-- for non-enum schemas --}}
8688
{{^isEnum}}
8789
{{^discriminator}}
88-
{{#oneOf.isEmpty}}
89-
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
90+
{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as]
91+
{{/vendorExtensions.x-rust-has-byte-array}}{{#oneOf.isEmpty}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
9092
pub struct {{{classname}}} {
9193
{{#vars}}
9294
{{#description}}
9395
/// {{{.}}}
9496
{{/description}}
97+
{{#isByteArray}}
98+
{{#required}}#[serde_as(as = "serde_with::base64::Base64")]{{/required}}{{^required}}#[serde_as(as = "Option<serde_with::base64::Base64>")]{{/required}}
99+
{{/isByteArray}}
95100
#[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default, with = "::serde_with::rust::double_option"{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})]
96-
pub {{{name}}}: {{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{^isModel}}{{{dataType}}}{{/isModel}}{{/isEnum}}{{#isNullable}}>{{/isNullable}}{{^required}}>{{/required}},
101+
pub {{{name}}}: {{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{^isModel}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isModel}}{{/isEnum}}{{#isNullable}}>{{/isNullable}}{{^required}}>{{/required}},
97102
{{/vars}}
98103
}
99104

100105
impl {{{classname}}} {
101106
{{#description}}
102107
/// {{{.}}}
103108
{{/description}}
104-
pub fn new({{#requiredVars}}{{{name}}}: {{#isNullable}}Option<{{/isNullable}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}>{{/isNullable}}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{{classname}}} {
109+
pub fn new({{#requiredVars}}{{{name}}}: {{#isNullable}}Option<{{/isNullable}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isEnum}}{{#isNullable}}>{{/isNullable}}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{{classname}}} {
105110
{{{classname}}} {
106111
{{#vars}}
107112
{{{name}}}{{^required}}: None{{/required}}{{#required}}{{#isModel}}{{^avoidBoxedModels}}: {{^isNullable}}Box::new({{{name}}}){{/isNullable}}{{#isNullable}}if let Some(x) = {{{name}}} {Some(Box::new(x))} else {None}{{/isNullable}}{{/avoidBoxedModels}}{{/isModel}}{{/required}},

modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ components:
838838
- string
839839
- boolean
840840
- uuid
841+
- bytes
841842
properties:
842843
int32:
843844
type: integer
@@ -858,6 +859,9 @@ components:
858859
uuid:
859860
type: string
860861
format: uuid
862+
bytes:
863+
type: string
864+
format: byte
861865
Return:
862866
description: Test using keywords
863867
type: object

samples/client/petstore/rust/hyper/petstore/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ edition = "2018"
99
[dependencies]
1010
serde = "^1.0"
1111
serde_derive = "^1.0"
12-
serde_with = "^2.0"
12+
serde_with = { version = "^3", default-features = false, features = ["base64", "std", "macros"] }
1313
serde_json = "^1.0"
1414
url = "^2.2"
1515
uuid = { version = "^1.0", features = ["serde", "v4"] }

samples/client/petstore/rust/hyper/petstore/docs/TypeTesting.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Name | Type | Description | Notes
1111
**string** | **String** | |
1212
**boolean** | **bool** | |
1313
**uuid** | [**uuid::Uuid**](uuid::Uuid.md) | |
14+
**bytes** | **String** | |
1415

1516
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
1617

samples/client/petstore/rust/hyper/petstore/src/models/type_testing.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010

1111
use crate::models;
1212

13+
use serde_with::serde_as;
14+
1315
/// TypeTesting : Test handling of different field data types
16+
#[serde_as]
1417
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1518
pub struct TypeTesting {
1619
#[serde(rename = "int32")]
@@ -27,11 +30,14 @@ pub struct TypeTesting {
2730
pub boolean: bool,
2831
#[serde(rename = "uuid")]
2932
pub uuid: uuid::Uuid,
33+
#[serde_as(as = "serde_with::base64::Base64")]
34+
#[serde(rename = "bytes")]
35+
pub bytes: Vec<u8>,
3036
}
3137

3238
impl TypeTesting {
3339
/// Test handling of different field data types
34-
pub fn new(int32: i32, int64: i64, float: f32, double: f64, string: String, boolean: bool, uuid: uuid::Uuid) -> TypeTesting {
40+
pub fn new(int32: i32, int64: i64, float: f32, double: f64, string: String, boolean: bool, uuid: uuid::Uuid, bytes: Vec<u8>) -> TypeTesting {
3541
TypeTesting {
3642
int32,
3743
int64,
@@ -40,6 +46,7 @@ impl TypeTesting {
4046
string,
4147
boolean,
4248
uuid,
49+
bytes,
4350
}
4451
}
4552
}

samples/client/petstore/rust/reqwest/petstore-async-middleware/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ edition = "2018"
99
[dependencies]
1010
serde = "^1.0"
1111
serde_derive = "^1.0"
12-
serde_with = "^2.0"
12+
serde_with = { version = "^3", default-features = false, features = ["base64", "std", "macros"] }
1313
serde_json = "^1.0"
1414
url = "^2.2"
1515
uuid = { version = "^1.0", features = ["serde", "v4"] }

samples/client/petstore/rust/reqwest/petstore-async-middleware/docs/TypeTesting.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Name | Type | Description | Notes
1111
**string** | **String** | |
1212
**boolean** | **bool** | |
1313
**uuid** | [**uuid::Uuid**](uuid::Uuid.md) | |
14+
**bytes** | **String** | |
1415

1516
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
1617

samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/type_testing.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010

1111
use crate::models;
1212

13+
use serde_with::serde_as;
14+
1315
/// TypeTesting : Test handling of different field data types
16+
#[serde_as]
1417
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1518
pub struct TypeTesting {
1619
#[serde(rename = "int32")]
@@ -27,11 +30,14 @@ pub struct TypeTesting {
2730
pub boolean: bool,
2831
#[serde(rename = "uuid")]
2932
pub uuid: uuid::Uuid,
33+
#[serde_as(as = "serde_with::base64::Base64")]
34+
#[serde(rename = "bytes")]
35+
pub bytes: Vec<u8>,
3036
}
3137

3238
impl TypeTesting {
3339
/// Test handling of different field data types
34-
pub fn new(int32: i32, int64: i64, float: f32, double: f64, string: String, boolean: bool, uuid: uuid::Uuid) -> TypeTesting {
40+
pub fn new(int32: i32, int64: i64, float: f32, double: f64, string: String, boolean: bool, uuid: uuid::Uuid, bytes: Vec<u8>) -> TypeTesting {
3541
TypeTesting {
3642
int32,
3743
int64,
@@ -40,6 +46,7 @@ impl TypeTesting {
4046
string,
4147
boolean,
4248
uuid,
49+
bytes,
4350
}
4451
}
4552
}

0 commit comments

Comments
 (0)