diff --git a/.github/workflows/samples-cpp-oatpp-client.yaml b/.github/workflows/samples-cpp-oatpp-client.yaml
new file mode 100644
index 00000000000..4d133eb891e
--- /dev/null
+++ b/.github/workflows/samples-cpp-oatpp-client.yaml
@@ -0,0 +1,30 @@
+name: Samples cpp oat++ client
+
+on:
+ push:
+ branches:
+ - "samples/client/petstore/cpp-oatpp/**"
+ pull_request:
+ paths:
+ - "samples/client/petstore/cpp-oatpp/**"
+
+env:
+ GRADLE_VERSION: 6.9
+
+jobs:
+ build:
+ name: Build cpp oat++ client
+ strategy:
+ matrix:
+ sample:
+ - samples/client/petstore/cpp-oatpp
+ os:
+ - ubuntu-latest
+ - macOS-latest
+ - windows-latest
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build
+ working-directory: ${{ matrix.sample }}
+ run: cmake -B build && cmake --build build --verbose
diff --git a/.github/workflows/samples-cpp-oatpp-server.yaml b/.github/workflows/samples-cpp-oatpp-server.yaml
index 12ad210e587..37ff5c81d66 100644
--- a/.github/workflows/samples-cpp-oatpp-server.yaml
+++ b/.github/workflows/samples-cpp-oatpp-server.yaml
@@ -13,7 +13,7 @@ env:
jobs:
build:
- name: Build cpp qt client
+ name: Build cpp oat++ server
strategy:
matrix:
sample:
diff --git a/.gitignore b/.gitignore
index a2e29abe05b..5c6df879f35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,6 +92,12 @@ samples/client/petstore/cpp-restsdk/client/cmake_install.cmake
samples/client/petstore/cpp-restsdk/client/CppRestPetstoreClientConfig.cmake
samples/client/petstore/cpp-restsdk/client/CMakeCache.txt
+# cpp-oatpp
+samples/client/petstore/cpp-oatpp/build
+samples/client/petstore/cpp-oatpp/external
+samples/server/petstore/cpp-oatpp/build
+samples/server/petstore/cpp-oatpp/external
+
#Java/Android
**/.gradle
samples/client/petstore/java/hello.txt
diff --git a/README.md b/README.md
index e7af152c433..c4c0e95a042 100644
--- a/README.md
+++ b/README.md
@@ -1025,6 +1025,7 @@ Here is a list of template creators:
* Apex: @asnelling
* Bash: @bkryza
* C: @PowerOfCreation @zhemant [:heart:](https://www.patreon.com/zhemant)
+ * C++ Oat++: @Kraust
* C++ REST: @Danielku15
* C++ Tiny: @AndersSpringborg @kaareHH @michelealbano @mkakbas
* C++ UE4: @Kahncode
diff --git a/bin/configs/cpp-oatpp-client.yaml b/bin/configs/cpp-oatpp-client.yaml
new file mode 100644
index 00000000000..eb8d3804a4e
--- /dev/null
+++ b/bin/configs/cpp-oatpp-client.yaml
@@ -0,0 +1,6 @@
+generatorName: cpp-oatpp-client
+outputDir: samples/client/petstore/cpp-oatpp
+inputSpec: modules/openapi-generator/src/test/resources/3_0/cpp-oatpp-client/petstore.yaml
+templateDir: modules/openapi-generator/src/main/resources/cpp-oatpp-client
+additionalProperties:
+ addExternalLibs: "true"
diff --git a/docs/generators.md b/docs/generators.md
index 009bb56b4bc..ca295d97c5a 100644
--- a/docs/generators.md
+++ b/docs/generators.md
@@ -12,6 +12,7 @@ The following generators are available:
* [bash](generators/bash.md)
* [c](generators/c.md)
* [clojure](generators/clojure.md)
+* [cpp-oatpp-client](generators/cpp-oatpp-client.md)
* [cpp-qt-client](generators/cpp-qt-client.md)
* [cpp-restsdk](generators/cpp-restsdk.md)
* [cpp-tiny (beta)](generators/cpp-tiny.md)
diff --git a/docs/generators/cpp-oatpp-client.md b/docs/generators/cpp-oatpp-client.md
new file mode 100644
index 00000000000..343f01645ea
--- /dev/null
+++ b/docs/generators/cpp-oatpp-client.md
@@ -0,0 +1,262 @@
+---
+title: Documentation for the cpp-oatpp-client Generator
+---
+
+## METADATA
+
+| Property | Value | Notes |
+| -------- | ----- | ----- |
+| generator name | cpp-oatpp-client | pass this to the generate command after -g |
+| generator stability | STABLE | |
+| generator type | CLIENT | |
+| generator language | C++ | |
+| generator default templating engine | mustache | |
+| helpTxt | Generates a C++ API client (based on Oat++) | |
+
+## 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 |
+| ------ | ----------- | ------ | ------- |
+|addExternalLibs|Add the Possibility to fetch and compile external Libraries needed by this Framework.| |true|
+|reservedWordPrefix|Prefix to prepend to reserved words in order to avoid conflicts| |r_|
+|variableNameFirstCharacterUppercase|Make first character of variable name uppercase (eg. value -> Value)| |true|
+
+## IMPORT MAPPING
+
+| Type/Alias | Imports |
+| ---------- | ------- |
+
+
+## INSTANTIATION TYPES
+
+| Type/Alias | Instantiated By |
+| ---------- | --------------- |
+
+
+## LANGUAGE PRIMITIVES
+
+
+- oatpp::Any
+- oatpp::Boolean
+- oatpp::Fields
+- oatpp::Float64
+- oatpp::Int32
+- oatpp::Int64
+- oatpp::Object
+- oatpp::String
+- oatpp::UnorderedSet
+- oatpp::Vector
+
+
+## RESERVED WORDS
+
+
+- NULL
+- alignas
+- alignof
+- and
+- and_eq
+- asm
+- auto
+- bitand
+- bitor
+- bool
+- break
+- case
+- catch
+- char
+- char16_t
+- char32_t
+- class
+- compl
+- concept
+- const
+- const_cast
+- constexpr
+- continue
+- decltype
+- default
+- delete
+- do
+- double
+- dynamic_cast
+- else
+- enum
+- explicit
+- export
+- extern
+- false
+- float
+- for
+- friend
+- goto
+- if
+- inline
+- int
+- linux
+- long
+- mutable
+- namespace
+- new
+- noexcept
+- not
+- not_eq
+- nullptr
+- operator
+- or
+- or_eq
+- private
+- protected
+- public
+- register
+- reinterpret_cast
+- requires
+- return
+- short
+- signed
+- sizeof
+- static
+- static_assert
+- static_cast
+- struct
+- switch
+- template
+- this
+- thread_local
+- throw
+- true
+- try
+- typedef
+- typeid
+- typename
+- union
+- unsigned
+- using
+- virtual
+- void
+- volatile
+- wchar_t
+- while
+- xor
+- xor_eq
+
+
+## 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/languages/CppOatppClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java
new file mode 100644
index 00000000000..eec28ac05de
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
+ *
+ * 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.oas.models.Operation;
+import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.servers.Server;
+import org.apache.commons.lang3.StringUtils;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.meta.features.*;
+import org.openapitools.codegen.model.ModelMap;
+import org.openapitools.codegen.model.OperationMap;
+import org.openapitools.codegen.model.OperationsMap;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+import java.util.function.Predicate;
+
+import static org.openapitools.codegen.utils.StringUtils.underscore;
+
+public class CppOatppClientCodegen extends AbstractCppCodegen {
+ private final Logger LOGGER = LoggerFactory.getLogger(CppOatppClientCodegen.class);
+
+ protected boolean isAddExternalLibs = true;
+ public static final String OPTIONAL_EXTERNAL_LIB = "addExternalLibs";
+ public static final String OPTIONAL_EXTERNAL_LIB_DESC = "Add the Possibility to fetch and compile external Libraries needed by this Framework.";
+ protected final String PREFIX = "";
+
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.CLIENT;
+ }
+
+ @Override
+ public String getName() {
+ return "cpp-oatpp-client";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Generates a C++ API client (based on Oat++)";
+ }
+
+ public CppOatppClientCodegen() {
+ super();
+
+ // TODO: cpp-oatpp-client maintainer review
+ modifyFeatureSet(features -> features
+ .includeDocumentationFeatures(DocumentationFeature.Readme)
+ .securityFeatures(EnumSet.noneOf(SecurityFeature.class))
+ .excludeGlobalFeatures(
+ GlobalFeature.XMLStructureDefinitions,
+ GlobalFeature.Callbacks,
+ GlobalFeature.LinkObjects,
+ GlobalFeature.ParameterStyling,
+ GlobalFeature.MultiServer)
+ .excludeSchemaSupportFeatures(
+ SchemaSupportFeature.Polymorphism)
+ .excludeParameterFeatures(
+ ParameterFeature.Cookie));
+
+ if (StringUtils.isEmpty(modelNamePrefix)) {
+ modelNamePrefix = PREFIX;
+ }
+
+ apiPackage = "org.openapitools.client.api";
+ modelPackage = "org.openapitools.client.model";
+
+ apiTemplateFiles.put("api-header.mustache", ".hpp");
+
+ modelTemplateFiles.put("model-header.mustache", ".hpp");
+
+ embeddedTemplateDir = templateDir = "cpp-oatpp-client";
+
+ cliOptions.clear();
+ addSwitch(OPTIONAL_EXTERNAL_LIB, OPTIONAL_EXTERNAL_LIB_DESC, this.isAddExternalLibs);
+ addOption(RESERVED_WORD_PREFIX_OPTION, RESERVED_WORD_PREFIX_DESC, this.reservedWordPrefix);
+ addOption(VARIABLE_NAME_FIRST_CHARACTER_UPPERCASE_OPTION,
+ VARIABLE_NAME_FIRST_CHARACTER_UPPERCASE_DESC,
+ Boolean.toString(this.variableNameFirstCharacterUppercase));
+
+ setupSupportingFiles();
+
+ languageSpecificPrimitives = new HashSet<>(
+ Arrays.asList(
+ "oatpp::String",
+ "oatpp::Boolean",
+ "oatpp::Int32",
+ "oatpp::Int64",
+ "oatpp::Vector",
+ "oatpp::Fields",
+ "oatpp::UnorderedSet",
+ "oatpp::Object",
+ "oatpp::Float64",
+ "oatpp::Any"
+ ));
+
+ typeMapping = new HashMap<>();
+ typeMapping.put("date", "oatpp::String");
+ typeMapping.put("DateTime", "oatpp::String");
+ typeMapping.put("string", "oatpp::String");
+ typeMapping.put("integer", "oatpp::Int32");
+ typeMapping.put("long", "oatpp::Int64");
+ typeMapping.put("boolean", "oatpp::Boolean");
+ typeMapping.put("array", "oatpp::Vector");
+ typeMapping.put("map", "oatpp::Fields");
+ typeMapping.put("set", "oatpp::UnorderedSet");
+ typeMapping.put("file", "oatpp::String");
+ typeMapping.put("object", "oatpp::Object");
+ typeMapping.put("binary", "oatpp::String");
+ typeMapping.put("number", "oatpp::Float64");
+ typeMapping.put("UUID", "oatpp::String");
+ typeMapping.put("URI", "oatpp::String");
+ typeMapping.put("ByteArray", "oatpp::String");
+ typeMapping.put("AnyType", "oatpp::Any");
+
+ super.importMapping = new HashMap<>();
+ }
+
+ private void setupSupportingFiles() {
+ supportingFiles.clear();
+ supportingFiles
+ .add(new SupportingFile("main-api-client.mustache", "", modelNamePrefix + "main-api-client.cpp"));
+ supportingFiles.add(new SupportingFile("cmake.mustache", "", "CMakeLists.txt"));
+ supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
+ }
+
+ @Override
+ public void processOpts() {
+ super.processOpts();
+ if (additionalProperties.containsKey("modelNamePrefix")) {
+ additionalProperties().put("prefix", modelNamePrefix);
+ setupSupportingFiles();
+ }
+ if (additionalProperties.containsKey(RESERVED_WORD_PREFIX_OPTION)) {
+ reservedWordPrefix = (String) additionalProperties.get(RESERVED_WORD_PREFIX_OPTION);
+ }
+
+ additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\."));
+ additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::"));
+ additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\."));
+ additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::"));
+ additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix);
+
+ if (additionalProperties.containsKey(OPTIONAL_EXTERNAL_LIB)) {
+ setAddExternalLibs(convertPropertyToBooleanAndWriteBack(OPTIONAL_EXTERNAL_LIB));
+ } else {
+ additionalProperties.put(OPTIONAL_EXTERNAL_LIB, isAddExternalLibs);
+ }
+ }
+
+ @Override
+ public String toModelImport(String name) {
+ if (importMapping.containsKey(name)) {
+ return importMapping.get(name);
+ } else {
+ return "#include \"" + name + ".hpp\"";
+ }
+ }
+
+ @Override
+ public CodegenModel fromModel(String name, Schema model) {
+ CodegenModel codegenModel = super.fromModel(name, model);
+
+ Set oldImports = codegenModel.imports;
+ codegenModel.imports = new HashSet<>();
+ for (String imp : oldImports) {
+ String newImp = toModelImport(imp);
+ if (!newImp.isEmpty()) {
+ codegenModel.imports.add(newImp);
+ }
+ }
+
+ if (!codegenModel.isEnum
+ && codegenModel.anyOf.size() > 1
+ && codegenModel.anyOf.contains("std::string")
+ && !codegenModel.anyOf.contains("AnyType")
+ && codegenModel.interfaces.size() == 1) {
+ codegenModel.vendorExtensions.put("x-is-string-enum-container", true);
+ }
+ return codegenModel;
+ }
+
+ @Override
+ public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List servers) {
+ CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers);
+
+ if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
+ ApiResponse apiResponse = findMethodResponse(operation.getResponses());
+
+ if (apiResponse != null) {
+ Schema response = ModelUtils.getSchemaFromResponse(openAPI, apiResponse);
+ if (response != null) {
+ CodegenProperty cm = fromProperty("response", response, false);
+ op.vendorExtensions.put("x-codegen-response", cm);
+ if ("HttpContent".equals(cm.dataType)) {
+ op.vendorExtensions.put("x-codegen-response-ishttpcontent", true);
+ }
+ }
+ }
+ }
+
+ String pathForOatpp = path.replaceAll("\\{(.*?)}", "{$1}");
+ op.vendorExtensions.put("x-codegen-oatpp-path", pathForOatpp);
+
+ return op;
+ }
+
+ @Override
+ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
+ OperationMap operations = objs.getOperations();
+ String classname = operations.getClassname();
+ operations.put("classnameSnakeUpperCase", underscore(classname).toUpperCase(Locale.ROOT));
+ operations.put("classnameSnakeLowerCase", underscore(classname).toLowerCase(Locale.ROOT));
+ List operationList = operations.getOperation();
+ for (CodegenOperation op : operationList) {
+ postProcessSingleOperation(operations, op);
+ }
+
+ return objs;
+ }
+
+ private void postProcessSingleOperation(OperationMap operations, CodegenOperation op) {
+ if (op.vendorExtensions == null) {
+ op.vendorExtensions = new HashMap<>();
+ }
+
+ if (op.bodyParam != null) {
+ if (op.bodyParam.vendorExtensions == null) {
+ op.bodyParam.vendorExtensions = new HashMap<>();
+ }
+
+ boolean isStringOrDate = op.bodyParam.isString || op.bodyParam.isDate;
+ op.bodyParam.vendorExtensions.put("x-codegen-oatpp-is-string-or-date", isStringOrDate);
+ }
+
+ boolean consumeJson = false;
+ if (op.consumes != null) {
+ Predicate