Skip to content

[Rust Server] Add auto-generated CLI Client #19392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 24, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ public RustServerCodegen() {
supportingFiles.add(new SupportingFile("example-ca.pem", "examples", "ca.pem"));
supportingFiles.add(new SupportingFile("example-server-chain.pem", "examples", "server-chain.pem"));
supportingFiles.add(new SupportingFile("example-server-key.pem", "examples", "server-key.pem"));
supportingFiles.add(new SupportingFile("bin-cli.mustache", "bin", "cli.rs"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
.doNotOverwrite());
}
Expand Down Expand Up @@ -586,6 +587,13 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
String vendorExtensionHttpMethod = op.httpMethod.toUpperCase(Locale.ROOT);
op.vendorExtensions.put("x-http-method", vendorExtensionHttpMethod);

boolean isDelete = op.httpMethod.toUpperCase(Locale.ROOT).equals("DELETE");
op.vendorExtensions.put("x-is-delete", isDelete);

if (isDelete) {
additionalProperties.put("apiHasDeleteMethods", true);
}

if (!op.vendorExtensions.containsKey("x-must-use-response")) {
// If there's more than one response, than by default the user must explicitly handle them
op.vendorExtensions.put("x-must-use-response", op.responses.size() > 1);
Expand Down Expand Up @@ -858,6 +866,27 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
op.vendorExtensions.put("x-has-request-body", true);
}

// The CLI generates a structopt structure for each operation. This can only have a single
// use of a short option, which comes from the parameter name, so we need to police
// against duplicates
HashMap<Character, CodegenParameter> availableOptions = new HashMap();

for (CodegenParameter p : op.allParams) {
if (p.isBoolean && p.isPrimitiveType) {
char shortOption = p.paramName.charAt(0);
if (shortOption == 'a' || shortOption == 'o' || shortOption == 'f') {
// These are used by serverAddress, output, and force
p.vendorExtensions.put("x-provide-cli-short-opt", false);
} else if (availableOptions.containsKey(shortOption)) {
availableOptions.get(shortOption).vendorExtensions.put("x-provide-cli-short-opt", false);
p.vendorExtensions.put("x-provide-cli-short-opt", false);
} else {
availableOptions.put(shortOption, p);
p.vendorExtensions.put("x-provide-cli-short-opt", true);
}
}
}

String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT);

if (op.bodyParam != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ server = [
{{! Anything added to the list below, should probably be added to the callbacks list above }}
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
{{#apiHasDeleteMethods}}
"dialoguer",
{{/apiHasDeleteMethods}}
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]

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

# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
{{#apiHasDeleteMethods}}
dialoguer = { version = "0.8", optional = true }
{{/apiHasDeleteMethods}}

# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
Expand Down Expand Up @@ -153,3 +169,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]

[[bin]]
name = "{{{packageName}}}"
path = "bin/cli.rs"
required-features = ["client", "cli"]
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The Rust Server Generator templates use Mustache Partials.
The following tree shows which templates include which:

- `api_doc.mustache`
- `bin-cli.mustache`
- `cargo-config`
- `Cargo.mustache`
- `context.mustache`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This autogenerated project defines an API crate `{{{packageName}}}` which contai
* Data types representing the underlying data model.
* A `Client` type which implements `Api` and issues HTTP requests for each operation.
* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* A CLI tool to drive basic API operations from the command line.

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

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

## CLI

Run the included CLI tool with:

```
cargo run --bin cli --features=cli
```

To pass in arguments, put them after `--`, for example:

```
cargo run --bin cli --features=cli -- --help
```

See the help text for available options.

To build a standalone tool, use:

```
cargo build --bin cli --features=cli --release
```

You'll find the binary at `target/release/cli`.

## Examples

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

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

Expand Down
Loading
Loading