Skip to content

Commit 2b40a2c

Browse files
authored
[Rust Server] Add auto-generated CLI Client (#19392)
* [Rust Server] Add auto-generated CLI tool * [Rust Server] Test multiple path parameters * [Rust Server] Test boolean parameters and apostrophes * [Rust Server] Test operation with two boolean parameters with same first letter * [Rust Server] Test apostrophes in operation summary * Update samples * [Rust Server] Fix build errors with OpenSSL * Update samples * Update samples
1 parent 69cce24 commit 2b40a2c

43 files changed

Lines changed: 5197 additions & 29 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/RustServerCodegen.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ public RustServerCodegen() {
250250
supportingFiles.add(new SupportingFile("example-ca.pem", "examples", "ca.pem"));
251251
supportingFiles.add(new SupportingFile("example-server-chain.pem", "examples", "server-chain.pem"));
252252
supportingFiles.add(new SupportingFile("example-server-key.pem", "examples", "server-key.pem"));
253+
supportingFiles.add(new SupportingFile("bin-cli.mustache", "bin", "cli.rs"));
253254
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
254255
.doNotOverwrite());
255256
}
@@ -586,6 +587,13 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
586587
String vendorExtensionHttpMethod = op.httpMethod.toUpperCase(Locale.ROOT);
587588
op.vendorExtensions.put("x-http-method", vendorExtensionHttpMethod);
588589

590+
boolean isDelete = op.httpMethod.toUpperCase(Locale.ROOT).equals("DELETE");
591+
op.vendorExtensions.put("x-is-delete", isDelete);
592+
593+
if (isDelete) {
594+
additionalProperties.put("apiHasDeleteMethods", true);
595+
}
596+
589597
if (!op.vendorExtensions.containsKey("x-must-use-response")) {
590598
// If there's more than one response, than by default the user must explicitly handle them
591599
op.vendorExtensions.put("x-must-use-response", op.responses.size() > 1);
@@ -858,6 +866,27 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
858866
op.vendorExtensions.put("x-has-request-body", true);
859867
}
860868

869+
// The CLI generates a structopt structure for each operation. This can only have a single
870+
// use of a short option, which comes from the parameter name, so we need to police
871+
// against duplicates
872+
HashMap<Character, CodegenParameter> availableOptions = new HashMap();
873+
874+
for (CodegenParameter p : op.allParams) {
875+
if (p.isBoolean && p.isPrimitiveType) {
876+
char shortOption = p.paramName.charAt(0);
877+
if (shortOption == 'a' || shortOption == 'o' || shortOption == 'f') {
878+
// These are used by serverAddress, output, and force
879+
p.vendorExtensions.put("x-provide-cli-short-opt", false);
880+
} else if (availableOptions.containsKey(shortOption)) {
881+
availableOptions.get(shortOption).vendorExtensions.put("x-provide-cli-short-opt", false);
882+
p.vendorExtensions.put("x-provide-cli-short-opt", false);
883+
} else {
884+
availableOptions.put(shortOption, p);
885+
p.vendorExtensions.put("x-provide-cli-short-opt", true);
886+
}
887+
}
888+
}
889+
861890
String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT);
862891

863892
if (op.bodyParam != null) {

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ server = [
6868
{{! Anything added to the list below, should probably be added to the callbacks list above }}
6969
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
7070
]
71+
cli = [
72+
{{#apiHasDeleteMethods}}
73+
"dialoguer",
74+
{{/apiHasDeleteMethods}}
75+
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
76+
]
7177
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
7278

7379
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@@ -126,6 +132,16 @@ lazy_static = { version = "1.4", optional = true }
126132
percent-encoding = {version = "2.1.0", optional = true}
127133
regex = {version = "1.3", optional = true}
128134

135+
# CLI-specific
136+
anyhow = { version = "1", optional = true }
137+
clap-verbosity-flag = { version = "0.3", optional = true }
138+
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
139+
structopt = { version = "0.3", optional = true }
140+
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
141+
{{#apiHasDeleteMethods}}
142+
dialoguer = { version = "0.8", optional = true }
143+
{{/apiHasDeleteMethods}}
144+
129145
# Conversion
130146
frunk = { version = "0.4.0", optional = true }
131147
frunk_derives = { version = "0.4.0", optional = true }
@@ -153,3 +169,8 @@ required-features = ["client"]
153169
[[example]]
154170
name = "server"
155171
required-features = ["server"]
172+
173+
[[bin]]
174+
name = "{{{packageName}}}"
175+
path = "bin/cli.rs"
176+
required-features = ["client", "cli"]

modules/openapi-generator/src/main/resources/rust-server/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The Rust Server Generator templates use Mustache Partials.
55
The following tree shows which templates include which:
66

77
- `api_doc.mustache`
8+
- `bin-cli.mustache`
89
- `cargo-config`
910
- `Cargo.mustache`
1011
- `context.mustache`

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ This autogenerated project defines an API crate `{{{packageName}}}` which contai
2828
* Data types representing the underlying data model.
2929
* A `Client` type which implements `Api` and issues HTTP requests for each operation.
3030
* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
31+
* A CLI tool to drive basic API operations from the command line.
3132

3233
It also contains an example server and client which make use of `{{{packageName}}}`:
3334

@@ -41,6 +42,30 @@ It also contains an example server and client which make use of `{{{packageName}
4142
You can use the example server and client as a basis for your own code.
4243
See below for [more detail on the examples](#using-the-generated-library).
4344

45+
## CLI
46+
47+
Run the included CLI tool with:
48+
49+
```
50+
cargo run --bin cli --features=cli
51+
```
52+
53+
To pass in arguments, put them after `--`, for example:
54+
55+
```
56+
cargo run --bin cli --features=cli -- --help
57+
```
58+
59+
See the help text for available options.
60+
61+
To build a standalone tool, use:
62+
63+
```
64+
cargo build --bin cli --features=cli --release
65+
```
66+
67+
You'll find the binary at `target/release/cli`.
68+
4469
## Examples
4570

4671
Run examples with:
@@ -103,6 +128,8 @@ The generated library has a few optional features that can be activated through
103128
* The constructed client implements the API trait by making remote API call.
104129
* `conversions`
105130
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
131+
* `cli`
132+
* This defaults to disabled and is required for building the included CLI tool.
106133

107134
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
108135

0 commit comments

Comments
 (0)