diff --git a/docs/generators/crystal.md b/docs/generators/crystal.md
index ba481b67e4da..ea2b3cc400db 100644
--- a/docs/generators/crystal.md
+++ b/docs/generators/crystal.md
@@ -94,7 +94,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
if
in
include
-instance
+instance_sizeof
is_a?
lib
macro
@@ -102,9 +102,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl
next
nil
nil?
+object_id
of
out
pointerof
+previous_def
private
protected
require
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java
index f8bddd40330d..29bb9e09ab95 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java
@@ -29,8 +29,7 @@
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.PrefixWithHashLambda;
-import org.openapitools.codegen.templating.mustache.UppercaseLambda;
-import org.openapitools.codegen.templating.mustache.TitlecaseLambda;
+import org.openapitools.codegen.templating.mustache.PascalCaseLambda;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -85,28 +84,32 @@ public CrystalClientCodegen() {
SecurityFeature.BasicAuth,
SecurityFeature.BearerToken,
SecurityFeature.ApiKey,
- SecurityFeature.OAuth2_Implicit))
+ SecurityFeature.OAuth2_Implicit
+ ))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling,
GlobalFeature.ParameterizedServer,
- GlobalFeature.MultiServer)
+ GlobalFeature.MultiServer
+ )
.includeSchemaSupportFeatures(
- SchemaSupportFeature.Polymorphism)
+ SchemaSupportFeature.Polymorphism
+ )
.excludeParameterFeatures(
- ParameterFeature.Cookie)
+ ParameterFeature.Cookie
+ )
.includeClientModificationFeatures(
ClientModificationFeature.BasePath,
- ClientModificationFeature.UserAgent));
-
- generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
- .stability(Stability.BETA)
- .build();
+ ClientModificationFeature.UserAgent
+ )
+ );
supportsInheritance = true;
+ generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata).stability(Stability.BETA).build();
+
// clear import mapping (from default generator) as crystal does not use it
// at the moment
importMapping.clear();
@@ -130,19 +133,24 @@ public CrystalClientCodegen() {
hideGenerationTimestamp = Boolean.TRUE;
// reserved word. Ref:
- // https://github.com/crystal-lang/crystal/wiki/Crystal-for-Rubyists#available-keywords
+ // https://crystal-lang.org/reference/1.18/crystal_for_rubyists/index.html#available-keywords
+ // https://crystal-lang.org/api/1.18.2/Reference.html
reservedWords = new HashSet<>(
Arrays.asList(
- "abstract", "annotation", "do", "if", "nil?", "select", "union",
- "alias", "else", "in", "of", "self", "unless",
- "as", "elsif", "include", "out", "sizeof", "until",
- "as?", "end", "instance", "sizeof", "pointerof", "struct", "verbatim",
- "asm", "ensure", "is_a?", "private", "super", "when",
- "begin", "enum", "lib", "protected", "then", "while",
- "break", "extend", "macro", "require", "true", "with",
- "case", "false", "module", "rescue", "type", "yield",
- "class", "for", "next", "responds_to?", "typeof",
- "def", "fun", "nil", "return", "uninitialized"));
+ // language reserved words (keywords)
+ "abstract", "do", "if", "nil?", "return", "uninitialized",
+ "alias", "else", "in", "of", "select", "union",
+ "as", "elsif", "include", "out", "self", "unless",
+ "as?", "end", "instance_sizeof", "pointerof", "sizeof", "until",
+ "asm", "ensure", "is_a?", "previous_def", "struct", "verbatim",
+ "begin", "enum", "lib", "private", "super", "when",
+ "break", "extend", "macro", "protected", "then", "while",
+ "case", "false", "module", "require", "true", "with",
+ "class", "for", "next", "rescue", "type", "yield",
+ "def", "fun", "nil", "responds_to?", "typeof",
+ // additional reserved words (methods)
+ "annotation", "object_id"
+ ));
languageSpecificPrimitives.clear();
languageSpecificPrimitives.add("String");
@@ -174,8 +182,8 @@ public CrystalClientCodegen() {
typeMapping.put("List", "Array");
typeMapping.put("set", "Set");
typeMapping.put("map", "Hash");
- typeMapping.put("object", "Object");
- typeMapping.put("AnyType", "Object");
+ typeMapping.put("object", "JSON::Any");
+ typeMapping.put("AnyType", "JSON::Any");
typeMapping.put("file", "::File");
typeMapping.put("binary", "String");
typeMapping.put("ByteArray", "String");
@@ -188,31 +196,18 @@ public CrystalClientCodegen() {
primitiveTypes = new ArrayList(typeMapping.values());
// remove modelPackage and apiPackage added by default
- cliOptions.removeIf(opt -> CodegenConstants.MODEL_PACKAGE.equals(opt.getOpt()) ||
- CodegenConstants.API_PACKAGE.equals(opt.getOpt()));
+ cliOptions.removeIf(opt -> CodegenConstants.MODEL_PACKAGE.equals(opt.getOpt()) || CodegenConstants.API_PACKAGE.equals(opt.getOpt()));
cliOptions.add(new CliOption(SHARD_NAME, "shard name (e.g. twitter_client").defaultValue("openapi_client"));
-
cliOptions.add(new CliOption(MODULE_NAME, "module name (e.g. TwitterClient").defaultValue("OpenAPIClient"));
-
cliOptions.add(new CliOption(SHARD_VERSION, "shard version.").defaultValue("1.0.0"));
-
cliOptions.add(new CliOption(SHARD_LICENSE, "shard license.").defaultValue("unlicense"));
-
cliOptions.add(new CliOption(SHARD_HOMEPAGE, "shard homepage.").defaultValue("http://org.openapitools"));
-
cliOptions.add(new CliOption(SHARD_DESCRIPTION, "shard description.").defaultValue("This shard maps to a REST API"));
-
cliOptions.add(new CliOption(SHARD_AUTHOR, "shard author (only one is supported)."));
-
cliOptions.add(new CliOption(SHARD_AUTHOR_EMAIL, "shard author email (only one is supported)."));
-
- cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
- CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC).defaultValue(Boolean.TRUE.toString()));
-
- cliOptions.add(new CliOption(PARAMS_ENCODER,
- "params_encoder setting (e.g. Crest::NestedParamsEncoder, Crest::EnumeratedFlatParamsEncoder, Crest::ZeroEnumeratedFlatParamsEncoder").
- defaultValue("Crest::NestedParamsEncoder"));
+ cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC).defaultValue(Boolean.TRUE.toString()));
+ cliOptions.add(new CliOption(PARAMS_ENCODER, "params_encoder setting (e.g. Crest::NestedParamsEncoder, Crest::EnumeratedFlatParamsEncoder, Crest::ZeroEnumeratedFlatParamsEncoder").defaultValue("Crest::NestedParamsEncoder"));
}
@Override
@@ -220,51 +215,63 @@ public void processOpts() {
super.processOpts();
if (StringUtils.isEmpty(System.getenv("CRYSTAL_POST_PROCESS_FILE"))) {
- LOGGER.info(
- "Hint: Environment variable 'CRYSTAL_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export CRYSTAL_POST_PROCESS_FILE=\"/usr/local/bin/crystal tool format\"' (Linux/Mac)");
+ LOGGER.info("Hint: Environment variable 'CRYSTAL_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export CRYSTAL_POST_PROCESS_FILE=\"/usr/local/bin/crystal tool format\"' (Linux/Mac)");
} else if (!this.isEnablePostProcessFile()) {
LOGGER.info("Warning: Environment variable 'CRYSTAL_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 (additionalProperties.containsKey(SHARD_NAME)) {
setShardName((String) additionalProperties.get(SHARD_NAME));
+ } else {
+ additionalProperties.put(SHARD_NAME, shardName);
}
- additionalProperties.put(SHARD_NAME, shardName);
if (additionalProperties.containsKey(MODULE_NAME)) {
setModuleName((String) additionalProperties.get(MODULE_NAME));
+ } else {
+ additionalProperties.put(MODULE_NAME, moduleName);
}
- additionalProperties.put(MODULE_NAME, moduleName);
if (additionalProperties.containsKey(SHARD_VERSION)) {
setShardVersion((String) additionalProperties.get(SHARD_VERSION));
} else {
- // not set, pass the default value to template
additionalProperties.put(SHARD_VERSION, shardVersion);
}
if (additionalProperties.containsKey(SHARD_LICENSE)) {
setShardLicense((String) additionalProperties.get(SHARD_LICENSE));
+ } else {
+ additionalProperties.put(SHARD_LICENSE, shardLicense);
}
if (additionalProperties.containsKey(SHARD_HOMEPAGE)) {
setShardHomepage((String) additionalProperties.get(SHARD_HOMEPAGE));
+ } else {
+ additionalProperties.put(SHARD_HOMEPAGE, shardHomepage);
}
if (additionalProperties.containsKey(SHARD_SUMMARY)) {
setShardSummary((String) additionalProperties.get(SHARD_SUMMARY));
+ } else {
+ additionalProperties.put(SHARD_SUMMARY, shardSummary);
}
if (additionalProperties.containsKey(SHARD_DESCRIPTION)) {
setShardDescription((String) additionalProperties.get(SHARD_DESCRIPTION));
+ } else {
+ additionalProperties.put(SHARD_DESCRIPTION, shardDescription);
}
if (additionalProperties.containsKey(SHARD_AUTHOR)) {
setShardAuthor((String) additionalProperties.get(SHARD_AUTHOR));
+ } else {
+ additionalProperties.put(SHARD_AUTHOR, shardAuthor);
}
if (additionalProperties.containsKey(SHARD_AUTHOR_EMAIL)) {
setShardAuthorEmail((String) additionalProperties.get(SHARD_AUTHOR_EMAIL));
+ } else {
+ additionalProperties.put(SHARD_AUTHOR_EMAIL, shardAuthorEmail);
}
if (additionalProperties.containsKey(PARAMS_ENCODER)) {
@@ -290,18 +297,14 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
- supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
supportingFiles.add(new SupportingFile("shard.mustache", "", "shard.yml"));
// crystal spec files
- supportingFiles.add(new SupportingFile("spec_helper.mustache", specFolder, "spec_helper.cr")
- .doNotOverwrite());
+ supportingFiles.add(new SupportingFile("spec_helper.mustache", specFolder, "spec_helper.cr").doNotOverwrite());
// add lambda for mustache templates
additionalProperties.put("lambdaPrefixWithHash", new PrefixWithHashLambda());
- additionalProperties.put("lambdaUppercase", new UppercaseLambda());
- additionalProperties.put("lambdaTitlecase", new TitlecaseLambda());
-
+ additionalProperties.put("lambdaPascalcase", new PascalCaseLambda());
}
@Override
@@ -321,14 +324,12 @@ public String getName() {
@Override
public String apiFileFolder() {
- return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator
- + apiPackage.replace("/", File.separator);
+ return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + apiPackage.replace("/", File.separator);
}
@Override
public String modelFileFolder() {
- return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator
- + modelPackage.replace("/", File.separator);
+ return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + modelPackage.replace("/", File.separator);
}
@Override
@@ -407,9 +408,9 @@ public String toModelName(final String name) {
// model name starts with number
if (modelName.matches("^\\d.*")) {
- LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName,
- camelize("model_" + modelName));
- modelName = "model_" + modelName; // e.g. 200Response => Model200Response (after camelize)
+ LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName, camelize("model_" + modelName));
+ // e.g. 200Response => Model200Response (after camelize)
+ modelName = "model_" + modelName;
}
// camelize the model name
@@ -548,8 +549,7 @@ public String toOperationId(String operationId) {
// operationId starts with a number
if (operationId.matches("^\\d.*")) {
- LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId,
- underscore(sanitizeName("call_" + operationId)));
+ LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId)));
operationId = "call_" + operationId;
}
@@ -576,6 +576,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List modelMaps = ModelMap.toCodegenModelMap(allModels);
HashMap processedModelMaps = new HashMap<>();
@@ -607,8 +608,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List modelMaps,
- HashMap processedModelMap) {
+ private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps, HashMap processedModelMap) {
if (codegenParameter.isArray) { // array
if (codegenParameter.items == null) {
return "[]";
@@ -675,8 +675,7 @@ private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps,
- HashMap processedModelMap) {
+ private String constructExampleCode(CodegenProperty codegenProperty, HashMap modelMaps, HashMap processedModelMap) {
if (codegenProperty.isArray) { // array
return "[" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "]";
} else if (codegenProperty.isMap) {
@@ -743,8 +742,7 @@ private String constructExampleCode(CodegenProperty codegenProperty, HashMap modelMaps,
- HashMap processedModelMap) {
+ private String constructExampleCode(CodegenModel codegenModel, HashMap modelMaps, HashMap processedModelMap) {
// break infinite recursion. Return, in case a model is already processed in the
// current context.
String model = codegenModel.name;
@@ -758,15 +756,24 @@ private String constructExampleCode(CodegenModel codegenModel, HashMap> enumVars = (List