diff --git a/.github/workflows/samples-rust.yaml b/.github/workflows/samples-rust.yaml
index 832ddf3e7e6a..b46265ca16a6 100644
--- a/.github/workflows/samples-rust.yaml
+++ b/.github/workflows/samples-rust.yaml
@@ -26,12 +26,54 @@ jobs:
- samples/client/others/rust/
- samples/client/petstore/rust/
- samples/server/petstore/rust-server/
+ - samples/server/petstore/rust-server-deprecated/
- samples/server/petstore/rust-axum/
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
+
+ - name: Rust cache
+ uses: Swatinem/rust-cache@v2
+ with:
+ cache-targets: false # Don't cache workspace target directories as they don't exist
+ cache-directories:
+ ${{ matrix.sample }}/target
+ workspaces: |
+ ${{ matrix.sample }}/output/*
+
- name: Build
working-directory: ${{ matrix.sample }}
run: cargo build --all-targets --all-features
+ - name: Tests
+ working-directory: ${{ matrix.sample }}
+ run: |
+ set -e
+ # Skip samples/client/petstore/rust/ as it's tests are failing.
+ if [[ "${{ matrix.sample }}" == "samples/client/petstore/rust/" ]]; then
+ echo "Skipping tests for samples/client/petstore/rust/"
+ exit 0
+ fi
+
+ # Iterate through each example and test various features
+ for package in $(find . -maxdepth 1 -mindepth 1 -type d)
+ do
+ # Not all versions have a client example
+ if test -f examples/client/main.rs; then
+ cargo build --example client --features="client"
+ fi
+ # Not all versions have a server example
+ if test -f examples/server/main.rs; then
+ cargo build --example server --features="server"
+ fi
+ # Test the CLI works if present
+ if test -f bin/cli.rs; then
+ cargo build --bin ${package##*/} --features cli
+ target/debug/${package##*/} --help
+ fi
+ cargo fmt
+ cargo test
+ cargo clippy
+ cargo doc
+ done
diff --git a/bin/configs/rust-server-deprecated-multipart-v3.yaml b/bin/configs/rust-server-deprecated-multipart-v3.yaml
new file mode 100644
index 000000000000..e014bc087895
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-multipart-v3.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/multipart-v3
+inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/multipart-v3.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: multipart-v3
diff --git a/bin/configs/rust-server-deprecated-no-example-v3.yaml b/bin/configs/rust-server-deprecated-no-example-v3.yaml
new file mode 100644
index 000000000000..df0e2c2271ae
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-no-example-v3.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/no-example-v3
+inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/no-example-v3.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: no-example-v3
diff --git a/bin/configs/rust-server-deprecated-openapi-v3.yaml b/bin/configs/rust-server-deprecated-openapi-v3.yaml
new file mode 100644
index 000000000000..f4fc0e28ed8c
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-openapi-v3.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/openapi-v3
+inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: openapi-v3
diff --git a/bin/configs/rust-server-deprecated-ops-v3.yaml b/bin/configs/rust-server-deprecated-ops-v3.yaml
new file mode 100644
index 000000000000..471bb16e0c94
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-ops-v3.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/ops-v3
+inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ops-v3.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: ops-v3
diff --git a/bin/configs/rust-server-deprecated-petstore-with-fake-endpoints-models-for-testing.yaml b/bin/configs/rust-server-deprecated-petstore-with-fake-endpoints-models-for-testing.yaml
new file mode 100644
index 000000000000..0a88b74392b3
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-petstore-with-fake-endpoints-models-for-testing.yaml
@@ -0,0 +1,9 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/petstore-with-fake-endpoints-models-for-testing
+inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/petstore-with-fake-endpoints-models-for-testing.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: petstore-with-fake-endpoints-models-for-testing
+ publishRustRegistry: crates-io
diff --git a/bin/configs/rust-server-deprecated-ping-bearer-auth-v3.yaml b/bin/configs/rust-server-deprecated-ping-bearer-auth-v3.yaml
new file mode 100644
index 000000000000..fb4dcf78258f
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-ping-bearer-auth-v3.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/ping-bearer-auth
+inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ping-bearer-auth.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: ping-bearer-auth
diff --git a/bin/configs/rust-server-deprecated-test.yaml b/bin/configs/rust-server-deprecated-test.yaml
new file mode 100644
index 000000000000..bcda8dfb0161
--- /dev/null
+++ b/bin/configs/rust-server-deprecated-test.yaml
@@ -0,0 +1,8 @@
+generatorName: rust-server-deprecated
+outputDir: samples/server/petstore/rust-server-deprecated/output/rust-server-test
+inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/rust-server-test.yaml
+templateDir: modules/openapi-generator/src/main/resources/rust-server-deprecated
+generateAliasAsModel: true
+additionalProperties:
+ hideGenerationTimestamp: "true"
+ packageName: rust-server-test
diff --git a/docs/generators.md b/docs/generators.md
index c74dfe531722..4c968c0f87a0 100644
--- a/docs/generators.md
+++ b/docs/generators.md
@@ -142,6 +142,7 @@ The following generators are available:
* [ruby-sinatra](generators/ruby-sinatra.md)
* [rust-axum (beta)](generators/rust-axum.md)
* [rust-server](generators/rust-server.md)
+* [rust-server-deprecated](generators/rust-server-deprecated.md)
* [scala-akka-http-server (beta)](generators/scala-akka-http-server.md)
* [scala-cask](generators/scala-cask.md)
* [scala-finch](generators/scala-finch.md)
diff --git a/docs/generators/rust-server-deprecated.md b/docs/generators/rust-server-deprecated.md
new file mode 100644
index 000000000000..1aca84f8e75d
--- /dev/null
+++ b/docs/generators/rust-server-deprecated.md
@@ -0,0 +1,232 @@
+---
+title: Documentation for the rust-server-deprecated Generator
+---
+
+## METADATA
+
+| Property | Value | Notes |
+| -------- | ----- | ----- |
+| generator name | rust-server-deprecated | pass this to the generate command after -g |
+| generator stability | STABLE | |
+| generator type | SERVER | |
+| generator language | Rust | |
+| generator default templating engine | mustache | |
+| helpTxt | Generates a Rust Hyper/Tower server library based on hyper 0.14. Also generates a matching Hyper client library within the same crate that implements the same trait. | |
+
+## CONFIG OPTIONS
+These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
+
+| Option | Description | Values | Default |
+| ------ | ----------- | ------ | ------- |
+|packageName|Rust crate name (convention: snake_case).| |openapi_client|
+|packageVersion|Rust crate version.| |null|
+
+## IMPORT MAPPING
+
+| Type/Alias | Imports |
+| ---------- | ------- |
+
+
+## INSTANTIATION TYPES
+
+| Type/Alias | Instantiated By |
+| ---------- | --------------- |
+|array|Vec|
+|map|std::collections::HashMap|
+
+
+## LANGUAGE PRIMITIVES
+
+
+- String
+- bool
+- char
+- f32
+- f64
+- i16
+- i32
+- i64
+- i8
+- isize
+- str
+- u16
+- u32
+- u64
+- u8
+- usize
+
+
+## RESERVED WORDS
+
+
+- Self
+- abstract
+- as
+- async
+- await
+- become
+- box
+- break
+- const
+- continue
+- crate
+- do
+- dyn
+- else
+- enum
+- extern
+- false
+- final
+- fn
+- for
+- if
+- impl
+- in
+- let
+- loop
+- macro
+- match
+- mod
+- move
+- mut
+- override
+- priv
+- pub
+- ref
+- return
+- self
+- static
+- struct
+- super
+- trait
+- true
+- try
+- type
+- typeof
+- unsafe
+- unsized
+- use
+- virtual
+- where
+- while
+- yield
+
+
+## FEATURE SET
+
+
+### Client Modification Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasePath|✓|ToolingExtension
+|Authorizations|✗|ToolingExtension
+|UserAgent|✗|ToolingExtension
+|MockServer|✗|ToolingExtension
+
+### Data Type Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Custom|✗|OAS2,OAS3
+|Int32|✓|OAS2,OAS3
+|Int64|✓|OAS2,OAS3
+|Float|✓|OAS2,OAS3
+|Double|✓|OAS2,OAS3
+|Decimal|✓|ToolingExtension
+|String|✓|OAS2,OAS3
+|Byte|✓|OAS2,OAS3
+|Binary|✓|OAS2,OAS3
+|Boolean|✓|OAS2,OAS3
+|Date|✓|OAS2,OAS3
+|DateTime|✓|OAS2,OAS3
+|Password|✓|OAS2,OAS3
+|File|✓|OAS2
+|Uuid|✗|
+|Array|✓|OAS2,OAS3
+|Null|✗|OAS3
+|AnyType|✗|OAS2,OAS3
+|Object|✓|OAS2,OAS3
+|Maps|✓|ToolingExtension
+|CollectionFormat|✓|OAS2
+|CollectionFormatMulti|✓|OAS2
+|Enum|✓|OAS2,OAS3
+|ArrayOfEnum|✓|ToolingExtension
+|ArrayOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
+|ArrayOfCollectionOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfEnum|✓|ToolingExtension
+|MapOfEnum|✓|ToolingExtension
+|MapOfModel|✓|ToolingExtension
+|MapOfCollectionOfPrimitives|✓|ToolingExtension
+|MapOfCollectionOfModel|✓|ToolingExtension
+|MapOfCollectionOfEnum|✓|ToolingExtension
+
+### Documentation Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Readme|✓|ToolingExtension
+|Model|✓|ToolingExtension
+|Api|✓|ToolingExtension
+
+### Global Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Host|✓|OAS2,OAS3
+|BasePath|✓|OAS2,OAS3
+|Info|✓|OAS2,OAS3
+|Schemes|✗|OAS2,OAS3
+|PartialSchemes|✓|OAS2,OAS3
+|Consumes|✓|OAS2
+|Produces|✓|OAS2
+|ExternalDocumentation|✓|OAS2,OAS3
+|Examples|✓|OAS2,OAS3
+|XMLStructureDefinitions|✗|OAS2,OAS3
+|MultiServer|✗|OAS3
+|ParameterizedServer|✗|OAS3
+|ParameterStyling|✗|OAS3
+|Callbacks|✓|OAS3
+|LinkObjects|✗|OAS3
+
+### Parameter Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Path|✓|OAS2,OAS3
+|Query|✓|OAS2,OAS3
+|Header|✓|OAS2,OAS3
+|Body|✓|OAS2
+|FormUnencoded|✓|OAS2
+|FormMultipart|✓|OAS2
+|Cookie|✗|OAS3
+
+### Schema Support Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Simple|✓|OAS2,OAS3
+|Composite|✓|OAS2,OAS3
+|Polymorphism|✗|OAS2,OAS3
+|Union|✗|OAS3
+|allOf|✗|OAS2,OAS3
+|anyOf|✗|OAS3
+|oneOf|✗|OAS3
+|not|✗|OAS3
+
+### Security Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasicAuth|✓|OAS2,OAS3
+|ApiKey|✓|OAS2,OAS3
+|OpenIDConnect|✗|OAS3
+|BearerToken|✓|OAS3
+|OAuth2_Implicit|✓|OAS2,OAS3
+|OAuth2_Password|✗|OAS2,OAS3
+|OAuth2_ClientCredentials|✗|OAS2,OAS3
+|OAuth2_AuthorizationCode|✗|OAS2,OAS3
+|SignatureAuth|✗|OAS3
+|AWSV4Signature|✗|ToolingExtension
+
+### Wire Format Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|JSON|✓|OAS2,OAS3
+|XML|✓|OAS2,OAS3
+|PROTOBUF|✗|ToolingExtension
+|Custom|✓|OAS2,OAS3
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
index 33918bed8462..2a4f74bf7d7d 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
@@ -54,6 +54,7 @@
import org.openapitools.codegen.languages.PhpNextgenClientCodegen;
import org.openapitools.codegen.languages.RustAxumServerCodegen;
import org.openapitools.codegen.languages.RustServerCodegen;
+import org.openapitools.codegen.languages.RustServerCodegenDeprecated;
import org.openapitools.codegen.meta.FeatureSet;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
@@ -5328,7 +5329,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports)
parameterSchema = unaliasSchema(parameter.getSchema());
parameterModelName = getParameterDataType(parameter, parameterSchema);
CodegenProperty prop;
- if (this instanceof RustServerCodegen) {
+ if (this instanceof RustServerCodegen || this instanceof RustServerCodegenDeprecated) {
// for rust server, we need to do something special as it uses
// $ref (e.g. #components/schemas/Pet) to determine whether it's a model
prop = fromProperty(parameter.getName(), parameterSchema, false);
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java
index ec9b3de2340a..54112fcc5e1d 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java
@@ -46,6 +46,7 @@
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
@@ -222,7 +223,7 @@ public RustServerCodegen() {
*/
supportingFiles.add(new SupportingFile("openapi.mustache", "api", "openapi.yaml"));
supportingFiles.add(new SupportingFile("Cargo.mustache", "", "Cargo.toml"));
- supportingFiles.add(new SupportingFile("cargo-config", ".cargo", "config"));
+ supportingFiles.add(new SupportingFile("cargo-config", ".cargo", "config.toml"));
supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
supportingFiles.add(new SupportingFile("lib.mustache", "src", "lib.rs"));
supportingFiles.add(new SupportingFile("context.mustache", "src", "context.rs"));
@@ -1531,7 +1532,16 @@ private void processParam(CodegenParameter param, CodegenOperation op) {
}
} else if (param.isArray) {
param.vendorExtensions.put("x-format-string", "{:?}");
- example = (param.example != null) ? param.example : "&Vec::new()";
+ if (param.items.isString) {
+ // We iterate through the list of string and ensure they end up in the format vec!["example".to_string()]
+ example = (param.example != null)
+ ? "&vec![" + Arrays.stream(param.example.replace("[", "").replace("]", "").split(","))
+ .map(item -> item + ".to_string()")
+ .collect(Collectors.joining(", ")) + "]"
+ : "&Vec::new()";
+ } else {
+ example = (param.example != null) ? param.example : "&Vec::new()";
+ }
} else {
param.vendorExtensions.put("x-format-string", "{:?}");
if (param.example != null) {
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java
new file mode 100644
index 000000000000..7297b8b4de41
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java
@@ -0,0 +1,1658 @@
+/*
+ * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
+ * Copyright 2018 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openapitools.codegen.languages;
+
+import io.swagger.v3.core.util.Json;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.media.*;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import io.swagger.v3.oas.models.parameters.RequestBody;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.servers.Server;
+import joptsimple.internal.Strings;
+import lombok.Setter;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.meta.features.*;
+import org.openapitools.codegen.model.*;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.openapitools.codegen.utils.URLPathUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.net.URL;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static org.openapitools.codegen.utils.StringUtils.camelize;
+import static org.openapitools.codegen.utils.StringUtils.underscore;
+
+public class RustServerCodegenDeprecated extends AbstractRustCodegen implements CodegenConfig {
+
+ private final Logger LOGGER = LoggerFactory.getLogger(RustServerCodegenDeprecated.class);
+
+ private Map modelXmlNames = new HashMap();
+
+ protected String apiVersion = "1.0.0";
+ protected String serverHost = "localhost";
+ protected int serverPort = 8080;
+ protected String projectName = "openapi-server";
+ protected String apiPath = "rust-server";
+ protected String apiDocPath = "docs/";
+ protected String modelDocPath = "docs/";
+ protected String packageName;
+ @Setter protected String packageVersion;
+ protected String externCrateName;
+ protected Map> pathSetMap = new HashMap();
+ protected Map> callbacksPathSetMap = new HashMap();
+
+ private static final String uuidType = "uuid::Uuid";
+ private static final String bytesType = "swagger::ByteArray";
+
+ private static final String xmlMimeType = "application/xml";
+ private static final String textXmlMimeType = "text/xml";
+ private static final String octetMimeType = "application/octet-stream";
+ private static final String plainTextMimeType = "text/plain";
+ private static final String jsonMimeType = "application/json";
+
+ // RFC 7386 support
+ private static final String mergePatchJsonMimeType = "application/merge-patch+json";
+
+ // RFC 7807 Support
+ private static final String problemJsonMimeType = "application/problem+json";
+ private static final String problemXmlMimeType = "application/problem+xml";
+
+ public RustServerCodegenDeprecated() {
+ super();
+
+ modifyFeatureSet(features -> features
+ .includeDocumentationFeatures(DocumentationFeature.Readme)
+ .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
+ .securityFeatures(EnumSet.of(
+ SecurityFeature.ApiKey,
+ SecurityFeature.BasicAuth,
+ SecurityFeature.BearerToken,
+ SecurityFeature.OAuth2_Implicit
+ ))
+ .excludeGlobalFeatures(
+ GlobalFeature.LinkObjects,
+ GlobalFeature.ParameterStyling
+ )
+ .excludeSchemaSupportFeatures(
+ SchemaSupportFeature.Polymorphism
+ )
+ .excludeParameterFeatures(
+ ParameterFeature.Cookie
+ )
+ .includeClientModificationFeatures(
+ ClientModificationFeature.BasePath
+ )
+ );
+
+ // We need inline enums to be resolved to a separate model so that
+ // anyOf/oneOf that contain them work correctly.
+ inlineSchemaOption.put("RESOLVE_INLINE_ENUMS", "true");
+
+ // Show the generation timestamp by default
+ hideGenerationTimestamp = Boolean.FALSE;
+
+ // set the output folder here
+ outputFolder = "generated-code/rust-server";
+
+ /*
+ * Models. You can write model files using the modelTemplateFiles map.
+ * if you want to create one template for file, you can do so here.
+ * for multiple files for model, just put another entry in the `modelTemplateFiles` with
+ * a different extension
+ */
+ modelTemplateFiles.clear();
+
+ /*
+ * Api classes. You can write classes for each Api file with the apiTemplateFiles map.
+ * as with models, add multiple entries with different extensions for multiple files per
+ * class
+ */
+ apiTemplateFiles.clear();
+
+ modelDocTemplateFiles.put("model_doc.mustache", ".md");
+ apiDocTemplateFiles.put("api_doc.mustache", ".md");
+
+ /*
+ * Template Location. This is the location which templates will be read from. The generator
+ * will use the resource stream to attempt to read the templates.
+ */
+ embeddedTemplateDir = templateDir = "rust-server";
+
+ defaultIncludes = new HashSet<>(
+ Arrays.asList(
+ "map",
+ "array")
+ );
+
+ languageSpecificPrimitives = new HashSet<>(
+ Arrays.asList(
+ "bool",
+ "char",
+ "i8",
+ "i16",
+ "i32",
+ "i64",
+ "u8",
+ "u16",
+ "u32",
+ "u64",
+ "isize",
+ "usize",
+ "f32",
+ "f64",
+ "str",
+ "String")
+ );
+
+ instantiationTypes.clear();
+ instantiationTypes.put("array", "Vec");
+ instantiationTypes.put("map", "std::collections::HashMap");
+
+ typeMapping.clear();
+ typeMapping.put("number", "f64");
+ typeMapping.put("integer", "i32");
+ typeMapping.put("long", "i64");
+ typeMapping.put("float", "f32");
+ typeMapping.put("double", "f64");
+ typeMapping.put("string", "String");
+ typeMapping.put("UUID", uuidType);
+ typeMapping.put("URI", "String");
+ typeMapping.put("byte", "u8");
+ typeMapping.put("ByteArray", bytesType);
+ typeMapping.put("binary", bytesType);
+ typeMapping.put("boolean", "bool");
+ typeMapping.put("date", "chrono::naive::NaiveDate");
+ typeMapping.put("DateTime", "chrono::DateTime::");
+ typeMapping.put("password", "String");
+ typeMapping.put("File", bytesType);
+ typeMapping.put("file", bytesType);
+ typeMapping.put("array", "Vec");
+ typeMapping.put("map", "std::collections::HashMap");
+ typeMapping.put("object", "serde_json::Value");
+ typeMapping.put("AnyType", "serde_json::Value");
+
+ importMapping = new HashMap();
+
+ cliOptions.clear();
+ cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME,
+ "Rust crate name (convention: snake_case).")
+ .defaultValue("openapi_client"));
+ cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION,
+ "Rust crate version."));
+
+ /*
+ * Additional Properties. These values can be passed to the templates and
+ * are available in models, apis, and supporting files
+ */
+ additionalProperties.put("apiVersion", apiVersion);
+ additionalProperties.put("apiPath", apiPath);
+
+ /*
+ * Supporting Files. You can write single files for the generator with the
+ * entire object tree available. If the input file has a suffix of `.mustache
+ * it will be processed by the template engine. Otherwise, it will be copied
+ */
+ supportingFiles.add(new SupportingFile("openapi.mustache", "api", "openapi.yaml"));
+ supportingFiles.add(new SupportingFile("Cargo.mustache", "", "Cargo.toml"));
+ supportingFiles.add(new SupportingFile("cargo-config", ".cargo", "config"));
+ supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
+ supportingFiles.add(new SupportingFile("lib.mustache", "src", "lib.rs"));
+ supportingFiles.add(new SupportingFile("context.mustache", "src", "context.rs"));
+ supportingFiles.add(new SupportingFile("models.mustache", "src", "models.rs"));
+ supportingFiles.add(new SupportingFile("header.mustache", "src", "header.rs"));
+ supportingFiles.add(new SupportingFile("auth.mustache", "src", "auth.rs"));
+ supportingFiles.add(new SupportingFile("server-mod.mustache", "src/server", "mod.rs"));
+ supportingFiles.add(new SupportingFile("server-server_auth.mustache", "src/server", "server_auth.rs"));
+ supportingFiles.add(new SupportingFile("client-mod.mustache", "src/client", "mod.rs"));
+ supportingFiles.add(new SupportingFile("example-server-main.mustache", "examples/server", "main.rs"));
+ supportingFiles.add(new SupportingFile("example-server-server.mustache", "examples/server", "server.rs"));
+ supportingFiles.add(new SupportingFile("example-server-auth.mustache", "examples/server", "server_auth.rs"));
+ supportingFiles.add(new SupportingFile("example-client-main.mustache", "examples/client", "main.rs"));
+ supportingFiles.add(new SupportingFile("example-client-auth.mustache", "examples/client", "client_auth.rs"));
+ 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());
+ }
+
+ @Override
+ public void processOpts() {
+ super.processOpts();
+
+ if (StringUtils.isEmpty(System.getenv("RUST_POST_PROCESS_FILE"))) {
+ LOGGER.info("Environment variable RUST_POST_PROCESS_FILE not defined. rustfmt will be used" +
+ " by default. To choose a different tool, try" +
+ " 'export RUST_POST_PROCESS_FILE=\"/usr/local/bin/rustfmt\"' (Linux/Mac)");
+ LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` " +
+ " (--enable-post-process-file for CLI).");
+ } else if (!this.isEnablePostProcessFile()) {
+ LOGGER.info("Warning: Environment variable 'RUST_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
+ }
+
+ if (!Boolean.TRUE.equals(ModelUtils.isGenerateAliasAsModel())) {
+ LOGGER.warn("generateAliasAsModel is set to false, which means array/map will be generated as model instead and the resulting code may have issues. Please enable `generateAliasAsModel` to address the issue.");
+ }
+
+ setPackageName((String) additionalProperties.getOrDefault(CodegenConstants.PACKAGE_NAME, "openapi_client"));
+
+ if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
+ setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
+ }
+
+ additionalProperties.put("apiDocPath", apiDocPath);
+ additionalProperties.put("modelDocPath", modelDocPath);
+
+ additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
+ additionalProperties.put("externCrateName", externCrateName);
+ }
+
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+
+ // Also set the extern crate name, which has any '-' replace with a '_'.
+ this.externCrateName = packageName.replace('-', '_');
+ }
+
+ @Override
+ public String apiPackage() {
+ return apiPath;
+ }
+
+ /**
+ * Configures the type of generator.
+ *
+ * @return the CodegenType for this generator
+ * @see org.openapitools.codegen.CodegenType
+ */
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.SERVER;
+ }
+
+ /**
+ * Configures a friendly name for the generator. This will be used by the generator
+ * to select the library with the -g flag.
+ *
+ * @return the friendly name for the generator
+ */
+ @Override
+ public String getName() {
+ return "rust-server-deprecated";
+ }
+
+ /**
+ * Returns human-friendly help for the generator. Provide the consumer with help
+ * tips, parameters here
+ *
+ * @return A string value for the help message
+ */
+ @Override
+ public String getHelp() {
+ return "Generates a Rust Hyper/Tower server library based on hyper 0.14. Also generates a matching Hyper client library within " +
+ "the same crate that implements the same trait.";
+ }
+
+ @Override
+ public void preprocessOpenAPI(OpenAPI openAPI) {
+
+ Info info = openAPI.getInfo();
+
+ URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides());
+ additionalProperties.put("serverHost", url.getHost());
+ additionalProperties.put("serverPort", URLPathUtils.getPort(url, serverPort));
+
+ if (packageVersion == null || packageVersion.isEmpty()) {
+ List versionComponents = new ArrayList<>(Arrays.asList(info.getVersion().split("[.]")));
+ if (versionComponents.size() < 1) {
+ versionComponents.add("1");
+ }
+ while (versionComponents.size() < 3) {
+ versionComponents.add("0");
+ }
+
+ setPackageVersion(StringUtils.join(versionComponents, "."));
+ }
+
+ additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
+ }
+
+ @Override
+ public String toApiName(String name) {
+ if (name.isEmpty()) {
+ return "default";
+ }
+ return sanitizeIdentifier(name, CasingType.SNAKE_CASE, "api", "API", true);
+ }
+
+ /**
+ * Location to write api files. You can use the apiPackage() as defined when the class is
+ * instantiated
+ */
+ @Override
+ public String apiFileFolder() {
+ return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
+ }
+
+ @Override
+ public String toOperationId(String operationId) {
+ // rust-server uses camel case instead
+ return sanitizeIdentifier(operationId, CasingType.CAMEL_CASE, "call", "method", true);
+ }
+
+ @Override
+ public String toEnumValue(String value, String datatype) {
+ // rust-server templates expect value to be in quotes
+ return "\"" + super.toEnumValue(value, datatype) + "\"";
+ }
+
+ @Override
+ public String apiDocFileFolder() {
+ return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
+ }
+
+ @Override
+ public String modelDocFileFolder() {
+ return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
+ }
+
+ @Override
+ public String toApiDocFilename(String name) {
+ return toApiName(name) + "_api";
+ }
+
+ private boolean isMimetypeXml(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith(xmlMimeType) ||
+ mimetype.toLowerCase(Locale.ROOT).startsWith(problemXmlMimeType) ||
+ mimetype.toLowerCase(Locale.ROOT).startsWith(textXmlMimeType);
+ }
+
+ private boolean isMimetypeJson(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith(jsonMimeType) ||
+ mimetype.toLowerCase(Locale.ROOT).startsWith(mergePatchJsonMimeType) ||
+ mimetype.toLowerCase(Locale.ROOT).startsWith(problemJsonMimeType);
+ }
+
+ private boolean isMimetypeWwwFormUrlEncoded(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded");
+ }
+
+ private boolean isMimetypeMultipartFormData(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith("multipart/form-data");
+ }
+
+ private boolean isMimetypeOctetStream(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith(octetMimeType);
+ }
+
+ private boolean isMimetypeMultipartRelated(String mimetype) {
+ return mimetype.toLowerCase(Locale.ROOT).startsWith("multipart/related");
+ }
+
+ private boolean isMimetypeUnknown(String mimetype) {
+ return "*/*".equals(mimetype);
+ }
+
+ /**
+ * Do we have any special handling for this mimetype?
+ */
+ boolean isMimetypePlain(String mimetype) {
+ boolean result = !(isMimetypeUnknown(mimetype) ||
+ isMimetypeXml(mimetype) ||
+ isMimetypeJson(mimetype) ||
+ isMimetypeWwwFormUrlEncoded(mimetype) ||
+ isMimetypeMultipartFormData(mimetype) ||
+ isMimetypeMultipartRelated(mimetype));
+ return result;
+ }
+
+ private String tidyUpRuntimeCallbackParam(String param) {
+ return underscore(param.replace("-", "_").replace(".", "_").replace("{", "").replace("#", "_").replace("/", "_").replace("}", "").replace("$", "").replaceAll("_+", "_"));
+ }
+
+ @Override
+ public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List servers) {
+ Map definitions = ModelUtils.getSchemas(this.openAPI);
+ CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers);
+
+ String pathFormatString = op.path;
+ for (CodegenParameter param : op.pathParams) {
+ // Replace {baseName} with {paramName} for format string
+ String paramSearch = "{" + param.baseName + "}";
+ String paramReplace = "{" + param.paramName + "}";
+
+ pathFormatString = pathFormatString.replace(paramSearch, paramReplace);
+ }
+ op.vendorExtensions.put("x-path-format-string", pathFormatString);
+
+ // The Rust code will need to contain a series of regular expressions.
+ // For performance, we'll construct these at start-of-day and re-use
+ // them. That means we need labels for them.
+ //
+ // Construct a Rust constant (uppercase) token name, and ensure it's
+ // unique using a numeric tie-breaker if required.
+ String basePathId = sanitizeName(op.path.replace("/", "_").replace("{", "").replace("}", "").replaceAll("^_", "")).toUpperCase(Locale.ROOT);
+ String pathId = basePathId;
+ int pathIdTiebreaker = 2;
+ boolean found = false;
+
+ Map> pathSetMap;
+
+ // The callback API is logically distinct from the main API, so
+ // it uses a separate path set map.
+ if (op.isCallbackRequest) {
+ pathSetMap = this.callbacksPathSetMap;
+ } else {
+ pathSetMap = this.pathSetMap;
+ }
+
+ while (pathSetMap.containsKey(pathId)) {
+ Map pathSetEntry = pathSetMap.get(pathId);
+ if (pathSetEntry.get("path").equals(op.path)) {
+ found = true;
+ break;
+ }
+ pathId = basePathId + pathIdTiebreaker;
+ pathIdTiebreaker++;
+ }
+
+ boolean hasPathParams = !op.pathParams.isEmpty();
+
+ // String for matching for path using a regex
+ // Don't prefix with '^' so that the templates can put the
+ // basePath on the front.
+ String regex = op.path;
+ // String for formatting the path for a client to make a request
+ String formatPath = op.path;
+
+ for (CodegenParameter param : op.pathParams) {
+ // Replace {baseName} with {paramName} for format string
+ String paramSearch = "{" + param.baseName + "}";
+ String paramReplace = "{" + param.paramName + "}";
+
+ formatPath = formatPath.replace(paramSearch, paramReplace);
+ }
+
+ // Handle runtime callback parameters. Runtime callback parameters
+ // are different from regular path parameters:
+ // - They begin with a "{$" sequence, which allows us to identify them.
+ // - They can contain multiple path segments, so we need to use a different
+ // regular expression.
+ // - They may contain special characters such as "#", "." and "/" which aren't
+ // valid in Rust identifiers.
+ // In the future, we may support parsing them directly
+ if (op.isCallbackRequest) {
+ formatPath = formatPath.substring(1); // Callback paths are absolute so strip initial '/'
+
+ List params = new ArrayList();
+
+ Matcher match = Pattern.compile("\\{\\$[^}{]*\\}").matcher(op.path);
+
+ while (match.find()) {
+ String param = match.group();
+
+ // Convert to a rust variable name
+ String rustParam = tidyUpRuntimeCallbackParam(param);
+ params.add(rustParam);
+
+ // Convert to a format arg
+ String formatParam = "{" + rustParam + "}";
+
+ formatPath = formatPath.replace(param, formatParam);
+
+ // Convert to a regex
+ String newParam = "(?P<" + rustParam + ">.*)";
+
+ regex = regex.replace(param, newParam);
+
+ hasPathParams = true;
+ }
+
+ op.vendorExtensions.put("x-callback-params", params);
+ }
+
+ // Save off the regular expression and path details in the relevant
+ // "pathSetMap", which we'll add to the source document that will be
+ // processed by the templates.
+ if (!found) {
+ Map pathSetEntry = new HashMap();
+ pathSetEntry.put("path", op.path);
+ pathSetEntry.put("PATH_ID", pathId);
+
+ if (hasPathParams) {
+ pathSetEntry.put("hasPathParams", "true");
+ }
+
+ // Don't prefix with '^' so that the templates can put the
+ // basePath on the front.
+ for (CodegenParameter param : op.pathParams) {
+ // Replace {baseName} with (?P[^/?#]*) for regex
+ // TODO: Sanitize baseName to avoid using '-' (see clippy::invalid_regex)
+ String paramSearch = "{" + param.baseName + "}";
+ String paramReplace = "(?P<" + param.baseName + ">[^/?#]*)";
+
+ regex = regex.replace(paramSearch, paramReplace);
+ }
+
+ pathSetEntry.put("pathRegEx", regex + "$");
+ pathSetMap.put(pathId, pathSetEntry);
+ }
+
+ String underscoredOperationId = underscore(op.operationId);
+ op.vendorExtensions.put("x-operation-id", underscoredOperationId);
+ op.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId.toUpperCase(Locale.ROOT));
+ String vendorExtensionPath = op.path.replace("{", ":").replace("}", "");
+ op.vendorExtensions.put("x-path", vendorExtensionPath);
+ op.vendorExtensions.put("x-path-id", pathId);
+ op.vendorExtensions.put("x-has-path-params", hasPathParams);
+ op.vendorExtensions.put("x-path-format-string", formatPath);
+
+ 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);
+ }
+
+ for (CodegenParameter param : op.allParams) {
+ processParam(param, op);
+ }
+
+ // We keep track of the 'default' model type for this API. If there are
+ // *any* XML responses, then we set the default to XML, otherwise we
+ // let the default be JSON. It would be odd for an API to want to use
+ // both XML and JSON on a single operation, and if we don't know
+ // anything then JSON is a more modern (ergo reasonable) choice.
+ boolean defaultsToXml = false;
+
+ // Determine the types that this operation produces. `getProducesInfo`
+ // simply lists all the types, and then we add the correct imports to
+ // the generated library.
+ List produces = new ArrayList(getProducesInfo(openAPI, operation));
+ boolean producesXml = false;
+ boolean producesPlainText = false;
+ if (produces != null && !produces.isEmpty()) {
+ List