Skip to content

Commit 3df525e

Browse files
gferonbjgill
authored andcommitted
Add enum support to rust and skip none option serialization in clients (#2244)
* Add support for enum schemas and properties to the rust generator Also: * Skip serializing a field with serde if it's optional and empty * Fix borrow checker error when using &std::path::Path (should be std::path::PathBuf) * Add script to generate sample with rust-reqwest * Regenerate petstore sample for both rust targets * Remove go code from README.md * Fix formatting of serde skip_serializing_if attribute
1 parent c2f5038 commit 3df525e

Some content is hidden

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

44 files changed

+457
-503
lines changed

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

+16-9
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ public RustClientCodegen() {
123123
// TODO(bcourtine): review file mapping.
124124
// I tried to map as "std::io::File", but Reqwest multipart file requires a "AsRef<Path>" param.
125125
// Getting a file from a Path is simple, but the opposite is difficult. So I map as "std::path::Path".
126-
// Reference is required here because Path does not implement the "Sized" trait.
127-
typeMapping.put("file", "&std::path::Path");
126+
typeMapping.put("file", "std::path::PathBuf");
128127
typeMapping.put("binary", "::models::File");
129128
typeMapping.put("ByteArray", "String");
130129
typeMapping.put("object", "Value");
@@ -151,6 +150,12 @@ public RustClientCodegen() {
151150
setLibrary(HYPER_LIBRARY);
152151
}
153152

153+
@Override
154+
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
155+
// process enum in models
156+
return postProcessModelsEnum(objs);
157+
}
158+
154159
@Override
155160
public void processOpts() {
156161
super.processOpts();
@@ -488,25 +493,25 @@ public String toEnumDefaultValue(String value, String datatype) {
488493
@Override
489494
public String toEnumVarName(String name, String datatype) {
490495
if (name.length() == 0) {
491-
return "EMPTY";
496+
return "Empty";
492497
}
493498

494499
// number
495500
if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) {
496501
String varName = name;
497-
varName = varName.replaceAll("-", "MINUS_");
498-
varName = varName.replaceAll("\\+", "PLUS_");
499-
varName = varName.replaceAll("\\.", "_DOT_");
502+
varName = varName.replaceAll("-", "Minus");
503+
varName = varName.replaceAll("\\+", "Plus");
504+
varName = varName.replaceAll("\\.", "Dot");
500505
return varName;
501506
}
502507

503508
// for symbol, e.g. $, #
504509
if (getSymbolName(name) != null) {
505-
return getSymbolName(name).toUpperCase(Locale.ROOT);
510+
return getSymbolName(name);
506511
}
507512

508513
// string
509-
String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT));
514+
String enumName = sanitizeName(camelize(name));
510515
enumName = enumName.replaceFirst("^_", "");
511516
enumName = enumName.replaceFirst("_$", "");
512517

@@ -519,7 +524,9 @@ public String toEnumVarName(String name, String datatype) {
519524

520525
@Override
521526
public String toEnumName(CodegenProperty property) {
522-
String enumName = underscore(toModelName(property.name)).toUpperCase(Locale.ROOT);
527+
// camelize the enum name
528+
// phone_number => PhoneNumber
529+
String enumName = camelize(toModelName(property.name));
523530

524531
// remove [] for array or map of enum
525532
enumName = enumName.replace("[]", "");

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

+4-61
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
2020

2121
## Installation
2222

23-
Put the package under your project folder and add the following in import:
23+
Put the package under your project folder and add the following to `Cargo.toml` under `[dependencies]`:
2424

2525
```
26-
"./{{{packageName}}}"
26+
openapi = { path = "./generated" }
2727
```
2828

2929
## Documentation for API Endpoints
@@ -40,69 +40,12 @@ Class | Method | HTTP request | Description
4040
{{#models}}{{#model}} - [{{{classname}}}]({{{modelDocPath}}}{{{classname}}}.md)
4141
{{/model}}{{/models}}
4242

43-
## Documentation For Authorization
44-
45-
{{^authMethods}} Endpoints do not require authorization.
46-
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
47-
{{#authMethods}}
48-
49-
## {{{name}}}
50-
51-
{{#isApiKey}}- **Type**: API key
52-
53-
Example
54-
```
55-
auth := context.WithValue(context.TODO(), sw.ContextAPIKey, sw.APIKey{
56-
Key: "APIKEY",
57-
Prefix: "Bearer", // Omit if not necessary.
58-
})
59-
r, err := client.Service.Operation(auth, args)
60-
```
61-
{{/isApiKey}}
62-
{{#isBasic}}- **Type**: HTTP basic authentication
63-
64-
Example
65-
66-
```
67-
auth := context.WithValue(context.TODO(), sw.ContextBasicAuth, sw.BasicAuth{
68-
UserName: "username",
69-
Password: "password",
70-
})
71-
r, err := client.Service.Operation(auth, args)
72-
```
73-
74-
{{/isBasic}}
75-
{{#isOAuth}}
76-
77-
- **Type**: OAuth
78-
- **Flow**: {{{flow}}}
79-
- **Authorization URL**: {{{authorizationUrl}}}
80-
- **Scopes**: {{^scopes}}N/A{{/scopes}}
81-
{{#scopes}} - **{{{scope}}}**: {{{description}}}
82-
{{/scopes}}
83-
84-
Example
85-
86-
```
87-
auth := context.WithValue(context.TODO(), sw.ContextAccessToken, "ACCESSTOKENSTRING")
88-
r, err := client.Service.Operation(auth, args)
89-
```
90-
91-
Or via OAuth2 module to automatically refresh tokens and perform user authentication.
43+
To get access to the crate's generated documentation, use:
9244

9345
```
94-
import "golang.org/x/oauth2"
95-
96-
/ .. Perform OAuth2 round trip request and obtain a token .. //
97-
98-
tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token)
99-
auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource)
100-
r, err := client.Service.Operation(auth, args)
46+
cargo doc --open
10147
```
10248

103-
{{/isOAuth}}
104-
{{/authMethods}}
105-
10649
## Author
10750

10851
{{#apiInfo}}{{#apis}}{{^hasMore}}{{{infoEmail}}}

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

+32-10
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@
88
#[allow(unused_imports)]
99
use serde_json::Value;
1010

11+
{{!-- for enum schemas --}}
12+
{{#isEnum}}
13+
/// {{{description}}}
14+
#[derive(Debug, Serialize, Deserialize)]
15+
pub enum {{classname}} {
16+
{{#allowableValues}}
17+
{{#enumVars}}
18+
#[serde(rename = "{{{value}}}")]
19+
{{name}},
20+
{{/enumVars}}{{/allowableValues}}
21+
}
22+
{{/isEnum}}
23+
24+
{{!-- for non-enum schemas --}}
25+
{{^isEnum}}
1126
#[derive(Debug, Serialize, Deserialize)]
1227
pub struct {{{classname}}} {
1328
{{#vars}}
1429
{{#description}}
1530
/// {{{description}}}
1631
{{/description}}
17-
#[serde(rename = "{{{baseName}}}")]
32+
#[serde(rename = "{{{baseName}}}"{{^required}}, skip_serializing_if = "Option::is_none"{{/required}})]
1833
pub {{{name}}}: {{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{^required}}Option<{{/required}}{{{dataType}}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^required}}>{{/required}},
1934
{{/vars}}
2035
}
@@ -31,16 +46,23 @@ impl {{{classname}}} {
3146
}
3247
}
3348
}
49+
{{/isEnum}}
50+
51+
{{!-- for properties that are of enum type --}}
52+
{{#vars}}
3453
{{#isEnum}}
35-
// TODO enum
36-
// List of {{{name}}}
37-
//const (
38-
// {{#allowableValues}}
39-
// {{#enumVars}}
40-
// {{{name}}} {{{classname}}} = "{{{value}}}"
41-
// {{/enumVars}}
42-
// {{/allowableValues}}
43-
//)
54+
/// {{{description}}}
55+
#[derive(Debug, Serialize, Deserialize)]
56+
pub enum {{enumName}} {
57+
{{#allowableValues}}
58+
{{#enumVars}}
59+
#[serde(rename = "{{{value}}}")]
60+
{{name}},
61+
{{/enumVars}}
62+
{{/allowableValues}}
63+
}
4464
{{/isEnum}}
65+
{{/vars}}
66+
4567
{{/model}}
4668
{{/models}}

samples/client/petstore/rust-reqwest/README.md

+7-36
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
44

55
## Overview
6+
67
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client.
78

89
- API version: 1.0.0
910
- Package version: 1.0.0
1011
- Build package: org.openapitools.codegen.languages.RustClientCodegen
1112

1213
## Installation
13-
Put the package under your project folder and add the following in import:
14+
15+
Put the package under your project folder and add the following to `Cargo.toml` under `[dependencies]`:
16+
1417
```
15-
"./petstore_client"
18+
openapi = { path = "./generated" }
1619
```
1720

1821
## Documentation for API Endpoints
@@ -53,42 +56,10 @@ Class | Method | HTTP request | Description
5356
- [User](docs/User.md)
5457

5558

56-
## Documentation For Authorization
59+
To get access to the crate's generated documentation, use:
5760

58-
## api_key
59-
- **Type**: API key
60-
61-
Example
62-
```
63-
auth := context.WithValue(context.TODO(), sw.ContextAPIKey, sw.APIKey{
64-
Key: "APIKEY",
65-
Prefix: "Bearer", // Omit if not necessary.
66-
})
67-
r, err := client.Service.Operation(auth, args)
68-
```
69-
## petstore_auth
70-
- **Type**: OAuth
71-
- **Flow**: implicit
72-
- **Authorization URL**: http://petstore.swagger.io/api/oauth/dialog
73-
- **Scopes**:
74-
- **write:pets**: modify pets in your account
75-
- **read:pets**: read your pets
76-
77-
Example
7861
```
79-
auth := context.WithValue(context.TODO(), sw.ContextAccessToken, "ACCESSTOKENSTRING")
80-
r, err := client.Service.Operation(auth, args)
81-
```
82-
83-
Or via OAuth2 module to automatically refresh tokens and perform user authentication.
84-
```
85-
import "golang.org/x/oauth2"
86-
87-
/ .. Perform OAuth2 round trip request and obtain a token .. //
88-
89-
tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token)
90-
auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource)
91-
r, err := client.Service.Operation(auth, args)
62+
cargo doc --open
9263
```
9364

9465
## Author

samples/client/petstore/rust-reqwest/docs/ApiResponse.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# ApiResponse
22

33
## Properties
4+
45
Name | Type | Description | Notes
56
------------ | ------------- | ------------- | -------------
67
**code** | **i32** | | [optional]

samples/client/petstore/rust-reqwest/docs/Category.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Category
22

33
## Properties
4+
45
Name | Type | Description | Notes
56
------------ | ------------- | ------------- | -------------
67
**id** | **i64** | | [optional]

samples/client/petstore/rust-reqwest/docs/Order.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Order
22

33
## Properties
4+
45
Name | Type | Description | Notes
56
------------ | ------------- | ------------- | -------------
67
**id** | **i64** | | [optional]

samples/client/petstore/rust-reqwest/docs/Pet.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Pet
22

33
## Properties
4+
45
Name | Type | Description | Notes
56
------------ | ------------- | ------------- | -------------
67
**id** | **i64** | | [optional]

0 commit comments

Comments
 (0)