Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.XML;
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.servers.Server;
import joptsimple.internal.Strings;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.features.*;
import org.openapitools.codegen.model.ApiInfoMap;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
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.io.IOException;
import java.math.BigInteger;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public class RustServerCodegen extends AbstractRustCodegen implements CodegenConfig {
private final Logger LOGGER = LoggerFactory.getLogger(RustServerCodegen.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;
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 RustServerCodegen() {
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
)
);
// 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("server-mod.mustache", "src/server", "mod.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-client-main.mustache", "examples/client", "main.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("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).");
}
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('-', '_');
}
public void setPackageVersion(String packageVersion) {
this.packageVersion = packageVersion;
}
@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";
}
/**
* 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. 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);
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