diff --git a/.github/workflows/samples-cpp-httplib-server.yaml b/.github/workflows/samples-cpp-httplib-server.yaml
new file mode 100644
index 000000000000..ec407c343401
--- /dev/null
+++ b/.github/workflows/samples-cpp-httplib-server.yaml
@@ -0,0 +1,59 @@
+name: Samples cpp httplib server
+
+on:
+ push:
+ paths:
+ - "samples/server/petstore/cpp-httplib-server/**"
+ - ".github/workflows/samples-cpp-httplib-server.yaml"
+ pull_request:
+ paths:
+ - "samples/server/petstore/cpp-httplib-server/**"
+ - ".github/workflows/samples-cpp-httplib-server.yaml"
+
+env:
+ GRADLE_VERSION: 6.9
+
+jobs:
+ build:
+ name: Build cpp httplib server
+ strategy:
+ matrix:
+ sample:
+ - samples/server/petstore/cpp-httplib-server/petstore
+ - samples/server/petstore/cpp-httplib-server/feature-test
+ os:
+ - ubuntu-latest
+ - macOS-latest
+ - windows-latest
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies (Linux)
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y build-essential libssl-dev zlib1g-dev cmake
+
+ - name: Install dependencies (macOS)
+ if: matrix.os == 'macOS-latest'
+ run: |
+ brew install openssl zlib cmake
+
+ - name: Install dependencies (Windows)
+ if: matrix.os == 'windows-latest'
+ run: |
+ vcpkg install openssl:x64-windows zlib:x64-windows
+ shell: cmd
+ timeout-minutes: 20
+
+ - name: Build
+ working-directory: ${{ matrix.sample }}
+ run: |
+ if [ "${{ matrix.os }}" = "windows-latest" ]; then
+ cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="C:/vcpkg/scripts/buildsystems/vcpkg.cmake"
+ else
+ cmake -S . -B build
+ fi
+ cmake --build build --verbose
+ shell: bash
\ No newline at end of file
diff --git a/README.md b/README.md
index bcb1f9a157ca..23392062100b 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se
| | Languages/Frameworks |
| -------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.1, .NET Core 3.1, .NET 5.0. Libraries: RestSharp, GenericHost, HttpClient), **C++** (Arduino, cpp-restsdk, Qt5, Tizen, Unreal Engine 4), **Clojure**, **Crystal**, **Dart**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Apache HttpClient 4.x, Apache HttpClient 5.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, Spring 6 RestClient, MicroProfile Rest Client, Helidon), **Jetbrains HTTP Client**, **Julia**, **k6**, **Kotlin**, **Lua**, **N4JS**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types, Apollo GraphQL DataStore), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (hyper, reqwest, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient, pekko), **Swift** (2.x, 3.x, 4.x, 5.x, 6.x), **Typescript** (AngularJS, Angular (9.x - 19.x), Aurelia, Axios, Fetch, Inversify, jQuery, Nestjs, Node, redux-query, Rxjs), **XoJo**, **Zapier** |
-| **Server stubs** | **Ada**, **C#** (ASP.NET Core, Azure Functions), **C++** (Oat++, Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin, Echo), **Haskell** (Servant, Yesod), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/), [Apache Camel](https://camel.apache.org/), [Helidon](https://helidon.io/)), **Julia**, **Kotlin** (Spring Boot, [Ktor](https://github.com/ktorio/ktor), [Vert.x](https://vertx.io/)), **PHP** ([Flight](https://docs.flightphp.com/), Laravel, Lumen, [Mezzio (fka Zend Expressive)](https://github.com/mezzio/mezzio), Slim, Silex, [Symfony](https://symfony.com/)), **Python** (FastAPI, Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** ([rust-server](https://openapi-generator.tech/docs/generators/rust-server/)), **Scala** (Akka, [Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), [Cask](https://github.com/com-lihaoyi/cask), Scalatra) |
+| **Server stubs** | **Ada**, **C#** (ASP.NET Core, Azure Functions), **C++** (Httplib, Oat++, Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin, Echo), **Haskell** (Servant, Yesod), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/), [Apache Camel](https://camel.apache.org/), [Helidon](https://helidon.io/)), **Julia**, **Kotlin** (Spring Boot, [Ktor](https://github.com/ktorio/ktor), [Vert.x](https://vertx.io/)), **PHP** ([Flight](https://docs.flightphp.com/), Laravel, Lumen, [Mezzio (fka Zend Expressive)](https://github.com/mezzio/mezzio), Slim, Silex, [Symfony](https://symfony.com/)), **Python** (FastAPI, Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** ([rust-server](https://openapi-generator.tech/docs/generators/rust-server/)), **Scala** (Akka, [Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), [Cask](https://github.com/com-lihaoyi/cask), Scalatra) |
| **API documentation generators** | **HTML**, **Confluence Wiki**, **Asciidoc**, **Markdown**, **PlantUML** |
| **Configuration files** | [**Apache2**](https://httpd.apache.org/) |
| **Others** | **GraphQL**, **JMeter**, **Ktorm**, **MySQL Schema**, **Postman Collection**, **Protocol Buffer**, **WSDL** |
@@ -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++ Httplib: @rajvesh
* C++ Oat++: @Kraust
* C++ REST: @Danielku15
* C++ Tiny: @AndersSpringborg @kaareHH @michelealbano @mkakbas
diff --git a/bin/configs/cpp-httplib-server-feature-test.yaml b/bin/configs/cpp-httplib-server-feature-test.yaml
new file mode 100644
index 000000000000..9f5f3e3fec14
--- /dev/null
+++ b/bin/configs/cpp-httplib-server-feature-test.yaml
@@ -0,0 +1,8 @@
+generatorName: cpp-httplib-server
+outputDir: samples/server/petstore/cpp-httplib-server/feature-test
+inputSpec: modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/feature-test.json
+templateDir: modules/openapi-generator/src/main/resources/cpp-httplib-server
+additionalProperties:
+ apiNamespace: "api"
+ modelNamespace: "models"
+ projectName: "cpp-httplib-server-feature-test"
diff --git a/bin/configs/cpp-httplib-server-petstore.yaml b/bin/configs/cpp-httplib-server-petstore.yaml
new file mode 100644
index 000000000000..c7f7b9168975
--- /dev/null
+++ b/bin/configs/cpp-httplib-server-petstore.yaml
@@ -0,0 +1,8 @@
+generatorName: cpp-httplib-server
+outputDir: samples/server/petstore/cpp-httplib-server/petstore
+inputSpec: modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json
+templateDir: modules/openapi-generator/src/main/resources/cpp-httplib-server
+additionalProperties:
+ apiNamespace: "api"
+ modelNamespace: "models"
+ projectName: "cpp-httplib-server-petstore"
diff --git a/docs/generators.md b/docs/generators.md
index bfa97dd22e54..97cc078f8ccc 100644
--- a/docs/generators.md
+++ b/docs/generators.md
@@ -89,6 +89,7 @@ The following generators are available:
* [ada-server](generators/ada-server.md)
* [aspnet-fastendpoints](generators/aspnet-fastendpoints.md)
* [aspnetcore](generators/aspnetcore.md)
+* [cpp-httplib-server](generators/cpp-httplib-server.md)
* [cpp-oatpp-server](generators/cpp-oatpp-server.md)
* [cpp-pistache-server](generators/cpp-pistache-server.md)
* [cpp-qt-qhttpengine-server](generators/cpp-qt-qhttpengine-server.md)
diff --git a/docs/generators/README.md b/docs/generators/README.md
index d61f00351d5a..8ae6bd7c255d 100644
--- a/docs/generators/README.md
+++ b/docs/generators/README.md
@@ -65,6 +65,7 @@ The following generators are available:
## SERVER generators
* [ada-server](ada-server.md)
* [aspnetcore](aspnetcore.md)
+* [cpp-httplib-server](cpp-httplib-server.md)
* [cpp-oatpp-server](cpp-oatpp-server.md)
* [cpp-pistache-server](cpp-pistache-server.md)
* [cpp-qt5-qhttpengine-server](cpp-qt5-qhttpengine-server.md)
diff --git a/docs/generators/cpp-httplib-server.md b/docs/generators/cpp-httplib-server.md
new file mode 100644
index 000000000000..24f4f1ba6e58
--- /dev/null
+++ b/docs/generators/cpp-httplib-server.md
@@ -0,0 +1,286 @@
+---
+title: Documentation for the cpp-httplib-server Generator
+---
+
+## METADATA
+
+| Property | Value | Notes |
+| -------- | ----- | ----- |
+| generator name | cpp-httplib-server | pass this to the generate command after -g |
+| generator stability | STABLE | |
+| generator type | SERVER | |
+| generator language | C++ | |
+| generator default templating engine | mustache | |
+| helpTxt | Generates a C++ server using the httplib library. | |
+
+## 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 |
+| ------ | ----------- | ------ | ------- |
+|addApiImplStubs|Generate API implementation stubs and a sample main.cpp for quick start| |false|
+|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
+|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false** The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications. **true** Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default. |true|
+|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
+|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|**false** No changes to the enum's are made, this is the default option. **true** With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case. |false|
+|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|**true** The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document. **false** The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing. |true|
+|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
+|reservedWordPrefix|Prefix to prepend to reserved words in order to avoid conflicts| |r_|
+|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
+|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
+|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
+
+
+bool
+char
+double
+float
+int
+long
+size_t
+std::any
+std::deque
+std::list
+std::map
+std::optional
+std::pair
+std::queue
+std::set
+std::stack
+std::string
+std::tuple
+std::unordered_map
+std::unordered_set
+std::variant
+std::vector
+unsigned char
+unsigned int
+unsigned long
+void
+
+
+## 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/CppHttplibServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java
new file mode 100644
index 000000000000..c621a9c8329f
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java
@@ -0,0 +1,2575 @@
+/*
+ * Copyright 2026 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 java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.openapitools.codegen.CodegenModel;
+import org.openapitools.codegen.CodegenType;
+import org.openapitools.codegen.CodegenOperation;
+import org.openapitools.codegen.CodegenParameter;
+import org.openapitools.codegen.CodegenProperty;
+import org.openapitools.codegen.CodegenResponse;
+import org.openapitools.codegen.SupportingFile;
+import org.openapitools.codegen.meta.features.DocumentationFeature;
+import org.openapitools.codegen.meta.features.GlobalFeature;
+import org.openapitools.codegen.meta.features.SecurityFeature;
+import org.openapitools.codegen.meta.features.WireFormatFeature;
+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.CamelizeOption;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.openapitools.codegen.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.media.MediaType;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+
+/**
+ * C++ HTTP Library Server Code Generator.
+ * This code generator creates C++ server stubs using the httplib library for handling HTTP requests.
+ * It generates:
+ * - Model classes with proper C++ typing (std::string, std::vector, std::optional, etc.)
+ * - API handler interfaces with type-safe request/response handling
+ * - CMake build configuration
+ * - JSON serialization/deserialization using nlohmann::json
+ * Key features:
+ * - Supports nullable types using std::optional
+ * - Proper namespace organization for models and APIs
+ * - Status code to error type mapping for responses
+ * - Standard C++ container types (vector, map, etc.)
+ * - Automatic include generation for dependencies
+ *
+ * @author OpenAPI Generator Contributors
+ */
+public class CppHttplibServerCodegen extends AbstractCppCodegen {
+ private final Logger LOGGER = LoggerFactory.getLogger(CppHttplibServerCodegen.class);
+
+ @Override
+ public void preprocessOpenAPI(OpenAPI openAPI) {
+ super.preprocessOpenAPI(openAPI);
+
+ // Check if any security is defined in the spec
+ boolean hasAnySecurity = (openAPI.getComponents() != null
+ && openAPI.getComponents().getSecuritySchemes() != null
+ && !openAPI.getComponents().getSecuritySchemes().isEmpty());
+ additionalProperties.put("hasAuthMethods", hasAnySecurity);
+
+ // Process all paths to enhance inline schemas with meaningful titles
+ if (openAPI.getPaths() != null) {
+ for (Map.Entry pathEntry : openAPI.getPaths().entrySet()) {
+ PathItem pathItem = pathEntry.getValue();
+ if (pathItem.readOperations() != null) {
+ for (Operation operation : pathItem.readOperations()) {
+ String operationId = operation.getOperationId() != null ? operation.getOperationId() : pathEntry.getKey();
+
+ // Process request body schema
+ if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) {
+ for (MediaType mediaType : operation.getRequestBody().getContent().values()) {
+ if (mediaType.getSchema() != null && mediaType.getSchema().getTitle() == null) {
+ mediaType.getSchema().setTitle(toPascalCase(operationId) + "Request");
+ processNestedSchemas(mediaType.getSchema(), toPascalCase(operationId) + "Request");
+ }
+ }
+ }
+
+ // Process response schemas
+ if (operation.getResponses() != null) {
+ for (Map.Entry respEntry : operation.getResponses().entrySet()) {
+ ApiResponse response = respEntry.getValue();
+ if (response.getContent() != null) {
+ for (MediaType mediaType : response.getContent().values()) {
+ if (mediaType.getSchema() != null && mediaType.getSchema().getTitle() == null) {
+ // Format: {OperationId}{StatusCode}Response (e.g., TestHeaderParameters200Response)
+ String responseTitle = toPascalCase(operationId) + respEntry.getKey() + "Response";
+ mediaType.getSchema().setTitle(responseTitle);
+ processNestedSchemas(mediaType.getSchema(), responseTitle);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public static final String PROJECT_NAME = "projectName";
+ private static final String DEFAULT_PROJECT_NAME = "cpp-httplib-server";
+
+ public static final String MODEL_NAMESPACE = "modelNamespace";
+ public static final String API_NAMESPACE = "apiNamespace";
+ public static final String ENUM_NAMESPACE = "enumNamespace";
+ public static final String MODEL_SUFFIX = "models";
+ public static final String API_SUFFIX = "api";
+ public static final String ENUM_SUFFIX = "enum";
+ public static final String INCLUDE_VARIANT = "#include ";
+ public static final String INCLUDE_OPTIONAL = "#include ";
+ public static final String HTTP_RESPONSE_PREFIX = "HTTP_RESPONSE_CODE_";
+ public static final String ADD_API_IMPL_STUBS = "addApiImplStubs";
+ public static final String ENUM_FROM_STRING = "EnumFromString";
+ public static final String ENUM_TO_STRING = "EnumToString";
+ public static final String PRIMITIVE_FROM_STRING = "PrimitiveFromString";
+ public static final String PRIMITIVE_TO_STRING = "PrimitiveToString";
+
+ // Standard includes for mapped C++ types
+ private static final Map standardIncludes = Map.ofEntries(
+ Map.entry("std::string", "#include "),
+ Map.entry("std::vector", "#include "),
+ Map.entry("std::map", "#include "),
+ Map.entry("std::set", "#include "),
+ Map.entry("std::list", "#include "),
+ Map.entry("std::unordered_map", "#include "),
+ Map.entry("std::unordered_set", "#include "),
+ Map.entry("std::deque", "#include "),
+ Map.entry("std::queue", "#include "),
+ Map.entry("std::stack", "#include "),
+ Map.entry("std::pair", "#include "),
+ Map.entry("std::tuple", "#include "),
+ Map.entry("std::optional", "#include "),
+ Map.entry("std::any", "#include "),
+ Map.entry("std::variant", "#include ")
+ );
+
+ // Numeric types for include
+ private static final Set cstdintTypes = Set.of(
+ "byte", "int", "integer", "long", "short", "unsigned int", "unsigned long",
+ "unsigned short", "size_t", "ssize_t", "ptrdiff_t", "double", "float",
+ "boolean", "bool", "char", "wchar_t", "char16_t", "char32_t"
+ );
+
+ /**
+ * Constructor for CppHttplibServerCodegen.
+ * Initializes the code generator with C++ httplib server specific configurations,
+ * type mappings, and template files.
+ */
+ public CppHttplibServerCodegen() {
+ super();
+ embeddedTemplateDir = templateDir = "cpp-httplib-server";
+
+ // Enable inline schema options to prevent schema reuse
+ // This ensures each operation gets its own uniquely named response model
+ inlineSchemaOption.put("SKIP_SCHEMA_REUSE", "true");
+
+ // Map OpenAPI types/formats to C++ types (always use std:: prefix)
+ typeMapping.put("integer", "int");
+ typeMapping.put("long", "long");
+ typeMapping.put("float", "float");
+ typeMapping.put("double", "double");
+ // Default: OpenAPI 'number' → 'double', unless format is 'float'
+ typeMapping.put("number", "double");
+ typeMapping.put("boolean", "bool");
+ typeMapping.put("string", "std::string");
+ typeMapping.put("byte", "unsigned char");
+ typeMapping.put("ByteArray", "std::vector");
+ typeMapping.put("binary", "std::string");
+ typeMapping.put("date", "std::string");
+ typeMapping.put("date-time", "std::string");
+ typeMapping.put("password", "std::string");
+ typeMapping.put("object", "nlohmann::json");
+ typeMapping.put("array", "std::vector");
+ typeMapping.put("file", "std::string");
+ typeMapping.put("oas_any_type_not_mapped", "nlohmann::json");
+
+ // Only mapped C++ types as primitives
+ languageSpecificPrimitives.addAll(Arrays.asList(
+ "int", "long", "float", "double", "bool", "char", "unsigned int", "unsigned long", "unsigned char", "size_t", "void",
+ "std::string", "std::vector", "std::map", "std::set", "std::list", "std::unordered_map", "std::unordered_set", "std::deque", "std::queue", "std::stack", "std::pair", "std::tuple", "std::optional", "std::any", "std::variant"
+ ));
+
+ // Configure template files for code generation
+ modelTemplateFiles.put("model-header.mustache", ".h");
+ modelTemplateFiles.put("model-source.mustache", ".cpp");
+ apiTemplateFiles.put("api-header.mustache", ".h");
+ apiTemplateFiles.put("api-source.mustache", ".cpp");
+
+ // Add CLI options
+ cliOptions.add(org.openapitools.codegen.CliOption.newBoolean(ADD_API_IMPL_STUBS,
+ "Generate API implementation stubs and a sample main.cpp for quick start").defaultValue("false"));
+
+ // Add supporting files for project structure
+ supportingFiles.add(new SupportingFile("CMakeLists.txt.mustache", "", "CMakeLists.txt"));
+ supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
+ supportingFiles.add(new SupportingFile("License.mustache", "", "LICENSE"));
+
+ // Initialize feature set for httplib server
+ // Note: Polymorphism is partially supported:
+ // ✅ allOf (inheritance/composition)
+ // ✅ anyOf/oneOf (std::variant)
+ // ❌ discriminator (not implemented)
+ modifyFeatureSet(features -> features
+ .includeDocumentationFeatures(DocumentationFeature.Readme)
+ .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON))
+ .securityFeatures(EnumSet.of(
+ SecurityFeature.ApiKey,
+ SecurityFeature.BasicAuth,
+ SecurityFeature.BearerToken
+ ))
+ .excludeGlobalFeatures(
+ GlobalFeature.XMLStructureDefinitions,
+ GlobalFeature.Callbacks,
+ GlobalFeature.LinkObjects
+ )
+ );
+
+ }
+
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.SERVER;
+ }
+
+ /**
+ * Returns the name identifier for this code generator.
+ *
+ * @return the generator name
+ */
+ @Override
+ public String getName() {
+ return DEFAULT_PROJECT_NAME;
+ }
+
+ /**
+ * Returns a brief description of what this code generator does.
+ *
+ * @return help text describing the generator's purpose
+ */
+ @Override
+ public String getHelp() {
+ return "Generates a C++ server using the httplib library.";
+ }
+
+ /**
+ * Converts a model name to its corresponding import statement.
+ * Handles both standard C++ types and custom model types.
+ *
+ * @param name the model name to import
+ * @return the appropriate #include directive or empty string
+ */
+ @Override
+ public String toModelImport(String name) {
+ // Skip if already formatted as an include
+ String includeFile;
+ if (name.startsWith("#include")) {
+ includeFile = null;
+ }
+ // Use import mapping if available
+ if (importMapping.containsKey(name)) {
+ includeFile = importMapping.get(name);
+ }
+ // Map OpenAPI type to C++ type if possible
+ String mappedType = typeMapping.getOrDefault(name, name);
+ if (languageSpecificPrimitives.contains(mappedType)) {
+ String include = getStandardIncludeForType(mappedType);
+ includeFile = include != null ? include : "";
+ } else {
+ Map pathFromClassName = stripPathFromClassName(mappedType);
+ includeFile = "#include \"" + pathFromClassName.get("className") + ".h\"";
+ }
+ return includeFile;
+ }
+
+ /**
+ * Returns the appropriate #include directive for a given C++ standard type.
+ *
+ * @param typeName the C++ type name
+ * @return the include directive or null if no include is needed
+ */
+ private String getStandardIncludeForType(String typeName) {
+ String mappedType = typeMapping.getOrDefault(typeName, typeName);
+ if (standardIncludes.containsKey(mappedType)) {
+ return standardIncludes.get(mappedType);
+ }
+ if (cstdintTypes.contains(typeName)) {
+ return "#include ";
+ }
+ else if (cstdintTypes.contains(mappedType)) {
+ return "#include ";
+ }
+ return null;
+ }
+
+ /**
+ * Post-processes operations after model processing to add C++ specific configurations.
+ * This method:
+ * - Sets up class names and namespaces for API classes
+ * - Processes request and response models for each operation
+ * - Maps response status codes to their corresponding types
+ * - Collects all used models for proper include generation
+ *
+ * @param objs the operations map to process
+ * @param allModels list of all models in the API
+ * @return the processed operations map
+ */
+
+ @Override
+ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
+
+ // No allParams logic; only expose requestModel, queryParams, headerParams as vendorExtensions
+ if (objs == null || objs.getOperations() == null) {
+ LOGGER.warn("Operations or operations map is null");
+ return objs;
+ }
+ OperationMap operations = objs.getOperations();
+ List operationList = operations.getOperation();
+ // Per-operation unique status code constants
+ // Set classname and compute apiNamespace ONCE per API class
+ String classname = operations.getClassname();
+ if ("DefaultApi".equals(classname) || classname == null || classname.isEmpty()) {
+ classname = addProjectOrDefaultName(classname, "DefaultApi");
+ operations.setClassname(classname);
+ }
+ objs.put("apiHeaderFileName", toPascalCase(classname) + toPascalCase(API_SUFFIX) + ".h");
+ objs.put("apiClassnameInPascalCase", toPascalCase(classname));
+
+ // Compute API namespace ONCE, append classname only if not already present as last segment
+ String apiNamespace = (String) additionalProperties.get(API_NAMESPACE);
+ if (apiNamespace == null || apiNamespace.isEmpty()) {
+ apiNamespace = API_SUFFIX.toLowerCase(Locale.ROOT);
+ }
+
+ objs.put("apiNamespace", apiNamespace);
+
+ // Build a lookup map: PascalCase className -> modelNamespace
+ Map modelNamespaceMap = new HashMap<>();
+ for (ModelMap modelMap : allModels) {
+ CodegenModel model = modelMap.getModel();
+ if (model != null && model.vendorExtensions.containsKey("modelNamespace")) {
+ modelNamespaceMap.put(model.classname, (String) model.vendorExtensions.get("modelNamespace"));
+ }
+ }
+
+ // Track all models used for includes
+ Set modelsUsed = new HashSet<>();
+ List> opStatusCodeConsts = new ArrayList<>();
+ boolean includeOptionalHeader = false;
+ for (CodegenOperation op : operationList) {
+ boolean hasPrimitiveParams = false;
+ Set seenSuccessTypes = new HashSet<>();
+ if (op.vendorExtensions == null) {
+ op.vendorExtensions = new HashMap<>();
+ }
+ // Add modelNamespace to vendorExtensions
+ String modelNamespace = (String) additionalProperties.get(MODEL_NAMESPACE);
+ final String apiNamespacetoFilter = apiNamespace;
+ //Filter common words in api and model namespaces
+ String namespaceFiltered = Arrays.stream(modelNamespace.toLowerCase(Locale.ROOT).split("::"))
+ .filter(word -> !Arrays.asList(apiNamespacetoFilter.toLowerCase(Locale.ROOT).split("::")).contains(word))
+ .collect(Collectors.joining("::"));
+ // Handle request model
+ if (op.bodyParam != null && op.bodyParam.baseType != null) {
+ String className = op.bodyParam.baseType;
+ String requestModel = toPascalCase(className);
+ op.vendorExtensions.put("requestModel", requestModel);
+
+ // Set bodyParam dataType with namespace prefix
+ if(!namespaceFiltered.isEmpty()) {
+ op.vendorExtensions.put("requestModelNamespace", namespaceFiltered);
+ op.bodyParam.dataType = namespaceFiltered + "::" + requestModel;
+ } else {
+ op.bodyParam.dataType = requestModel;
+ }
+
+ // Wrap in std::optional if not required
+ if (!op.bodyParam.required) {
+ op.bodyParam.dataType = "std::optional<" + op.bodyParam.dataType + ">";
+ }
+
+ includeOptionalHeader = true;
+ modelsUsed.add(requestModel);
+ }
+ // Add type flags for query and header params for template type conversion
+ if (op.queryParams != null) {
+ for (CodegenParameter qp : op.queryParams) {
+// setCppTypeFlags(qp);
+ if (!hasPrimitiveParams && Boolean.TRUE.equals(qp.vendorExtensions.get("isPrimitive"))) {
+ hasPrimitiveParams = true;
+ }
+ }
+ }
+ if (op.headerParams != null) {
+ for (CodegenParameter hp : op.headerParams) {
+// setCppTypeFlags(hp);
+ if (!hasPrimitiveParams && Boolean.TRUE.equals(hp.vendorExtensions.get("isPrimitive"))) {
+ hasPrimitiveParams = true;
+ }
+ }
+ }
+ op.vendorExtensions.put("hasPrimitiveParams", hasPrimitiveParams);
+ op.vendorExtensions.put("operationIdPascalCase", toHandlerFunctionName(op.httpMethod, op.path.toString(),false));
+ op.vendorExtensions.put("httpMethod", op.httpMethod != null ? toPascalCase(op.httpMethod) : "");
+ op.vendorExtensions.put("handlerFunctionName", toHandlerFunctionName(op.httpMethod, op.path.toString(),true));
+ op.vendorExtensions.put("requestType", toHandlerFunctionRequest(op.path.toString(),op.httpMethod));
+ op.vendorExtensions.put("responseType", toHandlerFunctionResponse(op.path.toString(),op.httpMethod));
+ if (op.path != null) {
+ op.vendorExtensions.put("path", op.path);
+ }
+
+ // Expose query, header, path, and cookie parameters for mustache
+ if (op.queryParams == null) op.queryParams = new ArrayList<>();
+ if (op.headerParams == null) op.headerParams = new ArrayList<>();
+ if (op.pathParams == null) op.pathParams = new ArrayList<>();
+ if (op.cookieParams == null) op.cookieParams = new ArrayList<>();
+
+ // Track path parameter indices for regex matching in C++
+ int pathParamIndex = 1; // Start at 1 for regex match groups
+
+ // Add style/explode/allowReserved to each param
+ for (CodegenParameter param : op.queryParams) {
+ param.vendorExtensions.put("style", param.style);
+ param.vendorExtensions.put("explode", param.isExplode);
+ // Set parameter type flags
+ setParameterTypeFlags(param);
+
+ // Track models used in parameters (for includes and namespace prefix)
+ if (param.baseType != null && !param.isPrimitiveType && !languageSpecificPrimitives.contains(param.baseType)) {
+ String className = toPascalCase(param.baseType);
+ modelsUsed.add(className);
+ // Add namespace prefix to parameter dataType if it's a model and doesn't already have namespace
+ // Extract inner type from containers like std::optional and std::vector
+ String innerType = param.dataType;
+ if (param.dataType.contains("<")) {
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = param.dataType.substring(startIdx, endIdx).trim();
+ }
+ }
+ if (!innerType.contains("::") && !languageSpecificPrimitives.contains(innerType)) {
+ String prefixedType = namespaceFiltered + "::" + innerType;
+ param.dataType = param.dataType.replace(innerType, prefixedType);
+ }
+ } else if (param.dataType != null && param.dataType.contains("<")) {
+ // Additional handling: extract model from dataType when baseType might not be set
+ // This handles cases like std::optional where baseType might be incorrectly set
+ String innerType = param.dataType;
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = param.dataType.substring(startIdx, endIdx).trim();
+ // Check if this looks like a model class (PascalCase)
+ if (!languageSpecificPrimitives.contains(innerType) &&
+ !innerType.startsWith("std::") &&
+ !innerType.contains("::") &&
+ innerType.length() > 0 &&
+ Character.isUpperCase(innerType.charAt(0))) {
+ modelsUsed.add(innerType);
+ // Add namespace prefix
+ String prefixedType = namespaceFiltered + "::" + innerType;
+ param.dataType = param.dataType.replace(innerType, prefixedType);
+ }
+ }
+ }
+
+ // Extract unwrapped type for JSON deserialization AFTER namespace has been added
+ // For std::optional, we need T for the .get() call
+ String unwrappedType = param.dataType;
+ if (param.dataType != null && param.dataType.contains("<")) {
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ unwrappedType = param.dataType.substring(startIdx, endIdx).trim();
+ }
+ }
+ param.vendorExtensions.put("unwrappedDataType", unwrappedType);
+ }
+ for (CodegenParameter param : op.headerParams) {
+ param.vendorExtensions.put("style", param.style);
+ param.vendorExtensions.put("explode", param.isExplode);
+ setParameterTypeFlags(param);
+ // Track models used in parameters
+ if (param.baseType != null && !param.isPrimitiveType && !languageSpecificPrimitives.contains(param.baseType)) {
+ String className = toPascalCase(param.baseType);
+ modelsUsed.add(className);
+ // Extract inner type from containers
+ String innerType = param.dataType;
+ if (param.dataType.contains("<")) {
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = param.dataType.substring(startIdx, endIdx).trim();
+ }
+ }
+ if (!innerType.contains("::") && !languageSpecificPrimitives.contains(innerType)) {
+ String prefixedType = namespaceFiltered + "::" + innerType;
+ param.dataType = param.dataType.replace(innerType, prefixedType);
+ }
+ }
+ }
+ for (CodegenParameter param : op.pathParams) {
+ param.vendorExtensions.put("style", param.style);
+ param.vendorExtensions.put("explode", param.isExplode);
+ param.vendorExtensions.put("pathIndex", pathParamIndex);
+ pathParamIndex++;
+ setParameterTypeFlags(param);
+ // Track models used in parameters
+ if (param.baseType != null && !param.isPrimitiveType && !languageSpecificPrimitives.contains(param.baseType)) {
+ String className = toPascalCase(param.baseType);
+ modelsUsed.add(className);
+ // Extract inner type from containers
+ String innerType = param.dataType;
+ if (param.dataType.contains("<")) {
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = param.dataType.substring(startIdx, endIdx).trim();
+ }
+ }
+ if (!innerType.contains("::") && !languageSpecificPrimitives.contains(innerType)) {
+ String prefixedType = namespaceFiltered + "::" + innerType;
+ param.dataType = param.dataType.replace(innerType, prefixedType);
+ }
+ }
+ }
+ for (CodegenParameter param : op.cookieParams) {
+ param.vendorExtensions.put("style", param.style);
+ param.vendorExtensions.put("explode", param.isExplode);
+ setParameterTypeFlags(param);
+ // Track models used in parameters
+ if (param.baseType != null && !param.isPrimitiveType && !languageSpecificPrimitives.contains(param.baseType)) {
+ String className = toPascalCase(param.baseType);
+ modelsUsed.add(className);
+ // Extract inner type from containers
+ String innerType = param.dataType;
+ if (param.dataType.contains("<")) {
+ int startIdx = param.dataType.indexOf('<') + 1;
+ int endIdx = param.dataType.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = param.dataType.substring(startIdx, endIdx).trim();
+ }
+ }
+ if (!innerType.contains("::") && !languageSpecificPrimitives.contains(innerType)) {
+ String prefixedType = namespaceFiltered + "::" + innerType;
+ param.dataType = param.dataType.replace(innerType, prefixedType);
+ }
+ }
+ }
+ op.vendorExtensions.put("queryParams", op.queryParams);
+ op.vendorExtensions.put("headerParams", op.headerParams);
+ op.vendorExtensions.put("pathParams", op.pathParams);
+ op.vendorExtensions.put("cookieParams", op.cookieParams);
+
+ // Process security requirements
+ processSecurityRequirements(op);
+
+ // Process responses
+ List errorTypes = new ArrayList<>();
+ Set successTypesSet = new LinkedHashSet<>();
+ Set allResponseTypesSet = new LinkedHashSet<>();
+ List> successCodeToTypes = new ArrayList<>();
+ List> errorCodeToTypes = new ArrayList<>();
+ boolean isSuccessResponseType = false;
+ boolean isSuccessResponsePrimitive = false;
+ boolean isErrorResponsePrimitive = false;
+ boolean hasVoidResponse = false;
+
+ for (CodegenResponse resp : op.responses) {
+ boolean hasAnyResponseSchema = false;
+
+ if (resp.code != null) {
+ // Check if response has no schema (void response)
+ if (resp.baseType == null || resp.baseType.isEmpty()) {
+ hasVoidResponse = true;
+ continue;
+ }
+
+ // Collect all 2xx response types as successTypes
+ if (resp.code.startsWith("2") && resp.baseType != null) {
+ hasAnyResponseSchema = true;
+ String successType;
+ String successConstName;
+ if (!typeMapping.containsKey(resp.baseType)) {
+ String className = toPascalCase(resp.baseType);
+ successType = namespaceFiltered + "::" + className;
+ modelsUsed.add(className);
+ successConstName = HTTP_RESPONSE_PREFIX + StringUtils.underscore(className).toUpperCase(Locale.ROOT);
+ } else {
+ successType = typeMapping.get(resp.baseType);
+ successConstName = HTTP_RESPONSE_PREFIX + "PRIMITIVE_"+StringUtils.underscore(resp.baseType).toUpperCase(Locale.ROOT);
+ isSuccessResponsePrimitive = true;
+ }
+ // Only add if not already in set (deduplication across all responses)
+ successTypesSet.add(successType);
+ if (allResponseTypesSet.add(successType)) {
+ if(successConstName != null && !successConstName.isEmpty() && successType!= null && !successType.isEmpty()) {
+ final String finalSuccessConstName = successConstName;
+ boolean successConstExists = opStatusCodeConsts.stream()
+ .anyMatch(listitem -> finalSuccessConstName.equals(listitem.get("constName")));
+ if (!successConstExists) {
+ Map item = new HashMap<>();
+ item.put("constName", successConstName);
+ item.put("statusCode", resp.code);
+ opStatusCodeConsts.add(item);
+ }
+ Map successItem = new HashMap<>();
+ successItem.put("successType", successType);
+ successItem.put("successConstName", successConstName);
+ successCodeToTypes.add(successItem);
+ }
+ }
+ } else {
+ String errorBaseType = resp.baseType;
+ String errorConstName = "";
+ if (errorBaseType != null && !errorBaseType.isEmpty()) {
+ hasAnyResponseSchema = true;
+ String errorType = "";
+ if (!typeMapping.containsKey(errorBaseType)) {
+ String className = toPascalCase(errorBaseType);
+ errorType = namespaceFiltered + "::" + className;
+ modelsUsed.add(className);
+ errorConstName = HTTP_RESPONSE_PREFIX + StringUtils.underscore(className).toUpperCase(Locale.ROOT);
+ // errorTypeIsEnum = Boolean.TRUE.equals(classNameIsEnumMap.get(toPascalCase(className)));
+ } else {
+ isErrorResponsePrimitive = true;
+ errorType = typeMapping.get(resp.baseType);
+ errorConstName = HTTP_RESPONSE_PREFIX + "PRIMITIVE_" + StringUtils.underscore(resp.baseType).toUpperCase(Locale.ROOT);
+ }
+
+ if(errorConstName != null && !errorConstName.isEmpty()) {
+ errorTypes.add(errorType);
+ op.vendorExtensions.put("errorTypes", errorTypes);
+ // Only add to errorCodeToTypes if not already used in any response
+ if (allResponseTypesSet.add(errorType)) {
+ final String finalErrorConstName = errorConstName;
+ boolean errorConstExists = opStatusCodeConsts.stream()
+ .anyMatch(listitem -> finalErrorConstName.equals(listitem.get("constName")));
+ if (!errorConstName.isEmpty() && !errorConstExists) {
+ Map item = new HashMap<>();
+ item.put("constName", errorConstName);
+ item.put("statusCode", resp.code);
+ opStatusCodeConsts.add(item);
+ }
+ Map errorItem = new HashMap<>();
+ errorItem.put("errorType", errorType);
+ errorItem.put("errorConstName", errorConstName);
+ // errorItem.put("errorTypeIsEnum", errorTypeIsEnum);
+ errorCodeToTypes.add(errorItem);
+ }
+ }
+ }
+ }
+ if (hasAnyResponseSchema) {
+ op.vendorExtensions.put("hasAnyResponseSchema", true);
+ }
+ }
+ }
+
+ op.vendorExtensions.put("hasVoidResponse", hasVoidResponse);
+ op.vendorExtensions.put("isSuccessResponseType", isSuccessResponseType);
+ op.vendorExtensions.put("isSuccessResponsePrimitive", isSuccessResponsePrimitive);
+ op.vendorExtensions.put("isErrorResponsePrimitive", isErrorResponsePrimitive);
+
+ // Convert Set to List for template
+ if (!successTypesSet.isEmpty()) {
+ op.vendorExtensions.put("successTypes", new ArrayList<>(successTypesSet));
+ }
+ if (successCodeToTypes != null && !successCodeToTypes.isEmpty()) {
+ op.vendorExtensions.put("successCodeToTypes", successCodeToTypes);
+ }
+ if (errorCodeToTypes != null && !errorCodeToTypes.isEmpty()) {
+ op.vendorExtensions.put("errorCodeToTypes", errorCodeToTypes);
+ }
+
+ // Check if we have only one response type (to avoid unnecessary std::variant)
+ int totalResponseTypes = successCodeToTypes.size() + errorCodeToTypes.size();
+ if (totalResponseTypes == 1) {
+ op.vendorExtensions.put("hasSingleResponseType", true);
+ // Store the single response type
+ if (!successCodeToTypes.isEmpty()) {
+ op.vendorExtensions.put("singleResponseType", successCodeToTypes.get(0).get("successType"));
+ } else if (!errorCodeToTypes.isEmpty()) {
+ op.vendorExtensions.put("singleResponseType", errorCodeToTypes.get(0).get("errorType"));
+ }
+ } else {
+ op.vendorExtensions.put("hasSingleResponseType", false);
+ }
+
+ boolean hasRequestSchema = (op.queryParams != null && !op.queryParams.isEmpty()) ||
+ (op.headerParams != null && !op.headerParams.isEmpty()) ||
+ (op.cookieParams != null && !op.cookieParams.isEmpty()) ||
+ (op.pathParams != null && !op.pathParams.isEmpty()) ||
+ (op.bodyParams != null && !op.bodyParams.isEmpty());
+ op.vendorExtensions.put("hasAnyRequestSchema", hasRequestSchema);
+
+ // Check if operation has any array parameters (for sstream include)
+ boolean hasArrayParams = false;
+ for (CodegenParameter param : op.allParams) {
+ if (param.isArray) {
+ hasArrayParams = true;
+ break;
+ }
+ }
+ op.vendorExtensions.put("hasArrayParams", hasArrayParams);
+ }
+
+ // Aggregate security settings from all operations in this API
+ // Also check if ANY operation actually uses auth (hasAuth flag)
+ boolean hasApiKeyAuth = false;
+ boolean hasBearerAuth = false;
+ boolean hasBasicAuth = false;
+ boolean hasOAuth2 = false;
+ boolean anyOperationUsesAuth = false;
+ List> apiKeyAuthList = new ArrayList<>();
+ List> bearerAuthList = new ArrayList<>();
+ List> basicAuthList = new ArrayList<>();
+ List> oauth2AuthList = new ArrayList<>();
+ Set seenApiKeys = new HashSet<>();
+ Set seenBearers = new HashSet<>();
+ Set seenBasics = new HashSet<>();
+ Set seenOAuth2s = new HashSet<>();
+
+ for (CodegenOperation op : operationList) {
+ // Check if this operation uses auth
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasAuth"))) {
+ anyOperationUsesAuth = true;
+ }
+
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasApiKeyAuth"))) {
+ hasApiKeyAuth = true;
+ @SuppressWarnings("unchecked")
+ List> opApiKeys = (List>) op.vendorExtensions.get("apiKeyAuth");
+ if (opApiKeys != null) {
+ for (Map auth : opApiKeys) {
+ String name = (String) auth.get("name");
+ if (name != null && seenApiKeys.add(name)) {
+ apiKeyAuthList.add(auth);
+ }
+ }
+ }
+ }
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasBearerAuth"))) {
+ hasBearerAuth = true;
+ @SuppressWarnings("unchecked")
+ List> opBearers = (List>) op.vendorExtensions.get("bearerAuth");
+ if (opBearers != null) {
+ for (Map auth : opBearers) {
+ String name = (String) auth.get("name");
+ if (name != null && seenBearers.add(name)) {
+ bearerAuthList.add(auth);
+ }
+ }
+ }
+ }
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasBasicAuth"))) {
+ hasBasicAuth = true;
+ @SuppressWarnings("unchecked")
+ List> opBasics = (List>) op.vendorExtensions.get("basicAuth");
+ if (opBasics != null) {
+ for (Map auth : opBasics) {
+ String name = (String) auth.get("name");
+ if (name != null && seenBasics.add(name)) {
+ basicAuthList.add(auth);
+ }
+ }
+ }
+ }
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasOAuth2"))) {
+ hasOAuth2 = true;
+ @SuppressWarnings("unchecked")
+ List> opOAuth2s = (List>) op.vendorExtensions.get("oauth2Auth");
+ if (opOAuth2s != null) {
+ for (Map auth : opOAuth2s) {
+ String name = (String) auth.get("name");
+ if (name != null && seenOAuth2s.add(name)) {
+ oauth2AuthList.add(auth);
+ }
+ }
+ }
+ }
+ }
+
+ // Add aggregated security settings to objs for the API class
+ // Only add them if at least one operation actually uses authentication
+ boolean hasAnyAuth = false;
+ if (anyOperationUsesAuth) {
+ hasAnyAuth = true;
+ if (hasApiKeyAuth) {
+ objs.put("hasApiKeyAuth", true);
+ objs.put("apiKeyAuth", apiKeyAuthList);
+ }
+ if (hasBearerAuth) {
+ objs.put("hasBearerAuth", true);
+ objs.put("bearerAuth", bearerAuthList);
+ }
+ if (hasBasicAuth) {
+ objs.put("hasBasicAuth", true);
+ objs.put("basicAuth", basicAuthList);
+ }
+ if (hasOAuth2) {
+ objs.put("hasOAuth2", true);
+ objs.put("oauth2Auth", oauth2AuthList);
+ }
+ }
+ // Only set hasAnyAuth if at least one operation actually uses auth
+ if (hasAnyAuth) {
+ objs.put("hasAnyAuth", true);
+ }
+
+ Object objApiStubs = additionalProperties.get(ADD_API_IMPL_STUBS);
+ boolean addApiImplStubs = false;
+ if(objApiStubs instanceof Boolean) {
+ addApiImplStubs = (Boolean) objApiStubs;
+ }
+ if(objApiStubs instanceof String) {
+ addApiImplStubs = Boolean.parseBoolean((String) objApiStubs);
+ }
+ LOGGER.debug("Generating Api stubs in the source file:{}.",addApiImplStubs);
+ if(addApiImplStubs == true) {
+ objs.put("addApiImplStubs", true);
+ }
+
+ // Create a map from status code to constant name for easy lookup in templates
+ Map statusCodeToConstMap = new HashMap<>();
+ for (Map item : opStatusCodeConsts) {
+ String code = item.get("statusCode");
+ String constName = item.get("constName");
+ // Store the first constant found for each code (prefer spec-defined ones)
+ if (!statusCodeToConstMap.containsKey(code)) {
+ statusCodeToConstMap.put(code, constName);
+ }
+ }
+
+ // Determine which common status codes are actually needed
+ boolean needsNoContent = false; // 204 - used for void responses
+ boolean needsBadRequest = false; // 400 - used for parameter validation
+ boolean needsUnauthorized = false; // 401 - used for authentication failures
+ boolean needsInternalError = false; // 500 - used for JSON parsing errors and response handler fallback
+
+ for (CodegenOperation op : operationList) {
+ // 204 NO_CONTENT: needed if any operation has void response
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasVoidResponse"))) {
+ needsNoContent = true;
+ }
+ // 400 BAD_REQUEST: needed if any operation has request parameters or request body
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasAnyRequestSchema"))) {
+ needsBadRequest = true;
+ }
+ // 401 UNAUTHORIZED: needed if any operation has authentication
+ if (Boolean.TRUE.equals(op.vendorExtensions.get("hasAuth"))) {
+ needsUnauthorized = true;
+ }
+ // 500 INTERNAL_SERVER_ERROR: needed if any operation has request body (for JSON parsing errors)
+ // or if any operation has response schema (for response handler fallback)
+ if (op.vendorExtensions.get("requestModel") != null ||
+ Boolean.TRUE.equals(op.vendorExtensions.get("hasAnyResponseSchema"))) {
+ needsInternalError = true;
+ }
+ }
+
+ // Only add common status codes if they're actually needed
+ if (needsNoContent && !statusCodeToConstMap.containsKey("204")) {
+ String constName = HTTP_RESPONSE_PREFIX + "NO_CONTENT";
+ statusCodeToConstMap.put("204", constName);
+ Map item = new HashMap<>();
+ item.put("constName", constName);
+ item.put("statusCode", "204");
+ opStatusCodeConsts.add(item);
+ }
+
+ if (needsBadRequest && !statusCodeToConstMap.containsKey("400")) {
+ String constName = HTTP_RESPONSE_PREFIX + "BAD_REQUEST";
+ statusCodeToConstMap.put("400", constName);
+ Map item = new HashMap<>();
+ item.put("constName", constName);
+ item.put("statusCode", "400");
+ opStatusCodeConsts.add(item);
+ }
+
+ if (needsUnauthorized && !statusCodeToConstMap.containsKey("401")) {
+ String constName = HTTP_RESPONSE_PREFIX + "UNAUTHORIZED";
+ statusCodeToConstMap.put("401", constName);
+ Map item = new HashMap<>();
+ item.put("constName", constName);
+ item.put("statusCode", "401");
+ opStatusCodeConsts.add(item);
+ }
+
+ if (needsInternalError && !statusCodeToConstMap.containsKey("500")) {
+ String constName = HTTP_RESPONSE_PREFIX + "INTERNAL_SERVER_ERROR";
+ statusCodeToConstMap.put("500", constName);
+ Map item = new HashMap<>();
+ item.put("constName", constName);
+ item.put("statusCode", "500");
+ opStatusCodeConsts.add(item);
+ }
+
+ // Pass the map to templates
+ objs.put("statusCodeToConst", statusCodeToConstMap);
+
+ if (!opStatusCodeConsts.isEmpty()) {
+ objs.put("statusCodeConsts", opStatusCodeConsts);
+ }
+ // Add modelsUsed to objs for header includes
+ if(modelsUsed != null && !modelsUsed.isEmpty()) {
+ ArrayList sortedModels = new ArrayList<>(modelsUsed);
+ Collections.sort(sortedModels);
+ objs.put("modelsUsed", new ArrayList<>(sortedModels));
+ objs.put("includeVariantHeader", INCLUDE_VARIANT);
+ }
+ if(includeOptionalHeader) {
+ objs.put("includeOptionalHeader", INCLUDE_OPTIONAL);
+ }
+ return objs;
+ }
+
+ /**
+ * Preprocesses the OpenAPI specification to enhance inline schemas with proper titles.
+ * This method ensures that request and response schemas without explicit titles
+ * get automatically generated names based on the operation ID.
+ *
+ * @param openAPI the OpenAPI specification to preprocess
+ */
+
+ /**
+ * Recursively processes nested schemas within a parent schema to set meaningful titles.
+ * This handles cases like component responses that have nested inline object schemas.
+ *
+ * @param schema the parent schema to process
+ * @param parentName the name of the parent (used for generating child names)
+ */
+ @SuppressWarnings("rawtypes")
+ private void processNestedSchemas(Schema schema, String parentName) {
+ processNestedSchemas(schema, parentName, 0);
+ }
+
+ private void processNestedSchemas(Schema schema, String parentName, int depth) {
+ if (schema == null || depth > 10) {
+ return;
+ }
+
+ // Process properties of object schemas
+ if (schema.getProperties() != null) {
+ for (Map.Entry propEntry : ((Map) schema.getProperties()).entrySet()) {
+ Schema propSchema = propEntry.getValue();
+ String propertyName = propEntry.getKey();
+
+ // Set title for nested inline schemas
+ if (propSchema.getTitle() == null && propSchema.get$ref() == null) {
+ // For nested objects, create a meaningful name
+ if ("object".equals(propSchema.getType()) || (propSchema.getType() == null && propSchema.getProperties() != null)) {
+ String title = toPascalCase(parentName + "_" + propertyName);
+ propSchema.setTitle(title);
+
+ // Recursively process nested properties
+ processNestedSchemas(propSchema, title, depth + 1);
+ }
+ }
+ }
+ }
+
+ // Process array items
+ if (schema.getItems() != null) {
+ Schema itemSchema = schema.getItems();
+ if (itemSchema.getTitle() == null && itemSchema.get$ref() == null) {
+ if ("object".equals(itemSchema.getType()) || (itemSchema.getType() == null && itemSchema.getProperties() != null)) {
+ String title = toPascalCase(parentName + "_item");
+ itemSchema.setTitle(title);
+ processNestedSchemas(itemSchema, title, depth + 1);
+ }
+ }
+ }
+
+ // Process additionalProperties
+ if (schema.getAdditionalProperties() instanceof Schema) {
+ Schema additionalSchema = (Schema) schema.getAdditionalProperties();
+ if (additionalSchema.getTitle() == null && additionalSchema.get$ref() == null) {
+ if ("object".equals(additionalSchema.getType()) || (additionalSchema.getType() == null && additionalSchema.getProperties() != null)) {
+ String title = toPascalCase(parentName + "_additional");
+ additionalSchema.setTitle(title);
+ processNestedSchemas(additionalSchema, title, depth + 1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Post-processes all models to add C++ specific configurations and namespace handling.
+ * This method:
+ * - Computes model namespaces based on class names
+ * - Filters imports to exclude primitives and standard types
+ * - Processes model variables for proper C++ typing
+ * - Sets up proper getter/setter configurations
+ *
+ * @param objs map of all models to process
+ * @return the processed models map
+ */
+ @Override
+ public Map postProcessAllModels(Map objs) {
+
+ Map processed = super.postProcessAllModels(objs);
+
+ // Compute modelNamespace ONCE and append className
+ String modelNamespaceBase = (String) additionalProperties.get(MODEL_NAMESPACE);
+ if (modelNamespaceBase == null || modelNamespaceBase.isEmpty()) {
+ modelNamespaceBase = MODEL_SUFFIX.toLowerCase(Locale.ROOT);
+ additionalProperties.put(MODEL_NAMESPACE, modelNamespaceBase);
+ }
+
+ for (Map.Entry entry : processed.entrySet()) {
+ ModelsMap modelsMap = entry.getValue();
+
+ for (ModelMap modelMap : modelsMap.getModels()) {
+ CodegenModel model = modelMap.getModel();
+ if (model != null) {
+ String modelClassName = model.classname;
+ // Convert model classname to proper PascalCase
+ String fixedClassName = toPascalCase(modelClassName);
+ LOGGER.debug("Model classname: {} -> {}", modelClassName, fixedClassName);
+ model.classname = fixedClassName;
+ // Set in vendorExtensions for backward compatibility
+ model.vendorExtensions.put("modelNamespace", modelNamespaceBase);
+ model.vendorExtensions.put("modelClassName", model.classname);
+
+ // --- Filter and set imports for mustache ---
+ Set systemHeaders = new LinkedHashSet<>();
+ Set projectHeaders = new LinkedHashSet<>();
+
+ if (model.imports != null) {
+ for (String imp : model.imports) {
+ // Only add if not a primitive, not a mapped type, and not "object", "nlohmann::json", or "Map"
+ if (!languageSpecificPrimitives.contains(imp)
+ && !typeMapping.containsKey(imp)
+ && !"object".equals(imp)
+ && !"nlohmann::json".equals(imp)
+ && !"Map".equals(imp)) {
+
+ String headerName = toPascalCase(imp);
+ projectHeaders.add("#include \"" + headerName + ".h\"");
+ }
+ else {
+ String standardInclude = getStandardIncludeForType(imp);
+ if (standardInclude != null) {
+ systemHeaders.add(standardInclude);
+ }
+ }
+ }
+ }
+
+ // Combine system headers first, then project headers (both sorted)
+ List filteredImports = new ArrayList<>();
+ List sortedSystemHeaders = new ArrayList<>(systemHeaders);
+ Collections.sort(sortedSystemHeaders);
+ filteredImports.addAll(sortedSystemHeaders);
+
+ List sortedProjectHeaders = new ArrayList<>(projectHeaders);
+ Collections.sort(sortedProjectHeaders);
+ filteredImports.addAll(sortedProjectHeaders);
+
+ // Set the imports for mustache template
+ model.vendorExtensions.put("filteredImports", filteredImports);
+ // --- End filter and set imports ---
+
+ if (model.allVars != null) {
+ for (CodegenProperty var : model.allVars) {
+ processModelVariable(var, model);
+ }
+ }
+ if (model.vars != null) {
+ for (CodegenProperty var : model.vars) {
+ processModelVariable(var, model);
+ }
+ }
+ // Ensure vars is populated from allVars for template access
+ if ((model.vars == null || model.vars.isEmpty()) && model.allVars != null && !model.allVars.isEmpty()) {
+ model.vars = new ArrayList<>(model.allVars);
+ }
+
+ // After processing variables, check if any use optional or variant
+ // (processModelVariable may wrap types in std::optional)
+ boolean hasOptional = false;
+ boolean needsVariant = Boolean.TRUE.equals(model.vendorExtensions.get("hasVariant"));
+
+ List allProperties = new ArrayList<>();
+ if (model.vars != null) allProperties.addAll(model.vars);
+ if (model.allVars != null) {
+ for (CodegenProperty v : model.allVars) {
+ if (!allProperties.contains(v)) allProperties.add(v);
+ }
+ }
+
+ for (CodegenProperty var : allProperties) {
+ if (var.dataType != null) {
+ if (!hasOptional && var.dataType.contains("std::optional<")) {
+ hasOptional = true;
+ }
+ if (!needsVariant && var.dataType.contains("std::variant<")) {
+ needsVariant = true;
+ }
+ }
+ }
+
+ // Update filtered imports if needed
+ if (hasOptional && !filteredImports.contains("#include ")) {
+ filteredImports.add(0, "#include ");
+ }
+ if (needsVariant && !filteredImports.contains("#include ")) {
+ filteredImports.add(0, "#include ");
+ }
+
+ model.vendorExtensions.put("filteredImports", filteredImports);
+ }
+ }
+ }
+
+ return processed;
+ }
+
+ /**
+ * Processes individual model variables to configure C++ specific attributes.
+ * Handles nullable types, container types, and default values according to C++ conventions.
+ *
+ * @param var the model variable to process
+ * @param model the parent model containing this variable
+ */
+ private void processModelVariable(CodegenProperty var, CodegenModel model) {
+ // Ensure baseName is set for JSON serialization
+ if (var.baseName == null || var.baseName.isEmpty()) {
+ var.baseName = var.name;
+ }
+ String modelClassName = model.vendorExtensions.get("modelClassName").toString();
+ String varName = toPascalCase(var.name);
+
+ // Convert inline model names in dataType to PascalCase
+ if (var.dataType != null && !var.isPrimitiveType && !var.isContainer) {
+ // Check if dataType contains underscore or hyphen (likely inline model)
+ if (var.dataType.contains("_") || var.dataType.contains("-")) {
+ // Don't process if it's a C++ standard library type
+ if (!var.dataType.startsWith("std::")) {
+ var.dataType = toPascalCase(var.dataType);
+ }
+ }
+ }
+ if (var.datatypeWithEnum != null && !var.isPrimitiveType && !var.isContainer) {
+ if (var.datatypeWithEnum.contains("_") || var.datatypeWithEnum.contains("-")) {
+ if (!var.datatypeWithEnum.startsWith("std::")) {
+ var.datatypeWithEnum = toPascalCase(var.datatypeWithEnum);
+ }
+ }
+ }
+
+ // Replace oas_any_type_not_mapped placeholder with nlohmann::json
+ if ("oas_any_type_not_mapped".equals(var.dataType)) {
+ var.dataType = "nlohmann::json";
+ }
+ if ("oas_any_type_not_mapped".equals(var.datatypeWithEnum)) {
+ var.datatypeWithEnum = "nlohmann::json";
+ }
+ if (var.items != null && "oas_any_type_not_mapped".equals(var.items.dataType)) {
+ var.items.dataType = "nlohmann::json";
+ }
+ if (var.items != null && "oas_any_type_not_mapped".equals(var.items.datatypeWithEnum)) {
+ var.items.datatypeWithEnum = "nlohmann::json";
+ }
+
+ // Handle anyOf/oneOf union types (std::variant)
+ if (var.dataType != null && var.dataType.startsWith("std::variant<")) {
+ var.vendorExtensions.put("isUnionType", true);
+
+ // Generate union type alias name from property name
+ String unionTypeName = toPascalCase(var.name);
+ var.vendorExtensions.put("unionTypeName", unionTypeName);
+
+ // Extract individual types from the variant declaration
+ String variantContent = var.dataType.substring(13, var.dataType.length() - 1);
+ List unionTypes = Arrays.asList(variantContent.split(",\\s*")); // Split by comma and optional whitespace
+ var.vendorExtensions.put("unionTypes", unionTypes);
+
+ // Mark that model needs variant include
+ model.vendorExtensions.put("hasVariant", true);
+ } else {
+ var.vendorExtensions.put("isUnionType", false);
+ }
+
+ // Handle nullable types
+ if (var.isNullable) {
+ var.vendorExtensions.put("isOptional", true);
+ // Don't wrap again if already wrapped
+ if (!var.dataType.startsWith("std::optional<")) {
+ // Store the inner type for the template
+ var.vendorExtensions.put("innerType", var.dataType);
+ var.dataType = "std::optional<" + var.dataType + ">";
+ }
+ } else {
+ var.vendorExtensions.put("isOptional", false);
+ }
+
+ // Remove namespace prefixes from enum names in array items
+ // Enums are defined within the class scope and don't need prefixes
+ if (var.isArray && var.items != null && var.items.isEnum) {
+ if (var.items.dataType != null && var.items.dataType.contains("::")) {
+ String[] parts = var.items.dataType.split("::");
+ var.items.dataType = parts[parts.length - 1];
+ }
+ if (var.items.datatypeWithEnum != null && var.items.datatypeWithEnum.contains("::")) {
+ String[] parts = var.items.datatypeWithEnum.split("::");
+ var.items.datatypeWithEnum = parts[parts.length - 1];
+ }
+ }
+
+ // Handle container types
+ if (var.isArray && var.dataType.startsWith("std::vector<")) {
+ // Use datatypeWithEnum for enums to preserve enum type names before they're stripped
+ String itemType = var.items != null ?
+ (var.items.datatypeWithEnum != null ? var.items.datatypeWithEnum : var.items.dataType) :
+ "std::string";
+
+ // For inline enums in arrays, qualify with the model class name if not already qualified
+ if (var.items != null && var.items.isEnum && !itemType.contains("::")) {
+ itemType = modelClassName + "::" + itemType;
+ }
+
+ // Use property name and item type for better naming
+ String arrayTypeName = toPascalCase(var.name) + "VectorOf" + toPascalCase(itemType);
+ var.vendorExtensions.put("arrayTypeName", arrayTypeName);
+ var.dataType = "std::vector<" + itemType + ">";
+ var.datatypeWithEnum = var.dataType; // Keep them in sync
+ }
+
+ if (var.isMap && var.dataType.startsWith("std::map<")) {
+ String itemType = var.items != null ? var.items.dataType : "std::string";
+ var.dataType = "std::map";
+ }
+
+ if(var.items !=null && "string".equals(var.items.dataType)) {
+ var.items.dataType = "std::string";
+ var.items.isPrimitiveType = true;
+ }
+
+ // Handle arrays
+ if (var.isArray) {
+ model.vendorExtensions.put("hasArrays", true);
+ setArrayVendorExtensions(var, varName, modelClassName, model);
+
+ // Set default value for arrays - use datatypeWithEnum for correct enum type
+ if (var.defaultValue == null) {
+ // Use datatypeWithEnum which has the correct type (including enum types)
+ var.defaultValue = var.datatypeWithEnum + "()";
+ }
+ }
+ else {
+ //Handle enums
+ if(var.isEnum) {
+ setEnumVendorExtensions(var, model);
+
+ // Check for explicit default values in schema
+ // Base generator may auto-generate defaults which should be ignored for enums
+ boolean hasExplicitDefault = var.defaultValue != null
+ && !var.defaultValue.equals("\"\"")
+ && !var.defaultValue.equals("null")
+ && !var.defaultValue.equals("0"); // Base generator default for integer types
+
+ // Handle enum default values based on required status and schema default
+ if (!hasExplicitDefault) {
+ if (!var.required) {
+ // Optional enum: wrap in std::optional and default to std::nullopt
+ if (!var.dataType.startsWith("std::optional<")) {
+ var.dataType = "std::optional<" + var.datatypeWithEnum + ">";
+ var.datatypeWithEnum = var.dataType;
+ }
+ var.defaultValue = "std::nullopt";
+ // Mark that this enum is wrapped in std::optional (for template conditionals)
+ var.vendorExtensions.put("isOptionalEnum", true);
+ } else {
+ // Required enum: Use first value which is always UNSPECIFIED (added by setEnumVendorExtensions)
+ if (var.allowableValues != null && var.allowableValues.containsKey("values")) {
+ @SuppressWarnings("unchecked")
+ List values = (List) var.allowableValues.get("values");
+ if (values != null && !values.isEmpty()) {
+ // First value is always safe (UNSPECIFIED) after setEnumVendorExtensions
+ String enumIdentifier = values.get(0).toString();
+ var.defaultValue = var.datatypeWithEnum + "::" + enumIdentifier;
+ }
+ }
+ }
+ } else {
+ // Explicit default value from schema - use qualified enum name
+ String defaultVal = var.defaultValue.replaceAll("\"", "");
+ // Convert numeric enum values (e.g., "100" -> "_100")
+ if (defaultVal.matches("^[0-9]+$")) {
+ defaultVal = "_" + defaultVal;
+ }
+ // Convert to UPPERCASE to match enum definition
+ defaultVal = defaultVal.toUpperCase(Locale.ROOT);
+ var.defaultValue = var.datatypeWithEnum + "::" + defaultVal;
+ }
+ }
+ // Handle Date types
+ if ("Date".equals(var.baseType) || "DateTime".equals(var.baseType)) {
+ var.dataType = "std::string";
+ var.datatypeWithEnum = "std::string";
+ }
+ // Set default values
+ if (var.defaultValue == null) {
+ var.defaultValue = getDefaultValueForType(var.dataType, var.isNullable);
+ }
+ // Set primitive flags for mustache
+ if (var.dataType.equals("int") || var.dataType.equals("int32_t") || var.dataType.equals("int64_t")
+ || var.dataType.equals("integer")) {
+ var.vendorExtensions.put("isInt", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if (var.dataType.equals("long")) {
+ var.vendorExtensions.put("isLong", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if (var.dataType.equals("float")) {
+ var.vendorExtensions.put("isFloat", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if (var.dataType.equals("double") || var.dataType.equals("number")) {
+ var.vendorExtensions.put("isDouble", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if (var.dataType.equals("bool") || var.dataType.equals("boolean")) {
+ var.vendorExtensions.put("isBool", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if (var.dataType.equals("std::string") || var.dataType.equals("string")) {
+ var.vendorExtensions.put("isString", true);
+ var.vendorExtensions.put("isPrimitive", true);
+ }
+ if(var.isModel) {
+ var.vendorExtensions.put("isModel", true);
+ if(var.defaultValue != null && var.defaultValue.startsWith("std::make_shared<")) {
+ var.defaultValue = var.datatypeWithEnum + "()" ;
+ }
+ }
+ }
+ //Handle getters and setters
+ if (var.getter != null) {
+ var.vendorExtensions.put("getter", var.getter);
+ var.vendorExtensions.put("getterType", var.datatypeWithEnum);
+ }
+ if (var.setter != null) {
+ var.vendorExtensions.put("setter", var.setter);
+ var.vendorExtensions.put("setterType", var.datatypeWithEnum);
+ }
+ }
+
+ /**
+ * Generates appropriate default values for C++ types based on the data type and nullability.
+ *
+ * @param dataType the C++ data type
+ * @param isNullable whether the type is nullable
+ * @return the appropriate default value string
+ */
+ private String getDefaultValueForType(String dataType, boolean isNullable) {
+ if (isNullable) {
+ return "std::nullopt";
+ }
+
+ if (dataType.equals("std::string")) {
+ return "\"\"";
+ } else if (dataType.equals("bool")) {
+ return "false";
+ } else if (dataType.equals("int") || dataType.equals("int32_t") || dataType.equals("int64_t")) {
+ return "0";
+ } else if (dataType.equals("long")) {
+ return "0L";
+ } else if (dataType.equals("float")) {
+ return "0.0f";
+ } else if (dataType.equals("double")) {
+ return "0.0";
+ } else if (dataType.startsWith("std::vector<")) {
+ return dataType + "()"; // Explicit construction
+ } else if (dataType.startsWith("std::map<")) {
+ return dataType + "()"; // Explicit construction
+ } else if (dataType.startsWith("std::optional<")) {
+ return "std::nullopt";
+ } else if (dataType.startsWith("std::shared_ptr<")) {
+ return "nullptr"; // Shared pointers can be null
+ }
+
+ // For model types, don't set default - let default constructor handle it
+ // Return null so the template skips this in the initializer list
+ return null;
+ }
+
+ /**
+ * Converts an OpenAPI model name to a C++ class name.
+ * Handles inline model prefixes and converts to PascalCase.
+ *
+ * @param name the original model name
+ * @return the C++ class name
+ */
+ @Override
+ public String toModelName(String name) {
+ if (name == null) {
+ return "Model";
+ }
+
+ // Handle generic object names that need better naming
+ if (name.equals("object") || name.startsWith("object_") || name.contains("inline_object")) {
+ // Try to extract a number suffix for uniqueness
+ if (name.contains("_")) {
+ String[] parts = name.split("_");
+ if (parts.length > 1 && parts[parts.length - 1].matches("\\d+")) {
+ return "InlineModel" + parts[parts.length - 1];
+ }
+ }
+ return "InlineModel";
+ }
+
+ // Handle inline model names starting with underscore
+ if (name.startsWith("_")) {
+ name = name.substring(1);
+ }
+
+ // Handle inline model names starting with "inline_"
+ if (name.startsWith("inline_")) {
+ name = name.substring(7); // Remove "inline_" prefix
+ }
+
+ // Convert to PascalCase
+ return toPascalCase(sanitizeName(name));
+ }
+
+ /**
+ * Converts an API name to a C++ API class name.
+ * Handles default naming and path-based names.
+ *
+ * @param name the original API name
+ * @return the C++ API class name
+ */
+ @Override
+ public String toApiName(String name) {
+ String apiName = name;
+ if ("Default".equals(apiName) || apiName == null || apiName.isEmpty()) {
+ apiName = addProjectOrDefaultName(apiName, "Default");
+ } else {
+ String[] parts = apiName.split("[/]");
+ if (parts.length > 1) {
+ apiName = toPascalCase(parts[1]);
+ } else {
+ apiName = toPascalCase(apiName);
+ }
+ }
+ return apiName;
+ }
+
+ /**
+ * Converts an API name to its corresponding filename.
+ *
+ * @param name the API name
+ * @return the filename without extension
+ */
+ @Override
+ public String toApiFilename(String name) {
+ String apiFileName = name;
+ if ("Default".equals(apiFileName) || apiFileName == null || apiFileName.isEmpty()) {
+ apiFileName = addProjectOrDefaultName(apiFileName, "Default");
+ } else {
+ apiFileName = toPascalCase(name);
+ }
+ return apiFileName + toPascalCase(API_SUFFIX);
+ }
+
+ @Override
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public String getTypeDeclaration(Schema p) {
+ if (p.getOneOf() != null && !p.getOneOf().isEmpty()) {
+ // For oneOf, map to std::variant of possible types
+ StringBuilder variant = new StringBuilder("std::variant<");
+ List schemas = p.getOneOf();
+ for (int i = 0; i < schemas.size(); i++) {
+ if (i > 0) variant.append(", ");
+ variant.append(getTypeDeclaration(schemas.get(i)));
+ }
+ variant.append(">");
+ return variant.toString();
+ } else if (p.getAnyOf() != null && !p.getAnyOf().isEmpty()) {
+ // For anyOf, also use std::variant to handle multiple possible types
+ StringBuilder variant = new StringBuilder("std::variant<");
+ List schemas = p.getAnyOf();
+ for (int i = 0; i < schemas.size(); i++) {
+ if (i > 0) variant.append(", ");
+ variant.append(getTypeDeclaration(schemas.get(i)));
+ }
+ variant.append(">");
+ return variant.toString();
+ } else if (ModelUtils.isArraySchema(p)) {
+ // Handle arrays with full type declaration
+ String inner = getTypeDeclaration(Objects.requireNonNull(ModelUtils.getSchemaItems(p)));
+ return "std::vector<" + inner + ">";
+ } else if (ModelUtils.isMapSchema(p)) {
+ // Handle maps with full type declaration
+ String inner = getTypeDeclaration(ModelUtils.getAdditionalProperties(p));
+ return "std::map";
+ } else if (ModelUtils.isComposedSchema(p) && p.getAllOf() != null) {
+ // allOf is handled by composition in fromModel
+ return super.getTypeDeclaration(p);
+ }
+ String typeDecl = super.getTypeDeclaration(p);
+
+ if (typeDecl != null && typeDecl.contains("oas_any_type_not_mapped")) {
+ typeDecl = typeDecl.replace("oas_any_type_not_mapped", "nlohmann::json");
+ }
+ return typeDecl;
+ }
+
+ @Override
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public CodegenModel fromModel(String name, Schema schema) {
+ // Use schema title if available for better naming
+ String modelName = name;
+ if (schema.getTitle() != null && !schema.getTitle().isEmpty()) {
+ modelName = schema.getTitle();
+ } else if (name != null && (name.startsWith("object") || name.contains("inline_object"))) {
+ // Try to generate a better name for inline objects
+ }
+
+ CodegenModel model = super.fromModel(modelName, schema);
+
+ // Ensure the model name is properly set
+ if (model != null) {
+ model.name = toModelName(modelName);
+ model.classname = model.name;
+
+ if (schema.getAllOf() != null && !schema.getAllOf().isEmpty() && this.openAPI != null) {
+ int refCount = 0;
+ String parentRef = null;
+ Set parentPropertyNames = new HashSet<>();
+ List mergedVars = new ArrayList<>();
+
+ for (Schema allOfSchema : (List) schema.getAllOf()) {
+ if (allOfSchema.get$ref() != null) {
+ refCount++;
+ parentRef = ModelUtils.getSimpleRef(allOfSchema.get$ref());
+
+ // Get parent properties to filter them out from child
+ Schema parentSchema = ModelUtils.getReferencedSchema(openAPI, allOfSchema);
+ CodegenModel parentModel = super.fromModel(parentRef, parentSchema);
+ if (parentModel != null && parentModel.vars != null) {
+ for (CodegenProperty var : parentModel.vars) {
+ parentPropertyNames.add(var.name);
+ }
+ // Only merge if we have multiple parents
+ if (schema.getAllOf().size() > 1) {
+ mergedVars.addAll(parentModel.vars);
+ }
+ }
+ } else {
+ // Inline schema: merge its properties
+ CodegenModel inlineModel = super.fromModel(modelName + "_inline", allOfSchema);
+ if (inlineModel != null && inlineModel.vars != null) {
+ mergedVars.addAll(inlineModel.vars);
+ }
+ }
+ }
+
+ // Case 1: Single parent with possible inline schema (inheritance)
+ if (refCount == 1 && parentRef != null) {
+ model.parent = toModelName(parentRef);
+ model.parentModel = null; // Don't need to store parent model
+
+ // Filter out inherited properties from child
+ List childOnlyVars = new ArrayList<>();
+ for (CodegenProperty var : model.vars) {
+ if (!parentPropertyNames.contains(var.name)) {
+ childOnlyVars.add(var);
+ }
+ }
+ model.vars = childOnlyVars;
+ }
+ // Case 2: Multiple parents or complex composition (merge all)
+ else if (refCount > 1 || mergedVars.size() > 0) {
+ // Avoid duplicates
+ Map uniqueVars = new LinkedHashMap<>();
+ for (CodegenProperty var : mergedVars) {
+ uniqueVars.put(var.name, var);
+ }
+ for (CodegenProperty var : model.vars) {
+ uniqueVars.put(var.name, var);
+ }
+ model.vars = new ArrayList<>(uniqueVars.values());
+ model.parent = null; // No inheritance for multiple parents
+ }
+ }
+
+ // Handle oneOf/anyOf schemas - generate std::variant type alias instead of class
+ if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
+ model.vendorExtensions.put("isOneOfSchema", true);
+ model.vendorExtensions.put("hasVariant", true);
+ List variantTypes = new ArrayList<>();
+ for (Schema oneOfSchema : (List) schema.getOneOf()) {
+ String typeName = getTypeDeclaration(oneOfSchema);
+ variantTypes.add(typeName);
+ }
+ model.vendorExtensions.put("oneOfTypes", variantTypes);
+
+ // Extract discriminator if present
+ if (schema.getDiscriminator() != null) {
+ model.vendorExtensions.put("discriminatorProperty", schema.getDiscriminator().getPropertyName());
+ if (schema.getDiscriminator().getMapping() != null) {
+ // Convert Map to List for Mustache iteration
+ List> mappingList = new ArrayList<>();
+ for (Map.Entry entry : schema.getDiscriminator().getMapping().entrySet()) {
+ Map mappingEntry = new HashMap<>();
+ mappingEntry.put("value", entry.getKey());
+ // Extract type name from schema reference
+ String schemaRef = entry.getValue();
+ String typeName = schemaRef.substring(schemaRef.lastIndexOf('/') + 1);
+ mappingEntry.put("key", typeName);
+ mappingList.add(mappingEntry);
+ }
+ model.vendorExtensions.put("discriminatorMapping", mappingList);
+ }
+ }
+ } else if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
+ model.vendorExtensions.put("isAnyOfSchema", true);
+ model.vendorExtensions.put("hasVariant", true);
+ List variantTypes = new ArrayList<>();
+ for (Schema anyOfSchema : (List) schema.getAnyOf()) {
+ String typeName = getTypeDeclaration(anyOfSchema);
+ variantTypes.add(typeName);
+ }
+ model.vendorExtensions.put("anyOfTypes", variantTypes);
+
+ // Extract discriminator if present
+ if (schema.getDiscriminator() != null) {
+ model.vendorExtensions.put("discriminatorProperty", schema.getDiscriminator().getPropertyName());
+ if (schema.getDiscriminator().getMapping() != null) {
+ // Convert Map to List for Mustache iteration
+ List> mappingList = new ArrayList<>();
+ for (Map.Entry entry : schema.getDiscriminator().getMapping().entrySet()) {
+ Map mappingEntry = new HashMap<>();
+ mappingEntry.put("value", entry.getKey());
+ // Extract type name from schema reference
+ String schemaRef = entry.getValue();
+ String typeName = schemaRef.substring(schemaRef.lastIndexOf('/') + 1);
+ mappingEntry.put("key", typeName);
+ mappingList.add(mappingEntry);
+ }
+ model.vendorExtensions.put("discriminatorMapping", mappingList);
+ }
+ }
+ }
+ }
+ return model;
+ }
+
+ @Override
+ public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
+ super.postProcessModelProperty(model, property);
+
+ // Convert Date/DateTime types to std::string
+ if ("Date".equals(property.baseType) || "DateTime".equals(property.baseType)) {
+ property.dataType = "std::string";
+ property.datatypeWithEnum = "std::string";
+ }
+
+ // Set vendor extensions for array properties
+ if (property.isArray) {
+ property.vendorExtensions.put("isArray", true);
+
+ // Check if it's an array of enums
+ if (property.items != null && property.items.isEnum) {
+ property.vendorExtensions.put("isArrayOfEnum", true);
+
+ if (property.datatypeWithEnum != null && property.datatypeWithEnum.contains("std::")) {
+ String enumName = property.items.datatypeWithEnum;
+ if (enumName != null && !enumName.contains("::")) {
+ // Replace std::EnumName with ClassName::EnumName
+ property.datatypeWithEnum = property.datatypeWithEnum.replace(
+ "std::" + enumName,
+ model.classname + "::" + enumName
+ );
+ property.dataType = property.datatypeWithEnum;
+ }
+ }
+ } else {
+ property.vendorExtensions.put("isArrayOfEnum", false);
+ }
+ } else {
+ property.vendorExtensions.put("isArray", false);
+ property.vendorExtensions.put("isArrayOfEnum", false);
+ }
+
+ // Set vendor extensions for map properties
+ if (property.isMap) {
+ property.vendorExtensions.put("isContainer", true);
+ } else if (property.isArray) {
+ property.vendorExtensions.put("isContainer", true);
+ } else {
+ property.vendorExtensions.put("isContainer", false);
+ }
+
+ // Set vendor extensions for enum properties
+ if (property.isEnum) {
+ property.vendorExtensions.put("isEnum", true);
+ // Convert numeric enum values to have underscore prefix
+ convertNumericEnumValues(property);
+ } else {
+ property.vendorExtensions.put("isEnum", false);
+ }
+
+ // Set vendor extension for nullable/optional properties
+ if (property.isNullable) {
+ property.vendorExtensions.put("isOptional", true);
+ } else {
+ property.vendorExtensions.put("isOptional", false);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public String toDefaultValue(Schema p) {
+ // Handle byte arrays separately - they are std::vector, not string
+ if (ModelUtils.isByteArraySchema(p)) {
+ return null; // Will be initialized as empty vector in constructor
+ }
+
+ if (ModelUtils.isStringSchema(p) || ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
+ // For enum types, don't return empty string - it will be handled in processModelVariable
+ if (p.getEnum() != null && !p.getEnum().isEmpty()) {
+ return null; // Let processModelVariable handle enum defaults
+ }
+ return p.getDefault() != null ? "\"" + p.getDefault().toString() + "\"" : "\"\"";
+ }
+ if (ModelUtils.isBooleanSchema(p)) {
+ return p.getDefault() != null ? p.getDefault().toString() : "false";
+ }
+ if (ModelUtils.isIntegerSchema(p)) {
+ return p.getDefault() != null ? p.getDefault().toString() : "0";
+ }
+ if (ModelUtils.isNumberSchema(p)) {
+ if (ModelUtils.isFloatSchema(p)) {
+ return p.getDefault() != null ? p.getDefault().toString() + "f" : "0.0f";
+ }
+ return p.getDefault() != null ? p.getDefault().toString() : "0.0";
+ }
+ if (ModelUtils.isArraySchema(p)) {
+ // For arrays, don't generate default value here - let postProcessModelProperty handle it
+ // This avoids incorrect enum type qualification issues
+ return null;
+ }
+ if (ModelUtils.isMapSchema(p)) {
+ String inner = getTypeDeclaration(ModelUtils.getAdditionalProperties(p));
+ return "std::map()";
+ }
+ if (p.get$ref() != null && !p.get$ref().isEmpty()) {
+ // Model references as shared pointers - create instance with make_shared
+ return "std::make_shared<" + toModelName(ModelUtils.getSimpleRef(p.get$ref())) + ">()";
+ }
+ // Unknown type - skip initialization, let default constructor handle it
+ return null;
+ }
+
+ /**
+ * Returns the directory path for generated model files.
+ *
+ * @return the model file directory path
+ */
+ @Override
+ public String modelFileFolder() {
+ return (outputFolder + "/models").replace("/", File.separator);
+ }
+
+ /**
+ * Returns the directory path for generated API files.
+ *
+ * @return the API file directory path
+ */
+ @Override
+ public String apiFileFolder() {
+ return (outputFolder + "/api").replace("/", File.separator);
+ }
+
+
+ private Map stripPathFromClassName(String path) {
+ Object stripPathValue = additionalProperties.get("stripPathFromClassName");
+ Boolean isStripPath = false;
+ if (stripPathValue instanceof Boolean) {
+ isStripPath = (Boolean) stripPathValue;
+ } else if (stripPathValue instanceof String) {
+ isStripPath = Boolean.parseBoolean((String) stripPathValue);
+ }
+ Map result = new HashMap<>();
+ if (isStripPath != null && isStripPath) {
+ if (path == null || path.isEmpty()) {
+ result.put("namespace", "");
+ result.put("className", "DefaultClass");
+ return result;
+ }
+ if (path.contains("_error") || path.contains("_Error") || path.contains("error_")
+ || path.contains("Error_")) {
+ path = path.replace("_error", "").replace("_Error", "").replace("error_", "").replace("Error_", "");
+ }
+ // Remove leading underscores
+ String clean = path.replaceAll("^_+", "");
+
+ // If the path is now empty or whitespace only, return default
+ if (clean.isEmpty() || clean.trim().isEmpty()) {
+ result.put("namespace", "");
+ result.put("className", "DefaultClass");
+ return result;
+ }
+
+ // Split by "/", "_", "-", " ", camelCase boundary
+ String[] segments = clean.split("(?<=[a-z0-9])(?=[A-Z])|[^a-zA-Z0-9]");
+ List parts = new ArrayList<>();
+ for (String seg : segments) {
+ if (seg != null && !seg.trim().isEmpty()) {
+ parts.add(seg.trim());
+ }
+ }
+
+ if (parts.size() == 0) {
+ result.put("namespace", "");
+ result.put("className", "DefaultClass");
+ return result;
+ }
+ if (clean.toLowerCase(Locale.ROOT).contains("error")) {
+ result.put("namespace", "error");
+ result.put("className", toPascalCase(clean));
+ return result;
+ }
+
+ // ClassName is the rest, joined and PascalCased
+ StringBuilder classNameBuilder = new StringBuilder();
+ String namespace = "";
+ if (parts.size() > 1) {
+ // Use first part as namespace only for API class names, not models
+ namespace = parts.get(0).toLowerCase(Locale.ROOT);
+ for (int i = 1; i < parts.size(); i++) {
+ String pascalPart = toPascalCase(parts.get(i));
+ if (pascalPart != null && !pascalPart.isEmpty()) {
+ classNameBuilder.append(pascalPart);
+ }
+ }
+ } else {
+ // Single part: use as class name
+ String pascalPart = toPascalCase(parts.get(0));
+ if (pascalPart != null && !pascalPart.isEmpty()) {
+ classNameBuilder.append(pascalPart);
+ }
+ }
+
+ // Ensure we have a valid class name
+ String finalClassName = classNameBuilder.toString();
+ if (finalClassName.isEmpty()) {
+ finalClassName = "DefaultClass";
+ }
+
+ result.put("namespace", namespace);
+ result.put("className", toPascalCase(finalClassName));
+ return result;
+ } else {
+ result.put("namespace", "");
+ result.put("className", toPascalCase(path));
+ return result;
+ }
+
+ }
+
+ /**
+ * Adds project name to a class name or returns default name if project name is not set.
+ *
+ * @param name the base name
+ * @param defaultName the default name to use if input is null/empty
+ * @return the project-prefixed name or default name
+ */
+ public String addProjectOrDefaultName(String name, String defaultName) {
+ String apiName = name;
+ if (name == null || name.isEmpty()) {
+ apiName = defaultName;
+ }
+ String projectName = (String) additionalProperties.get(PROJECT_NAME);
+ if (projectName != null && !projectName.isEmpty()) {
+ apiName = toPascalCase(projectName) + API_SUFFIX;
+ }
+ return apiName;
+ }
+
+ /**
+ * Converts a model namespace string to proper format.
+ *
+ * @param modelNamespace the namespace to convert
+ * @return the formatted model namespace
+ */
+ public String toModelNamespace(String modelNamespace) {
+ if (modelNamespace == null || modelNamespace.isEmpty()) {
+ return toPascalCase(MODEL_SUFFIX);
+ }
+ return modelNamespace;
+ }
+
+ /**
+ * Converts a string to PascalCase formatting with consistent handling.
+ * Handles hyphens, underscores, and camelCase boundaries.
+ * Used for class names, type names, and enum names.
+ *
+ * @param name the string to convert
+ * @return the PascalCase formatted string
+ */
+ public String toPascalCase(String name) {
+ if (name == null || name.isEmpty()) {
+ return "DefaultClass";
+ }
+
+ // First, insert spaces before uppercase letters to split camelCase
+ String normalized = name.replaceAll("([a-z])([A-Z])", "$1 $2");
+ // Split on delimiters (hyphens, underscores, spaces)
+ String[] parts = normalized.split("[_\\-\\s]+");
+ StringBuilder result = new StringBuilder();
+ for (String part : parts) {
+ if (part != null && !part.isEmpty()) {
+ // Check if the part starts with a digit
+ if (Character.isDigit(part.charAt(0))) {
+ // For parts starting with digit (e.g., "200response"), find where letters start
+ int firstLetterIndex = 0;
+ while (firstLetterIndex < part.length() && Character.isDigit(part.charAt(firstLetterIndex))) {
+ firstLetterIndex++;
+ }
+ // Keep digits as-is
+ result.append(part.substring(0, firstLetterIndex));
+ // Capitalize first letter after digits, lowercase the rest
+ if (firstLetterIndex < part.length()) {
+ result.append(part.substring(firstLetterIndex, firstLetterIndex + 1).toUpperCase(Locale.ROOT));
+ if (firstLetterIndex + 1 < part.length()) {
+ result.append(part.substring(firstLetterIndex + 1).toLowerCase(Locale.ROOT));
+ }
+ }
+ } else {
+ // Check if part is already in PascalCase or camelCase (mixed case)
+ boolean hasMixedCase = !part.equals(part.toLowerCase(Locale.ROOT)) &&
+ !part.equals(part.toUpperCase(Locale.ROOT));
+
+ if (hasMixedCase && Character.isUpperCase(part.charAt(0))) {
+ // Already PascalCase, keep as-is
+ result.append(part);
+ } else {
+ // Convert to PascalCase: Capitalize first letter, lowercase the rest
+ result.append(part.substring(0, 1).toUpperCase(Locale.ROOT));
+ if (part.length() > 1) {
+ result.append(part.substring(1).toLowerCase(Locale.ROOT));
+ }
+ }
+ }
+ }
+ }
+ return result.length() > 0 ? result.toString() : "DefaultClass";
+ }
+
+ /**
+ * Converts a string to camelCase formatting.
+ * Used for variable names and method names.
+ *
+ * @param name the string to convert
+ * @return the camelCase formatted string
+ */
+ public String toCamelCase(String name) {
+ if (name == null || name.isEmpty()) {
+ return "defaultVariable";
+ }
+ String result = camelize(name, true);
+ return result.isEmpty() ? "defaultVariable" : result;
+ }
+
+ /**
+ * Converts a string to camelCase or PascalCase with proper case handling.
+ * Handles uppercase words like "POST" -> "Post" and "GET" -> "Get".
+ *
+ * @param name the string to convert
+ * @param lowercaseFirstLetter whether to lowercase the first letter (camelCase)
+ * @return the camelized string
+ */
+ public String camelize(String name, boolean lowercaseFirstLetter) {
+ if (name == null || name.isEmpty()) {
+ return "";
+ }
+
+ // First, try the framework utility
+ String result;
+ if (lowercaseFirstLetter) {
+ result = org.openapitools.codegen.utils.StringUtils.camelize(name, CamelizeOption.LOWERCASE_FIRST_LETTER);
+ } else {
+ result = org.openapitools.codegen.utils.StringUtils.camelize(name);
+ }
+
+ // If the result is the same as input and input is all uppercase,
+ if (result.equals(name) && name.equals(name.toUpperCase(Locale.ROOT)) && name.matches("[A-Z]+")) {
+ // Handle all-caps words manually
+ result = name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).toLowerCase(Locale.ROOT);
+ if (lowercaseFirstLetter) {
+ result = result.substring(0, 1).toLowerCase(Locale.ROOT) + result.substring(1);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Generates a handler function name from HTTP method and path.
+ *
+ * @param httpMethod the HTTP method (GET, POST, etc.)
+ * @param path the API path
+ * @return the generated handler function name
+ */
+ public String toHandlerFunctionName(String httpMethod, String path, Boolean prefix) {
+ String method = toPascalCase(httpMethod);
+ String className = stripPathFromClassName(path).get("className");
+ className = className.replaceAll("[^A-Za-z0-9]","");
+ if(prefix) {
+ return "handle" + method + "For" + toPascalCase(className);
+ } else {
+ return toPascalCase(className) + method;
+ }
+ }
+
+ /**
+ * Generates a handler function request type from the API Path
+ *
+ * @param path the API path
+ * @return the generated handler function request
+ */
+ public String toHandlerFunctionRequest(String path, String method) {
+ String className = stripPathFromClassName(path).get("className");
+ className = className.replaceAll("[^A-Za-z0-9]","");
+ return toPascalCase(className + toPascalCase(method) + "Request");
+ }
+
+
+ /**
+ * Generates a handler function response type from the API path.
+ *
+ * @param path the API path
+ * @return the generated handler function response
+ */
+ public String toHandlerFunctionResponse(String path, String method) {
+ String className = stripPathFromClassName(path).get("className");
+ className = className.replaceAll("[^A-Za-z0-9]","");
+ return toPascalCase(className + toPascalCase(method) + "Response");
+ }
+
+ /**
+ * Processes command line options and additional properties.
+ * Sets up default values for project configuration.
+ */
+ @Override
+ public void processOpts() {
+ super.processOpts();
+
+ // Set default model package if not provided
+ if (modelPackage == null || modelPackage.isEmpty()) {
+ modelPackage = DEFAULT_PROJECT_NAME;
+ }
+
+ // Get project name from additional properties
+ String projectName = DEFAULT_PROJECT_NAME;
+ if (additionalProperties.containsKey(PROJECT_NAME)) {
+ projectName = (String) additionalProperties.get(PROJECT_NAME);
+ }
+
+ // Make package name available to templates
+ additionalProperties.put("packageName", modelPackage);
+ additionalProperties.put("projectName", projectName);
+ String cmakeProjectName = (String) additionalProperties.get("cmakeProjectName");
+ if(cmakeProjectName == null || cmakeProjectName.isEmpty()) {
+ cmakeProjectName = (String) projectName;
+ }
+ additionalProperties.put("cmakeProjectName", StringUtils.underscore(cmakeProjectName));
+
+
+ // Set Enum namespace
+ String enumNamespace = (String) additionalProperties.get(ENUM_NAMESPACE);
+ if (enumNamespace == null || enumNamespace.isEmpty()) {
+ enumNamespace = toPascalCase(ENUM_SUFFIX);
+ }
+ additionalProperties.put(ENUM_NAMESPACE, enumNamespace);
+
+ // Set API namespace for templates (needed for AuthenticationManager and other supporting files)
+ String apiNamespaceValue = (String) additionalProperties.get(API_NAMESPACE);
+ if (apiNamespaceValue == null || apiNamespaceValue.isEmpty()) {
+ apiNamespaceValue = toPascalCase(API_SUFFIX);
+ }
+ additionalProperties.put(API_NAMESPACE, apiNamespaceValue);
+
+ // Handle addApiImplStubs option
+ if (additionalProperties.containsKey(ADD_API_IMPL_STUBS)) {
+ boolean addApiImplStubs = Boolean.parseBoolean(additionalProperties.get(ADD_API_IMPL_STUBS).toString());
+ if (addApiImplStubs) {
+ // Add API implementation stub templates
+ apiTemplateFiles.put("api-impl-header.mustache", "Impl.h");
+ apiTemplateFiles.put("api-impl.mustache", "Impl.cpp");
+ // Add main.cpp for executable
+ supportingFiles.add(new SupportingFile("main.mustache", "", "main.cpp"));
+ LOGGER.debug("API implementation stubs will be generated");
+ }
+ }
+
+ }
+
+ /**
+ * Post-processes parameters to set up C++ specific configurations.
+ * Handles the required flag and parameter type conversions.
+ *
+ * @param parameter the parameter to process
+ */
+ @Override
+ public void postProcessParameter(CodegenParameter parameter) {
+ super.postProcessParameter(parameter);
+
+ // Set vendor extensions for template usage
+ parameter.vendorExtensions.put("isRequired", parameter.required);
+ parameter.vendorExtensions.put("isOptional", !parameter.required);
+
+ // Handle non-required parameters - wrap in std::optional
+ if (!parameter.required) {
+ if (!parameter.dataType.startsWith("std::optional<")) {
+ parameter.dataType = "std::optional<" + parameter.dataType + ">";
+ }
+ }
+
+ // Extract default value from schema if available
+ if (parameter.defaultValue != null && !parameter.defaultValue.isEmpty()) {
+ // Correct nullptr default value to std::nullopt for std::optional types
+ if ("nullptr".equals(parameter.defaultValue) && parameter.dataType.startsWith("std::optional<")) {
+ parameter.defaultValue = "std::nullopt";
+ }
+ parameter.vendorExtensions.put("hasDefaultValue", true);
+ parameter.vendorExtensions.put("defaultValue", parameter.defaultValue);
+ } else {
+ parameter.vendorExtensions.put("hasDefaultValue", false);
+ }
+ }
+
+ /**
+ * Sets C++ type flags for a parameter for use in Mustache templates.
+ * Handles arrays, enums, and primitives with proper vendor extensions.
+ */
+ private void setParameterTypeFlags(CodegenParameter param) {
+ if (param == null) {
+ return;
+ }
+
+ // Set parameter style flags for serialization
+ String style = param.style != null ? param.style : "form";
+ param.vendorExtensions.put("isStyleSimple", "simple".equals(style));
+ param.vendorExtensions.put("isStyleForm", "form".equals(style));
+ param.vendorExtensions.put("isStyleSpaceDelimited", "spaceDelimited".equals(style));
+ param.vendorExtensions.put("isStylePipeDelimited", "pipeDelimited".equals(style));
+ param.vendorExtensions.put("isStyleDeepObject", "deepObject".equals(style));
+
+ // Set array flags and helper names
+ if (param.isArray) {
+ setArrayVendorExtensions(param, param.nameInPascalCase, param.baseName, null);
+ } else if (param.isEnum) {
+ // Handle enum parameters
+ param.vendorExtensions.put("isEnum", true);
+ param.vendorExtensions.put("enumType", toPascalCase(param.baseType) + "Enum");
+ param.vendorExtensions.put("enumFromStringHelper", param.nameInPascalCase + ENUM_FROM_STRING);
+ param.vendorExtensions.put("enumToStringHelper", param.nameInPascalCase + ENUM_TO_STRING);
+ } else {
+ // Set primitive type flags
+ setPrimitiveTypes(param);
+ }
+ }
+
+ /**
+ * Helper to set vendorExtensions for array properties and parameters.
+ */
+ private void setArrayVendorExtensions(Object varObj, String varName, String modelBaseName, CodegenModel model) {
+ if (varObj == null) return;
+
+ Map vendorExtensions;
+ CodegenProperty items;
+ boolean isContainer;
+
+ if (varObj instanceof CodegenProperty) {
+ CodegenProperty var = (CodegenProperty) varObj;
+ vendorExtensions = var.vendorExtensions;
+ items = var.items;
+ isContainer = var.isContainer;
+ } else if (varObj instanceof CodegenParameter) {
+ CodegenParameter var = (CodegenParameter) varObj;
+ vendorExtensions = var.vendorExtensions;
+ items = var.items;
+ isContainer = var.isContainer;
+ } else {
+ return;
+ }
+
+ vendorExtensions.put("isArray", true);
+ String itemType = (items != null && items.dataType != null) ? items.dataType : "std::string";
+ // Improved array item type naming
+ String arrayTypeName = toPascalCase(varName) + "VectorOf" + toPascalCase(itemType);
+ vendorExtensions.put("arrayTypeName", arrayTypeName);
+ vendorExtensions.put("arrayItemType", itemType);
+ if (items != null) {
+ // ALWAYS set primitive type flags for items (needed for type conversion in templates)
+ setPrimitiveTypes(items);
+
+ if (items.isEnum) {
+ // Set up enum vendor extensions for items (needed for enumCases and conversion helpers)
+ // Only call if model is available (not for parameters)
+ if (model != null) {
+ setEnumVendorExtensions(items, model);
+ }
+
+ vendorExtensions.put("isArrayOfEnum", true);
+ vendorExtensions.put("enumFromStringHelper", varName + "EnumFromString");
+ vendorExtensions.put("enumToStringHelper", varName + "EnumToString");
+ if (modelBaseName != null && !modelBaseName.isEmpty()) {
+ vendorExtensions.put("enumModelClass", modelBaseName);
+ }
+ // Remove namespace prefixes from enum datatypes in array items
+ // Enums are defined inside the class, so namespace qualifiers are not needed
+ if (items.datatypeWithEnum != null && items.datatypeWithEnum.contains("::")) {
+ // Remove any namespace prefix (like "std::" or "ModelClass::")
+ String[] parts = items.datatypeWithEnum.split("::");
+ items.datatypeWithEnum = parts[parts.length - 1];
+ }
+ // Also fix items.dataType which is used to construct parent's datatypeWithEnum for arrays
+ if (items.dataType != null && items.dataType.contains("::")) {
+ String[] parts = items.dataType.split("::");
+ items.dataType = parts[parts.length - 1];
+ }
+ } else if (items.isModel) {
+ vendorExtensions.put("isArrayOfModel", true);
+ vendorExtensions.put("vectorFromJsonHelper", varName + "VectorFromJson");
+ vendorExtensions.put("vectorToJsonHelper", varName + "VectorToJson");
+ } else if (items.isPrimitiveType) {
+ vendorExtensions.put("isArrayOfPrimitive", true);
+ vendorExtensions.put("vectorFromStringHelper", varName + "VectorFromString");
+ vendorExtensions.put("vectorToStringHelper", varName + "VectorToString");
+ } else if(items.isContainer) {
+ vendorExtensions.put("isArrayOfContainer", true);
+ vendorExtensions.put("vectorFromStringHelper", varName + "VectorFromString");
+ vendorExtensions.put("vectorToStringHelper", varName + "VectorToString");
+ } else {
+ // Default to primitive if type is unknown but items are present
+ vendorExtensions.put("isArrayOfPrimitive", true);
+ }
+ } else {
+ // If items are not defined, default to a simple array of primitives (strings)
+ vendorExtensions.put("isArrayOfPrimitive", true);
+ }
+ }
+ private void setPrimitiveTypes(Object varObj) {
+ if (varObj == null) return;
+
+ String type;
+ Map vendorExtensions;
+ boolean isObject = false;
+
+ if (varObj instanceof CodegenProperty) {
+ CodegenProperty prop = (CodegenProperty) varObj;
+ type = (prop.dataType != null) ? prop.dataType : "";
+ vendorExtensions = prop.vendorExtensions;
+ isObject = prop.isModel || (!prop.isPrimitiveType && !prop.isArray && !prop.isEnum);
+ } else if (varObj instanceof CodegenParameter) {
+ CodegenParameter param = (CodegenParameter) varObj;
+ type = (param.dataType != null) ? param.dataType : "";
+ vendorExtensions = param.vendorExtensions;
+ isObject = param.isModel || (!param.isPrimitiveType && !param.isArray && !param.isEnum);
+ } else {
+ return; // Unsupported type
+ }
+
+ // Extract inner type from containers like std::optional, std::shared_ptr
+ String innerType = type;
+ if (type.contains("<")) {
+ int startIdx = type.indexOf('<') + 1;
+ int endIdx = type.lastIndexOf('>');
+ if (startIdx > 0 && endIdx > startIdx) {
+ innerType = type.substring(startIdx, endIdx).trim();
+ }
+ }
+
+ // Normalize some aliases
+ if ("string".equals(innerType)) innerType = "std::string";
+ if ("integer".equals(innerType)) innerType = "int";
+ if ("number".equals(innerType)) innerType = "double"; // treat generic number as double
+
+ boolean isLong = innerType.equals("long") || innerType.equals("int64_t");
+ boolean isFloat = innerType.equals("float");
+ boolean isString = innerType.equals("std::string") || innerType.equals("string");
+ boolean isInt = innerType.equals("int") || innerType.equals("int32_t") || innerType.equals("int64_t") || innerType.equals("integer");
+ boolean isBool = innerType.equals("bool") || innerType.equals("boolean");
+ boolean isDouble = innerType.equals("double") || innerType.equals("number");
+
+ // Handle overlaps: int64_t should be treated as long, not int
+ if (innerType.equals("int64_t")) {
+ isInt = false;
+ isLong = true;
+ }
+
+ boolean isPrimitive = isInt || isLong || isFloat || isBool || isDouble || isString;
+
+ vendorExtensions.put("isObject", isObject);
+ vendorExtensions.put("isString", isString);
+ vendorExtensions.put("isInt", isInt);
+ vendorExtensions.put("isLong", isLong);
+ vendorExtensions.put("isFloat", isFloat);
+ vendorExtensions.put("isBool", isBool);
+ vendorExtensions.put("isDouble", isDouble);
+ vendorExtensions.put("isPrimitive", isPrimitive);
+ }
+
+ /**
+ * Converts numeric enum values to valid C++ enum names by prefixing with underscore.
+ * This is needed because C++ enum identifiers cannot start with digits.
+ * This method processes enum values in a CodegenProperty and prefixes any purely numeric
+ * enum values with an underscore. This is necessary because in C++, enum identifiers cannot
+ * start with a digit, so numeric enum values must be transformed to valid C++ identifiers.
+ *
+ * For example:
+ * - "200" becomes "_200"
+ * - "404" becomes "_404"
+ * - "OK" remains "OK"
+ *
+ * The method retrieves enum values from either the property's {@code _enum} field or from
+ * the {@code allowableValues} map, then updates all three locations with the converted values
+ * to maintain consistency across the property's internal state.
+ *
+ * @param property the CodegenProperty containing enum values to convert
+ */
+ private void convertNumericEnumValues(CodegenProperty property) {
+ // Get enum values from property._enum or allowableValues
+ List> enumValues = property._enum;
+ if ((enumValues == null || enumValues.isEmpty()) && property.allowableValues != null) {
+ Object valuesObj = property.allowableValues.get("values");
+ if (valuesObj instanceof List) {
+ enumValues = (List>) valuesObj;
+ }
+ }
+
+ if (enumValues != null && !enumValues.isEmpty()) {
+ List convertedValues = new ArrayList<>();
+ for (Object enumVal : enumValues) {
+ String enumValStr = enumVal != null ? enumVal.toString() : "";
+ String convertedVal = enumValStr;
+ // Convert numeric values to have underscore prefix
+ if (enumValStr.matches("^[0-9]+$")) {
+ convertedVal = "_" + enumValStr;
+ }
+ // Convert to UPPERCASE for C++ enum standards (clang style)
+ convertedVal = convertedVal.toUpperCase(Locale.ROOT);
+ convertedValues.add(convertedVal);
+ }
+ // Update the property with converted values
+ property._enum = convertedValues;
+ property.vendorExtensions.put("values", convertedValues);
+ if (property.allowableValues != null) {
+ property.allowableValues.put("values", convertedValues);
+ }
+ }
+ }
+
+ /**
+ * Helper to set vendorExtensions for enum properties and parameters.
+ * Handles enum value conversion and helper function naming.
+ */
+ private void setEnumVendorExtensions(Object varObj, CodegenModel model) {
+ if (varObj == null) {
+ return;
+ }
+
+ CodegenProperty var;
+ if (varObj instanceof CodegenProperty) {
+ var = (CodegenProperty) varObj;
+ } else {
+ return;
+ }
+
+ var.vendorExtensions.put("isEnum", true);
+ var.vendorExtensions.put("enumType", toPascalCase(var.baseType) + "Enum");
+
+ // Use SHORT enum name that will be scoped to the class
+ // e.g., "CardTypeEnum" not "CreditCardCardTypeEnum"
+ // Use centralized toPascalCase for consistent naming
+ String shortEnumName = toPascalCase(var.baseName) + "Enum";
+ var.vendorExtensions.put("enumName", shortEnumName);
+ var.enumName = shortEnumName; // Also set the direct property
+ var.vendorExtensions.put("enumToStringHelper", var.name + ENUM_TO_STRING);
+
+ // Convert numeric enum values to valid C++ enum names
+ List convertedValues = new ArrayList<>();
+
+ // Try to get enum values from var._enum, allowableValues, or var.allowableValues
+ List enumValues = var._enum;
+ if ((enumValues == null || enumValues.isEmpty()) && var.allowableValues != null) {
+ enumValues = new ArrayList<>();
+ for (Object val : var.allowableValues.values()) {
+ enumValues.add(val != null ? val.toString() : "");
+ }
+ }
+
+ LOGGER.debug("Processing enum for variable {}: isEnum={}, _enum={}, allowableValues={}",
+ var.name, var.isEnum, var._enum, var.allowableValues);
+
+ // Check if enum has UNKNOWN/UNSPECIFIED value, if not add it at the beginning
+ boolean hasUnknownValue = false;
+ if (enumValues != null && !enumValues.isEmpty()) {
+ for (String enumVal : enumValues) {
+ String upperVal = enumVal.toUpperCase(Locale.ROOT);
+ if (upperVal.equals("UNKNOWN") || upperVal.equals("UNSPECIFIED") ||
+ upperVal.equals("NONE") || upperVal.equals("UNDEFINED")) {
+ hasUnknownValue = true;
+ break;
+ }
+ }
+ }
+
+ if (!hasUnknownValue) {
+ if (enumValues == null) {
+ enumValues = new ArrayList<>();
+ }
+ enumValues.add(0, "UNSPECIFIED");
+ LOGGER.debug("Added UNSPECIFIED value to enum {} (needed for safe initialization)", var.name);
+ }
+
+ if (enumValues != null && !enumValues.isEmpty()) {
+ for (String enumVal : enumValues) {
+ // Convert numeric values to words or keep string values as-is
+ String convertedVal = enumVal;
+ if (enumVal.matches("^[0-9]+$")) {
+ // Convert number to enum name
+ convertedVal = "_" + enumVal; // Prefix with underscore for numeric values
+ }
+ convertedValues.add(convertedVal);
+ }
+ } else {
+ LOGGER.warn("No enum values found for variable {} in model {}", var.name, model.classname);
+ }
+
+ var.vendorExtensions.put("values", convertedValues);
+ var._enum = convertedValues; // Also set it on the var itself for Mustache access
+ var.allowableValues.put("values", convertedValues); // Also update allowableValues for template
+
+ // Create enumCases for use in helper functions
+ List> enumCases = new ArrayList<>();
+ // Iterate through original enum values to maintain mapping between C++ identifiers and original JSON values
+ if (enumValues != null) {
+ for (int i = 0; i < enumValues.size(); i++) {
+ String originalVal = enumValues.get(i); // Original value (e.g., "200")
+ String convertedVal = convertedValues.get(i); // Converted C++ identifier (e.g., "_200")
+ Map caseMap = new HashMap<>();
+ caseMap.put("name", convertedVal); // C++ enum identifier
+ caseMap.put("value", originalVal); // Original JSON value
+ caseMap.put("enumName", shortEnumName); // Use short name
+ enumCases.add(caseMap);
+ }
+ }
+ var.vendorExtensions.put("enumCases", enumCases);
+
+ LOGGER.debug("Set enum values for {}: {}", var.name, convertedValues);
+ var.vendorExtensions.put("enumFromStringHelper", var.name + ENUM_FROM_STRING);
+ // Use the short enum name (e.g., "CardTypeEnum") for the datatype
+ // When used in containers, it will be scoped to the class (e.g., "CreditCard::CardTypeEnum")
+ var.vendorExtensions.put("shortEnumName", shortEnumName);
+ var.datatypeWithEnum = shortEnumName;
+ }
+
+ /**
+ * Process security requirements for an operation.
+ * Adds vendor extensions for different authentication types.
+ */
+ private void processSecurityRequirements(CodegenOperation op) {
+ if (op.authMethods == null || op.authMethods.isEmpty()) {
+ op.vendorExtensions.put("hasAuth", false);
+ return;
+ }
+
+ op.vendorExtensions.put("hasAuth", true);
+
+ List> apiKeyAuth = new ArrayList<>();
+ List> bearerAuth = new ArrayList<>();
+ List> basicAuth = new ArrayList<>();
+ List> oauth2Auth = new ArrayList<>();
+
+ for (org.openapitools.codegen.CodegenSecurity auth : op.authMethods) {
+ Map authInfo = new HashMap<>();
+ authInfo.put("name", auth.name);
+ authInfo.put("keyParamName", auth.keyParamName);
+
+ if (auth.isApiKey) {
+ authInfo.put("isKeyInHeader", auth.isKeyInHeader);
+ authInfo.put("isKeyInQuery", auth.isKeyInQuery);
+ authInfo.put("isKeyInCookie", auth.isKeyInCookie);
+ apiKeyAuth.add(authInfo);
+ } else if (auth.isBasicBearer) {
+ if (auth.name != null && auth.name.toLowerCase(Locale.ROOT).contains("bearer")) {
+ bearerAuth.add(authInfo);
+ } else {
+ basicAuth.add(authInfo);
+ }
+ } else if (auth.isBasic || auth.isBasicBasic) {
+ basicAuth.add(authInfo);
+ } else if (auth.isOAuth) {
+ authInfo.put("scopes", auth.scopes);
+ oauth2Auth.add(authInfo);
+ }
+ }
+
+ if (!apiKeyAuth.isEmpty()) {
+ op.vendorExtensions.put("hasApiKeyAuth", true);
+ op.vendorExtensions.put("apiKeyAuth", apiKeyAuth);
+ }
+ if (!bearerAuth.isEmpty()) {
+ op.vendorExtensions.put("hasBearerAuth", true);
+ op.vendorExtensions.put("bearerAuth", bearerAuth);
+ }
+ if (!basicAuth.isEmpty()) {
+ op.vendorExtensions.put("hasBasicAuth", true);
+ op.vendorExtensions.put("basicAuth", basicAuth);
+ }
+ if (!oauth2Auth.isEmpty()) {
+ op.vendorExtensions.put("hasOAuth2", true);
+ op.vendorExtensions.put("oauth2Auth", oauth2Auth);
+ }
+ }
+
+ /**
+ * Post-processes supporting file data to conditionally add files based on API features.
+ * This is called after preprocessOpenAPI, so hasAuthMethods is properly set.
+ *
+ * @param objs the supporting file data
+ * @return the processed supporting file data
+ */
+ @Override
+ public Map postProcessSupportingFileData(Map objs) {
+ // Conditionally add AuthenticationManager file if security is defined
+ if (additionalProperties.containsKey("hasAuthMethods")
+ && Boolean.TRUE.equals(additionalProperties.get("hasAuthMethods"))) {
+ supportingFiles.add(new SupportingFile("AuthenticationManager.mustache", "api", "AuthenticationManager.h"));
+ }
+ return super.postProcessSupportingFileData(objs);
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
index 345f9b2543b7..8e7f3b9423f2 100644
--- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
+++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
@@ -12,6 +12,7 @@ org.openapitools.codegen.languages.CrystalClientCodegen
org.openapitools.codegen.languages.CLibcurlClientCodegen
org.openapitools.codegen.languages.ClojureClientCodegen
org.openapitools.codegen.languages.ConfluenceWikiCodegen
+org.openapitools.codegen.languages.CppHttplibServerCodegen
org.openapitools.codegen.languages.CppOatppClientCodegen
org.openapitools.codegen.languages.CppQtClientCodegen
org.openapitools.codegen.languages.CppQtQHttpEngineServerCodegen
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/AuthenticationManager.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/AuthenticationManager.mustache
new file mode 100644
index 000000000000..40b3cb7784f1
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/AuthenticationManager.mustache
@@ -0,0 +1,75 @@
+{{>License}}
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace {{apiNamespace}} {
+
+/**
+ * @brief Authentication Manager Interface
+ *
+ * This interface defines the contract for authentication validation.
+ * Users must implement this interface to provide their own authentication logic.
+ *
+ * Example implementation:
+ * @code
+ * class MyAuthManager : public AuthenticationManager {
+ * public:
+ * bool validateApiKey(const std::string& key) override {
+ * return database.checkApiKey(key);
+ * }
+ *
+ * bool validateBearerToken(const std::string& token) override {
+ * return jwt::verify(token, secret);
+ * }
+ *
+ * bool validateBasicAuth(const std::string& username, const std::string& password) override {
+ * return bcrypt::verify(password, database.getPasswordHash(username));
+ * }
+ *
+ * bool validateOAuth2(const std::string& token, const std::vector& scopes) override {
+ * auto introspection = oauthProvider.introspect(token);
+ * return introspection.active && hasRequiredScopes(introspection, scopes);
+ * }
+ * };
+ * @endcode
+ */
+class AuthenticationManager {
+public:
+ virtual ~AuthenticationManager() = default;
+
+ /**
+ * @brief Validate an API key
+ * @param key The API key to validate
+ * @return true if the API key is valid, false otherwise
+ */
+ virtual bool validateApiKey(const std::string& key) = 0;
+
+ /**
+ * @brief Validate a Bearer token (e.g., JWT)
+ * @param token The bearer token to validate
+ * @return true if the token is valid, false otherwise
+ */
+ virtual bool validateBearerToken(const std::string& token) = 0;
+
+ /**
+ * @brief Validate Basic authentication credentials
+ * @param username The username
+ * @param password The password
+ * @return true if the credentials are valid, false otherwise
+ */
+ virtual bool validateBasicAuth(const std::string& username, const std::string& password) = 0;
+
+ /**
+ * @brief Validate an OAuth2 token with required scopes
+ * @param token The OAuth2 access token
+ * @param scopes The required scopes for this operation
+ * @return true if the token is valid and has required scopes, false otherwise
+ */
+ virtual bool validateOAuth2(const std::string& token, const std::vector& scopes) = 0;
+};
+
+} // namespace {{apiNamespace}}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache
new file mode 100644
index 000000000000..45ec4d39f669
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/CMakeLists.txt.mustache
@@ -0,0 +1,62 @@
+cmake_minimum_required(VERSION 3.14)
+project({{cmakeProjectName}} LANGUAGES CXX)
+
+include(FetchContent)
+
+# Fetch nlohmann_json
+FetchContent_Declare(
+ nlohmann_json
+ GIT_REPOSITORY https://github.com/nlohmann/json.git
+ GIT_TAG v3.11.3
+)
+FetchContent_MakeAvailable(nlohmann_json)
+
+# Fetch cpp-httplib
+FetchContent_Declare(
+ httplib
+ GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
+ GIT_TAG v0.15.3
+)
+FetchContent_MakeAvailable(httplib)
+
+# System libraries - install with: sudo apt-get install libssl-dev zlib1g-dev
+find_package(OpenSSL REQUIRED)
+find_package(ZLIB REQUIRED)
+
+set(TARGET_NAME {{cmakeProjectName}}_openapi_lib)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+file(GLOB API_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/api/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/api/*.cpp
+)
+file(GLOB MODEL_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/models/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp
+)
+add_library(${TARGET_NAME} ${API_SRCS} ${MODEL_SRCS})
+target_include_directories (${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+# Make sure these libraries/headers are available in the build environment before linking
+# Required libraries/headers are httplib,ssl,nlohmann::json,zlib
+target_link_libraries(${TARGET_NAME}
+ PRIVATE
+ httplib::httplib
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ nlohmann_json::nlohmann_json
+ ZLIB::ZLIB)
+
+{{#addApiImplStubs}}
+# Build executable server with stub implementation
+add_executable({{cmakeProjectName}}_server main.cpp)
+target_link_libraries({{cmakeProjectName}}_server
+ PRIVATE
+ ${TARGET_NAME}
+ httplib::httplib
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ nlohmann_json::nlohmann_json
+ ZLIB::ZLIB)
+target_include_directories({{cmakeProjectName}}_server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+{{/addApiImplStubs}}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/License.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/License.mustache
new file mode 100644
index 000000000000..286685933869
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/License.mustache
@@ -0,0 +1,5 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache
new file mode 100644
index 000000000000..979519ce5c71
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/README.mustache
@@ -0,0 +1,437 @@
+# {{projectName}} - C++ Server
+
+## Overview
+
+This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project.
+It uses the [cpp-httplib](https://github.com/yhirose/cpp-httplib) library to implement a lightweight HTTP server
+with JSON request/response handling via [nlohmann/json](https://github.com/nlohmann/json).
+
+## Requirements
+
+- C++17 compatible compiler
+- CMake (3.14 or higher)
+- OpenSSL (for HTTPS support)
+- ZLIB (for compression support)
+
+**Note:** The following libraries are automatically downloaded via CMake FetchContent:
+- [cpp-httplib](https://github.com/yhirose/cpp-httplib) v0.15.3
+- [nlohmann/json](https://github.com/nlohmann/json) v3.11.3
+
+### Platform-Specific Installation
+
+**Linux (Ubuntu/Debian):**
+```bash
+sudo apt-get update
+sudo apt-get install -y libssl-dev zlib1g-dev cmake build-essential
+```
+
+**macOS:**
+```bash
+brew install openssl zlib cmake
+```
+
+**Windows:**
+```powershell
+# Using vcpkg
+vcpkg install openssl:x64-windows zlib:x64-windows
+
+# Then configure CMake with vcpkg toolchain:
+cmake -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg_root]/scripts/buildsystems/vcpkg.cmake
+```
+
+## Project Structure
+
+```
+├── CMakeLists.txt # Project build configuration
+├── README.md # This file
+├── models/ # Generated model classes
+└── api/ # Generated API handler classes
+```
+
+## Building the Project
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+
+{{#addApiImplStubs}}
+### Quick Start with Generated Stubs
+
+This project was generated with implementation stubs enabled. You can build and run the server immediately:
+
+```bash
+mkdir build && cd build
+cmake ..
+make
+./{{cmakeProjectName}}_server
+```
+
+The server will start on `http://0.0.0.0:8080` with:
+- All API endpoints registered with stub implementations
+- Health check endpoint at `/health`
+{{#hasAuthMethods}}
+- Example authentication manager (remember to implement real authentication logic!)
+{{/hasAuthMethods}}
+
+**Implementation files to customize:**
+{{#apiInfo}}
+{{#apis}}
+{{#operations}}
+- `api/{{classFilename}}{{apiSuffix}}Impl.h` - API interface implementation
+- `api/{{classFilename}}{{apiSuffix}}Impl.cpp` - Business logic stubs
+{{/operations}}
+{{/apis}}
+{{/apiInfo}}
+- `main.cpp` - Server configuration and startup
+
+{{/addApiImplStubs}}
+## Working with Models
+
+### Model Classes
+
+{{#models}}
+{{#model}}
+#### {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}
+
+```cpp
+// Create a model
+auto model = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}();
+{{#vars}}
+model.{{vendorExtensions.setter}}(/* value */); // Set {{baseName}}
+{{/vars}}
+
+// Serialize to JSON
+nlohmann::json json = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = {{vendorExtensions.modelNamespace}}::{{vendorExtensions.modelClassName}}::fromJson(nlohmann::json::parse(jsonString));
+```
+{{/model}}
+{{/models}}
+
+## Implementing API Handlers
+
+### API Classes
+
+Each API is generated as an abstract base class with pure virtual methods that you must implement.
+
+{{#apiInfo}}
+{{#apis}}
+{{#operations}}
+#### {{classname}}
+
+Create a class that inherits from the generated base class:
+
+```cpp
+#include "api/{{classFilename}}{{apiSuffix}}.h"
+
+class {{classname}}Impl : public {{apiNamespace}}::{{classname}} {
+public:
+ {{#operation}}
+ {{#vendorExtensions}}
+ {{#hasAnyResponseSchema}}
+ {{responseType}} {{handlerFunctionName}}({{#hasAnyRequestSchema}}const {{requestType}}& params{{/hasAnyRequestSchema}}) override {
+ {{#hasAnyRequestSchema}}
+ // Access request parameters:
+ {{#pathParams}}
+ // Path: params.m_{{paramName}}
+ {{/pathParams}}
+ {{#queryParams}}
+ // Query: params.m_{{paramName}}{{^vendorExtensions.isRequired}} (optional){{/vendorExtensions.isRequired}}
+ {{/queryParams}}
+ {{#headerParams}}
+ // Header: params.m_{{paramName}}{{^vendorExtensions.isRequired}} (optional){{/vendorExtensions.isRequired}}
+ {{/headerParams}}
+ {{#requestModel}}
+ // Body: params.m_request (std::optional<{{requestModelNamespace}}::{{requestModel}}>)
+ {{/requestModel}}
+ {{/hasAnyRequestSchema}}
+
+ // Implement your business logic here
+
+ {{#successCodeToTypes}}
+ // Return success response (HTTP {{successConstName}}):
+ {{successType}} successResponse;
+ // ... populate response ...
+ return successResponse;
+ {{/successCodeToTypes}}
+ {{#errorCodeToTypes}}
+
+ // Or return error response (HTTP {{errorConstName}}):
+ // {{errorType}} errorResponse;
+ // return errorResponse;
+ {{/errorCodeToTypes}}
+ }
+ {{/hasAnyResponseSchema}}
+ {{^hasAnyResponseSchema}}
+ void {{handlerFunctionName}}({{#hasAnyRequestSchema}}const {{requestType}}& params{{/hasAnyRequestSchema}}) override {
+ {{#hasAnyRequestSchema}}
+ // Access request parameters from params struct
+ {{/hasAnyRequestSchema}}
+ // Implement your logic here
+ }
+ {{/hasAnyResponseSchema}}
+ {{/vendorExtensions}}
+
+ {{/operation}}
+};
+```
+{{/operations}}
+{{/apis}}
+{{/apiInfo}}
+
+## Running the Server
+
+Here's a complete example of setting up and running the server:
+
+```cpp
+#include
+#include
+{{#apiInfo}}{{#apis}}{{#operations}}
+#include "api/{{classFilename}}{{apiSuffix}}.h"
+{{/operations}}{{/apis}}{{/apiInfo}}
+{{#hasAuthMethods}}
+#include "api/AuthenticationManager.h"
+{{/hasAuthMethods}}
+
+int main() {
+ httplib::Server server;
+
+{{#hasAuthMethods}}
+ // Create authentication manager (required for this API)
+ auto authMgr = std::make_shared();
+
+{{/hasAuthMethods}}
+ // Create API implementations
+{{#apiInfo}}{{#apis}}{{#operations}}
+ {{classname}}Impl {{classVarName}};
+{{/operations}}{{/apis}}{{/apiInfo}}
+
+ // Register routes
+{{#apiInfo}}{{#apis}}{{#operations}}
+ {{classVarName}}.registerRoutes(server{{#hasAuthMethods}}, authMgr{{/hasAuthMethods}});
+{{/operations}}{{/apis}}{{/apiInfo}}
+
+ // Start server
+ std::cout << "Server starting on http://localhost:8080" << std::endl;
+ server.listen("localhost", 8080);
+
+ return 0;
+}
+```
+
+{{#hasAuthMethods}}
+### With Authentication
+
+When authentication is required, you must:
+1. Implement the `AuthenticationManager` interface (see Authentication section below)
+2. Pass the authentication manager to `registerRoutes()`
+
+{{/hasAuthMethods}}
+{{^hasAuthMethods}}
+### Without Authentication
+
+This API does not require authentication. Simply create your API implementations and register them with the server.
+
+{{/hasAuthMethods}}
+
+## Authentication
+
+{{#hasAuthMethods}}
+This API requires authentication. Implement the `AuthenticationManager` interface to provide your authentication logic:
+
+```cpp
+#include "api/AuthenticationManager.h"
+
+class MyAuthManager : public {{apiNamespace}}::AuthenticationManager {
+public:
+ bool validateApiKey(const std::string& key) override {
+ // Validate API key from header, query, or cookie
+ // Example: check against database or cache
+ return checkApiKeyInDatabase(key);
+ }
+
+ bool validateBearerToken(const std::string& token) override {
+ // Validate JWT or other bearer tokens
+ // Example: verify signature and expiration
+ return jwt::verify(token, secret_key);
+ }
+
+ bool validateBasicAuth(const std::string& username, const std::string& password) override {
+ // Validate username/password credentials
+ // Example: check against user database with hashed passwords
+ auto user = findUser(username);
+ return user && bcrypt::verify(password, user->passwordHash);
+ }
+
+ bool validateOAuth2(const std::string& token, const std::vector& scopes) override {
+ // Validate OAuth2 token and check required scopes
+ // Example: introspect token and verify scopes
+ auto introspection = oauthProvider.introspect(token);
+ return introspection.active && hasAllScopes(introspection.scopes, scopes);
+ }
+};
+```
+
+### Authentication Flow
+
+1. The server automatically extracts credentials from requests (headers, query params, cookies)
+2. Before calling your handler, it validates credentials using your `AuthenticationManager`
+3. If validation fails, the server returns HTTP 401 Unauthorized automatically
+4. If validation succeeds, your handler is called
+
+### Security Schemes
+
+The generated code supports:
+- **API Key**: Header, query parameter, or cookie-based authentication
+- **Bearer Token**: Authorization header with "Bearer" scheme (e.g., JWT)
+- **Basic Auth**: HTTP Basic authentication (username:password)
+- **OAuth2**: OAuth 2.0 token-based authentication with scope validation
+
+{{/hasAuthMethods}}
+{{^hasAuthMethods}}
+This API does not require authentication.
+
+{{/hasAuthMethods}}
+
+## Error Handling
+
+### Response Variants
+
+Each API endpoint that returns data uses `std::variant` to represent multiple possible response types (success and errors):
+
+```cpp
+// Example: endpoint returns success (User) or errors (NotFound, ServerError)
+using GetUserResponse = std::variant;
+
+GetUserResponse handleGetUser(const GetUserRequest& params) override {
+ if (userExists(params.m_userId)) {
+ User user = fetchUser(params.m_userId);
+ return user; // Automatically sets HTTP 200
+ } else {
+ NotFound error;
+ error.setMessage("User not found");
+ return error; // Automatically sets HTTP 404
+ }
+}
+```
+
+The server automatically:
+- Detects which type is returned from the variant
+- Sets the appropriate HTTP status code
+- Serializes the response to JSON
+
+### HTTP Status Codes
+
+Status codes are automatically set based on the response type you return. Each model type is associated with a specific HTTP status code defined in your OpenAPI specification.
+
+**Optimized Status Code Constants:**
+The generator only creates HTTP status code constants (e.g., `HTTP_RESPONSE_CODE_200`, `HTTP_RESPONSE_CODE_404`) for codes actually used by your API operations. This reduces code bloat and compilation time compared to generating all possible HTTP status codes.
+
+### Parameter Validation
+
+The generated code automatically validates:
+- **Required parameters**: Returns HTTP 400 if missing
+- **Type conversion**: Returns HTTP 400 if parameter cannot be converted to expected type
+- **JSON parsing**: Returns HTTP 400 if request body is invalid JSON
+
+Custom validation logic should be implemented in your handler methods.
+
+### Working with Optional Parameters
+
+Optional parameters and model fields use `std::optional`:
+
+```cpp
+void handleRequest(const RequestParams& params) override {
+ // Check if optional query parameter is present
+ if (params.m_optionalParam) {
+ auto value = *params.m_optionalParam; // Dereference to get value
+ // Use value...
+ }
+
+ // Check if optional request body is present
+ if (params.m_request) {
+ auto body = *params.m_request; // Dereference to get body
+ // Use body...
+ }
+}
+```
+
+## Advanced Features
+
+### Parameter Serialization Styles
+
+The generator supports various parameter serialization styles as defined in OpenAPI:
+
+- **simple**: Comma-separated values (default for path/header)
+- **form**: Ampersand-separated values (default for query)
+- **spaceDelimited**: Space-separated values
+- **pipeDelimited**: Pipe-separated values
+- **deepObject**: Nested object notation for query parameters
+
+These are automatically handled during parameter parsing.
+
+### Enum Handling
+
+All generated enums automatically include an `UNSPECIFIED` value as the first enum entry for safe initialization:
+
+```cpp
+enum class Status {
+ UNSPECIFIED = 0, // Added automatically for safety
+ PENDING,
+ APPROVED,
+ REJECTED
+};
+
+// Safe default initialization
+Status status; // Defaults to UNSPECIFIED (0)
+
+// Explicit initialization
+Status activeStatus = Status::APPROVED;
+
+// Enum serialization/deserialization
+// UNSPECIFIED is not a valid API value and indicates uninitialized state
+```
+
+**Why UNSPECIFIED?**
+- Provides a safe default value for uninitialized enums
+- Prevents undefined behavior from using uninitialized enum values
+- Makes it clear when an enum hasn't been set vs. having a valid API value
+- Does not appear in OpenAPI spec - internal C++ implementation detail
+
+### Union Types (anyOf/oneOf)
+
+When your OpenAPI spec uses `anyOf` or `oneOf`, the generated code uses `std::variant`:
+
+```cpp
+// OpenAPI: { "anyOf": [{"type": "string"}, {"type": "number"}] }
+using MyUnionType = std::variant;
+
+// In your model:
+MyUnionType value;
+
+// Use std::visit to handle different types:
+std::visit([](const auto& v) {
+ using T = std::decay_t;
+ if constexpr (std::is_same_v) {
+ std::cout << "String: " << v << std::endl;
+ } else if constexpr (std::is_same_v) {
+ std::cout << "Number: " << v << std::endl;
+ }
+}, value);
+```
+
+## Additional Resources
+
+- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib)
+- [nlohmann/json Documentation](https://github.com/nlohmann/json)
+- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/cpp-httplib-server)
+- [OpenAPI Specification](https://swagger.io/specification/)
+
+- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib)
+- [nlohmann/json Documentation](https://github.com/nlohmann/json)
+- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/)
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache
new file mode 100644
index 000000000000..7d3bd0b4a42a
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-header.mustache
@@ -0,0 +1,128 @@
+{{>License}}
+
+#pragma once
+
+// System headers
+#include {{#hasAuthMethods}}
+#include {{/hasAuthMethods}}{{#includeVariantHeader}}
+{{{includeVariantHeader}}}{{/includeVariantHeader}}{{#includeOptionalHeader}}
+{{{includeOptionalHeader}}}{{/includeOptionalHeader}}
+
+// Project headers
+{{#modelsUsed}}
+#include "models/{{{.}}}.h"
+{{/modelsUsed}}
+
+namespace {{apiNamespace}} {
+{{#hasAuthMethods}}
+ class AuthenticationManager;
+{{/hasAuthMethods}}
+class {{apiClassnameInPascalCase}} {
+public:
+ {{apiClassnameInPascalCase}}() = default;
+ virtual ~{{apiClassnameInPascalCase}}() = default;
+ /**
+ * @brief Register all routes for this API
+ * @param svr The httplib::Server instance to register routes on
+{{#hasAnyAuth}}
+ * @param auth The AuthenticationManager for authentication (optional, defaults to nullptr)
+{{/hasAnyAuth}}
+ */
+ void registerRoutes(httplib::Server& svr{{#hasAnyAuth}}, std::shared_ptr auth = nullptr{{/hasAnyAuth}});
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+{{#hasAnyRequestSchema}}
+{{#-first}}
+ // =========================
+ // ===== Request types =====
+ // =========================
+{{/-first}}
+
+ /**
+ * @brief Request type for {{handlerFunctionName}}.
+ */
+ struct {{requestType}}
+ {
+ {{#requestModel}}
+ {{{bodyParam.dataType}}} m_request; //Request Body{{#bodyParam.vendorExtensions.isRequired}} (required){{/bodyParam.vendorExtensions.isRequired}}{{^bodyParam.vendorExtensions.isRequired}} (optional){{/bodyParam.vendorExtensions.isRequired}}{{/requestModel}}{{#queryParams}}
+ {{{dataType}}} m_{{paramName}}; //Query Params{{#vendorExtensions.isRequired}} (required){{/vendorExtensions.isRequired}}{{^vendorExtensions.isRequired}} (optional){{/vendorExtensions.isRequired}}{{/queryParams}}{{#headerParams}}
+ {{{dataType}}} m_{{paramName}}; //HeaderParams{{#vendorExtensions.isRequired}} (required){{/vendorExtensions.isRequired}}{{^vendorExtensions.isRequired}} (optional){{/vendorExtensions.isRequired}}{{/headerParams}}{{#pathParams}}
+ {{{dataType}}} m_{{paramName}}; //PathParams (always required){{/pathParams}}{{#cookieParams}}
+ {{{dataType}}} m_{{paramName}}; //Cookies{{#vendorExtensions.isRequired}} (required){{/vendorExtensions.isRequired}}{{^vendorExtensions.isRequired}} (optional){{/vendorExtensions.isRequired}}{{/cookieParams}}
+ };
+{{/hasAnyRequestSchema}}
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+{{#hasAnyResponseSchema}}
+{{#-first}}
+ // ==========================
+ // ===== Response types =====
+ // ==========================
+{{/-first}}
+
+ /**
+ * @brief Response type for {{handlerFunctionName}}.
+ */
+{{#hasSingleResponseType}}
+ using {{responseType}} = {{singleResponseType}};
+{{/hasSingleResponseType}}
+{{^hasSingleResponseType}}
+ using {{responseType}} = std::variant<{{#successCodeToTypes}}{{#successType}}
+ {{successType}}{{/successType}}{{#errorCodeToTypes}}{{#-first}} ,{{/-first}}{{/errorCodeToTypes}}{{/successCodeToTypes}}{{^errorCodeToTypes}}>;{{/errorCodeToTypes}}{{#errorCodeToTypes}}
+ {{#errorType}}{{errorType}}{{/errorType}}{{^-last}} ,{{/-last}}{{#-last}} >;{{/-last}}{{/errorCodeToTypes}}
+{{/hasSingleResponseType}}
+{{/hasAnyResponseSchema}}
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+ // ============================================================
+ // ===== Pure virtual functions to be handled by the user =====
+ // ============================================================
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+ /**
+ {{#hasAnyRequestSchema}}
+ * {{requestType}} - struct containing all the query parameters and headers and schemas as available.
+ {{/hasAnyRequestSchema}}
+ {{#hasAnyResponseSchema}}
+ * @return {{responseType}} The response type returned by the handler.
+ {{/hasAnyResponseSchema}}
+ */
+ virtual {{#hasAnyResponseSchema}}{{responseType}}{{/hasAnyResponseSchema}}{{^hasAnyResponseSchema}}void{{/hasAnyResponseSchema}} {{handlerFunctionName}}({{#hasAnyRequestSchema}}const {{requestType}}& params{{/hasAnyRequestSchema}})=0;
+
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+private:
+ // ========================================
+ // ===== Helper function declarations =====
+ // ========================================
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+{{#hasAnyRequestSchema}}
+ static bool parse{{operationIdPascalCase}}Params(const httplib::Request& req, {{requestType}}& params, std::vector& paramErrors);
+{{/hasAnyRequestSchema}}
+ void handle{{operationIdPascalCase}}Request(const httplib::Request& req, httplib::Response& res{{#hasAuth}}, std::shared_ptr auth{{/hasAuth}});
+{{#hasAnyResponseSchema}}
+ static void handle{{responseType}}(const {{responseType}}& result, httplib::Response& res);
+{{/hasAnyResponseSchema}}
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+{{#hasAnyAuth}}
+ static bool performAuthentication(
+ const httplib::Request& req,
+ std::shared_ptr auth,
+ httplib::Response& res);
+{{/hasAnyAuth}}
+};
+
+} // namespace {{apiNamespace}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl-header.mustache
new file mode 100644
index 000000000000..a0c54ae42549
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl-header.mustache
@@ -0,0 +1,33 @@
+{{>License}}
+
+#pragma once
+
+#include "{{{apiHeaderFileName}}}"
+
+namespace {{apiNamespace}} {
+
+/**
+ * @brief Implementation class for {{apiClassnameInPascalCase}}
+ *
+ * This is a stub implementation that you can use as a starting point.
+ * Override the methods to implement your business logic.
+ */
+class {{apiClassnameInPascalCase}}Impl : public {{apiClassnameInPascalCase}} {
+public:
+ {{apiClassnameInPascalCase}}Impl() = default;
+ virtual ~{{apiClassnameInPascalCase}}Impl() = default;
+
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+ /**
+ * @brief Implementation stub for {{handlerFunctionName}}
+ */
+ {{#hasAnyResponseSchema}}{{responseType}}{{/hasAnyResponseSchema}}{{^hasAnyResponseSchema}}void{{/hasAnyResponseSchema}} {{handlerFunctionName}}({{#hasAnyRequestSchema}}const {{requestType}}& params{{/hasAnyRequestSchema}}) override;
+
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+};
+
+} // namespace {{apiNamespace}}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl.mustache
new file mode 100644
index 000000000000..7ffdcd8209c3
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-impl.mustache
@@ -0,0 +1,40 @@
+{{>License}}
+
+#include "{{{apiClassnameInPascalCase}}}ApiImpl.h"
+#include
+
+namespace {{apiNamespace}} {
+
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+/**
+ * Implementation stub for {{handlerFunctionName}}
+ * TODO: Implement your business logic here
+ */
+{{#hasAnyResponseSchema}}{{classname}}::{{responseType}}{{/hasAnyResponseSchema}}{{^hasAnyResponseSchema}}void{{/hasAnyResponseSchema}} {{classname}}::{{handlerFunctionName}}({{#hasAnyRequestSchema}}const {{classname}}::{{requestType}}& params{{/hasAnyRequestSchema}})
+{
+ // TODO: Implement your business logic here
+ {{#hasAnyResponseSchema}}
+ {{#hasSingleResponseType}}
+ // Return a default/stub response
+ {{responseType}} response;
+ // Initialize response fields as needed
+ return response;
+ {{/hasSingleResponseType}}
+ {{^hasSingleResponseType}}
+ // Return first success type as default
+ {{#successCodeToTypes}}{{#-first}}
+ {{successType}} response;
+ // Initialize response fields as needed
+ return response;
+ {{/-first}}{{/successCodeToTypes}}
+ {{/hasSingleResponseType}}
+ {{/hasAnyResponseSchema}}
+}
+
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+
+} // namespace {{apiNamespace}}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache
new file mode 100644
index 000000000000..a2663ab91ab7
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache
@@ -0,0 +1,756 @@
+{{>License}}
+
+// System headers
+#include
+#include
+#include {{#operations}}{{#operation}}{{#vendorExtensions}}{{#hasAnyRequestSchema}}{{#hasArrayParams}}{{#-first}}
+#include {{/-first}}{{/hasArrayParams}}{{/hasAnyRequestSchema}}{{/vendorExtensions}}{{/operation}}{{/operations}}
+
+// Project headers
+#include "{{{apiHeaderFileName}}}"{{#hasAnyAuth}}
+#include "AuthenticationManager.h"{{/hasAnyAuth}}
+
+{{#statusCodeConsts}}
+constexpr int {{constName}} = {{statusCode}};
+{{/statusCodeConsts}}
+
+namespace {{apiNamespace}} {
+
+using namespace {{modelNamespace}};
+
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+{{#hasAnyRequestSchema}}
+bool {{apiClassnameInPascalCase}}::parse{{operationIdPascalCase}}Params(const httplib::Request& req, {{apiClassnameInPascalCase}}::{{requestType}}& params, std::vector& paramErrors)
+{
+ std::vector errors;
+{{#requestModel}}
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+{{#bodyParam.vendorExtensions.isOptional}}
+ {{{bodyParam.baseType}}} temp;
+ from_json(json, temp);
+ params.m_request = temp;
+{{/bodyParam.vendorExtensions.isOptional}}
+{{^bodyParam.vendorExtensions.isOptional}}
+ from_json(json, params.m_request);
+{{/bodyParam.vendorExtensions.isOptional}}
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }{{#bodyParam.vendorExtensions.isRequired}}
+ else
+ {
+ errors.push_back("Missing required request body");
+ }{{/bodyParam.vendorExtensions.isRequired}}
+{{/requestModel}}
+{{#queryParams}}
+{{#-first}}
+
+{{/-first}}
+ // Query Parameters - {{baseName}}
+ if (req.has_param("{{baseName}}"))
+ {
+ {{#vendorExtensions.isObject}}try
+ {
+ {{#vendorExtensions.isDeepObject}}// deepObject: parse as JSON object from multiple keys
+ nlohmann::json obj;
+ for (const auto& [k, v] : req.params)
+ {
+ if (k.find("{{baseName}}[") == 0)
+ {
+ std::string field = k.substr(std::string("{{baseName}}[").size(), k.size() - std::string("{{baseName}}[").size() - 1);
+ obj[field] = v;
+ }
+ }
+ params.m_{{paramName}} = obj.get<{{{vendorExtensions.unwrappedDataType}}}>();{{/vendorExtensions.isDeepObject}}{{^vendorExtensions.isDeepObject}}// Parse JSON object from query
+ auto val = req.get_param_value("{{baseName}}", 0);
+ params.m_{{paramName}} = nlohmann::json::parse(val).get<{{{vendorExtensions.unwrappedDataType}}}>();{{/vendorExtensions.isDeepObject}}
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isObject}}{{#vendorExtensions.isArray}}try
+ {
+ {{#vendorExtensions.isSpaceDelimited}}// spaceDelimited: split by space
+ auto val = req.get_param_value("{{baseName}}", 0);
+ std::stringstream ss(val);
+ std::string item;{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ while (std::getline(ss, item, ' '))
+ {
+ if (!item.empty())
+ {
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+ }
+ }{{/vendorExtensions.isSpaceDelimited}}{{#vendorExtensions.isPipeDelimited}}// pipeDelimited: split by |
+ auto val = req.get_param_value("{{baseName}}", 0);
+ std::stringstream ss(val);
+ std::string item;{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ while (std::getline(ss, item, '|'))
+ {
+ if (!item.empty())
+ {
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+ }
+ }{{/vendorExtensions.isPipeDelimited}}{{^vendorExtensions.isSpaceDelimited}}{{^vendorExtensions.isPipeDelimited}}// form/simple: multi-param or comma-separated
+ size_t count = req.get_param_value_count("{{baseName}}");{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ if (count > 1)
+ {
+ for (size_t i = 0; i < count; ++i)
+ {
+ auto val = req.get_param_value("{{baseName}}", i);
+{{#items.vendorExtensions.isInt}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stoi(val));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stoi(val));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isInt}}
+{{#items.vendorExtensions.isLong}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stol(val));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stol(val));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isLong}}
+{{#items.vendorExtensions.isDouble}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stod(val));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stod(val));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isDouble}}
+{{#items.vendorExtensions.isFloat}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stof(val));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stof(val));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isFloat}}
+{{^items.vendorExtensions.isInt}}{{^items.vendorExtensions.isLong}}{{^items.vendorExtensions.isDouble}}{{^items.vendorExtensions.isFloat}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(val);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(val);
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isFloat}}{{/items.vendorExtensions.isDouble}}{{/items.vendorExtensions.isLong}}{{/items.vendorExtensions.isInt}}
+ }
+ }
+ else if (count == 1)
+ {
+ auto val = req.get_param_value("{{baseName}}", 0);
+ std::stringstream ss(val);
+ std::string item;
+ while (std::getline(ss, item, ','))
+ {
+ if (!item.empty())
+ {
+{{#items.vendorExtensions.isInt}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stoi(item));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stoi(item));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isInt}}
+{{#items.vendorExtensions.isLong}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stol(item));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stol(item));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isLong}}
+{{#items.vendorExtensions.isDouble}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stod(item));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stod(item));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isDouble}}
+{{#items.vendorExtensions.isFloat}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(std::stof(item));
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(std::stof(item));
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isFloat}}
+{{^items.vendorExtensions.isInt}}{{^items.vendorExtensions.isLong}}{{^items.vendorExtensions.isDouble}}{{^items.vendorExtensions.isFloat}}
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{/items.vendorExtensions.isFloat}}{{/items.vendorExtensions.isDouble}}{{/items.vendorExtensions.isLong}}{{/items.vendorExtensions.isInt}}
+ }
+ }
+ }{{/vendorExtensions.isPipeDelimited}}{{/vendorExtensions.isSpaceDelimited}}
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isArray}}{{#vendorExtensions.isEnum}}try
+ {
+ params.m_{{paramName}} = {{enumFromStringHelper}}(req.get_param_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isEnum}}{{#vendorExtensions.isString}}params.m_{{paramName}} = req.get_param_value("{{baseName}}"){{#vendorExtensions.isMatrix}}.substr(1){{/vendorExtensions.isMatrix}}{{#vendorExtensions.isLabel}}.substr(1){{/vendorExtensions.isLabel}};{{/vendorExtensions.isString}}{{#vendorExtensions.isInt}}try
+ {
+ params.m_{{paramName}} = std::stoi(req.get_param_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isInt}}{{#vendorExtensions.isLong}}try
+ {
+ params.m_{{paramName}} = std::stoll(req.get_param_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isLong}}{{#vendorExtensions.isBool}}try
+ {
+ params.m_{{paramName}} = (req.get_param_value("{{baseName}}") == "true");
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isBool}}{{#vendorExtensions.isDouble}}try
+ {
+ params.m_{{paramName}} = std::stod(req.get_param_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isDouble}}{{#vendorExtensions.isFloat}}try
+ {
+ params.m_{{paramName}} = std::stof(req.get_param_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isFloat}}{{^vendorExtensions.isObject}}{{^vendorExtensions.isArray}}{{^vendorExtensions.isEnum}}{{^vendorExtensions.isString}}{{^vendorExtensions.isInt}}{{^vendorExtensions.isLong}}{{^vendorExtensions.isBool}}{{^vendorExtensions.isDouble}}{{^vendorExtensions.isFloat}}// Fallback: direct assignment
+ params.m_{{paramName}} = req.get_param_value("{{baseName}}");{{/vendorExtensions.isFloat}}{{/vendorExtensions.isDouble}}{{/vendorExtensions.isBool}}{{/vendorExtensions.isLong}}{{/vendorExtensions.isInt}}{{/vendorExtensions.isString}}{{/vendorExtensions.isEnum}}{{/vendorExtensions.isArray}}{{/vendorExtensions.isObject}}
+ }{{^vendorExtensions.isRequired}}{{#vendorExtensions.hasDefaultValue}}
+ else
+ {
+ // Use default value for optional parameter
+ params.m_{{paramName}} = {{{vendorExtensions.defaultValue}}};
+ }{{/vendorExtensions.hasDefaultValue}}{{/vendorExtensions.isRequired}}
+{{/queryParams}}
+{{#headerParams}}
+{{#-first}}
+
+{{/-first}}
+ // Header Parameters - {{baseName}}
+ if (!req.get_header_value("{{baseName}}").empty())
+ {
+ {{#vendorExtensions.isObject}}try
+ {
+ // Parse JSON object from header
+ auto val = req.get_header_value("{{baseName}}");
+ params.m_{{paramName}} = nlohmann::json::parse(val);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isObject}}{{#vendorExtensions.isArray}}try
+ {
+ // Header arrays may be comma-separated
+ auto val = req.get_header_value("{{baseName}}");
+ std::stringstream ss(val);
+ std::string item;{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ while (std::getline(ss, item, ','))
+ {
+ if (!item.empty())
+ {
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isArray}}{{#vendorExtensions.isEnum}}try
+ {
+ params.m_{{paramName}} = {{enumFromStringHelper}}(req.get_header_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isEnum}}{{#vendorExtensions.isString}}params.m_{{paramName}} = req.get_header_value("{{baseName}}");{{/vendorExtensions.isString}}{{#vendorExtensions.isInt}}try
+ {
+ params.m_{{paramName}} = std::stoi(req.get_header_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isInt}}{{#vendorExtensions.isLong}}try
+ {
+ params.m_{{paramName}} = std::stoll(req.get_header_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isLong}}{{#vendorExtensions.isBool}}try
+ {
+ params.m_{{paramName}} = (req.get_header_value("{{baseName}}") == "true");
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isBool}}{{#vendorExtensions.isDouble}}try
+ {
+ params.m_{{paramName}} = std::stod(req.get_header_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isDouble}}{{#vendorExtensions.isFloat}}try
+ {
+ params.m_{{paramName}} = std::stof(req.get_header_value("{{baseName}}"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid header parameter '{{baseName}}': " + std::string(e.what()));
+ }{{/vendorExtensions.isFloat}}{{^vendorExtensions.isObject}}{{^vendorExtensions.isArray}}{{^vendorExtensions.isEnum}}{{^vendorExtensions.isString}}{{^vendorExtensions.isInt}}{{^vendorExtensions.isLong}}{{^vendorExtensions.isBool}}{{^vendorExtensions.isDouble}}{{^vendorExtensions.isFloat}}// Fallback: direct assignment
+ params.m_{{paramName}} = req.get_header_value("{{baseName}}");{{/vendorExtensions.isFloat}}{{/vendorExtensions.isDouble}}{{/vendorExtensions.isBool}}{{/vendorExtensions.isLong}}{{/vendorExtensions.isInt}}{{/vendorExtensions.isString}}{{/vendorExtensions.isEnum}}{{/vendorExtensions.isArray}}{{/vendorExtensions.isObject}}
+ }{{^vendorExtensions.isRequired}}{{#vendorExtensions.hasDefaultValue}}
+ else
+ {
+ // Use default value for optional parameter
+ params.m_{{paramName}} = {{{vendorExtensions.defaultValue}}};
+ }{{/vendorExtensions.hasDefaultValue}}{{/vendorExtensions.isRequired}}
+{{/headerParams}}
+{{#pathParams}}
+{{#-first}}
+
+{{/-first}}
+ // Path Parameters - {{baseName}} (index: {{vendorExtensions.pathIndex}})
+ if (req.matches.size() < {{vendorExtensions.pathIndex}} + 1)
+ {
+ errors.push_back("Missing path parameter '{{baseName}}'");
+ }
+ else
+ {
+ try
+ {
+ {{#vendorExtensions.isObject}}
+ // Parse JSON object from path param (rare)
+ auto val = req.matches[{{vendorExtensions.pathIndex}}];
+ params.m_{{paramName}} = nlohmann::json::parse(val);
+ {{/vendorExtensions.isObject}}
+ {{#vendorExtensions.isArray}}
+ // Path arrays may be delimited (e.g., comma, pipe, etc.)
+ auto val = req.matches[{{vendorExtensions.pathIndex}}];
+ std::stringstream ss(val);
+ std::string item;{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ while (std::getline(ss, item, ','))
+ {
+ if (!item.empty())
+ {
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+ }
+ }
+ {{/vendorExtensions.isArray}}
+ {{#vendorExtensions.isEnum}}
+ params.m_{{paramName}} = {{enumFromStringHelper}}(req.matches[{{vendorExtensions.pathIndex}}]);
+ {{/vendorExtensions.isEnum}}
+ {{#vendorExtensions.isString}}
+ params.m_{{paramName}} = req.matches[{{vendorExtensions.pathIndex}}];
+ {{/vendorExtensions.isString}}
+ {{#vendorExtensions.isInt}}
+ params.m_{{paramName}} = std::stoi(req.matches[{{vendorExtensions.pathIndex}}]);
+ {{/vendorExtensions.isInt}}
+ {{#vendorExtensions.isLong}}
+ params.m_{{paramName}} = std::stoll(req.matches[{{vendorExtensions.pathIndex}}]);
+ {{/vendorExtensions.isLong}}
+ {{#vendorExtensions.isBool}}
+ params.m_{{paramName}} = (req.matches[{{vendorExtensions.pathIndex}}] == "true");
+ {{/vendorExtensions.isBool}}
+ {{#vendorExtensions.isDouble}}
+ params.m_{{paramName}} = std::stod(req.matches[{{vendorExtensions.pathIndex}}]);
+ {{/vendorExtensions.isDouble}}
+ {{#vendorExtensions.isFloat}}
+ params.m_{{paramName}} = std::stof(req.matches[{{vendorExtensions.pathIndex}}]);
+ {{/vendorExtensions.isFloat}}
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid path parameter '{{baseName}}': " + std::string(e.what()));
+ }
+ }
+{{/pathParams}}
+{{#cookieParams}}
+{{#-first}}
+
+{{/-first}}
+ // Cookie Parameters - {{baseName}}
+ try
+ {
+ auto cookieHeader = req.get_header_value("Cookie");
+ if (!cookieHeader.empty())
+ {
+ std::string cookieValue;
+ std::string key = "{{baseName}}=";
+ size_t start = cookieHeader.find(key);
+ if (start != std::string::npos)
+ {
+ start += key.length();
+ size_t end = cookieHeader.find(";", start);
+ if (end == std::string::npos) end = cookieHeader.length();
+ cookieValue = cookieHeader.substr(start, end - start);
+ {{#vendorExtensions.isObject}}params.m_{{paramName}} = nlohmann::json::parse(cookieValue);{{/vendorExtensions.isObject}}{{#vendorExtensions.isArray}}{{#vendorExtensions.isOptional}}
+ if (!params.m_{{paramName}}.has_value())
+ {
+ params.m_{{paramName}} = std::vector<{{{items.dataType}}}>{};
+ }{{/vendorExtensions.isOptional}}
+ std::stringstream ss(cookieValue);
+ std::string item;
+ while (std::getline(ss, item, ','))
+ {
+ if (!item.empty())
+ {
+{{#vendorExtensions.isOptional}}
+ params.m_{{paramName}}->emplace_back(item);
+{{/vendorExtensions.isOptional}}
+{{^vendorExtensions.isOptional}}
+ params.m_{{paramName}}.emplace_back(item);
+{{/vendorExtensions.isOptional}}
+ }
+ }{{/vendorExtensions.isArray}}{{#vendorExtensions.isEnum}}params.m_{{paramName}} = {{enumFromStringHelper}}(cookieValue);{{/vendorExtensions.isEnum}}{{#vendorExtensions.isString}}params.m_{{paramName}} = cookieValue;{{/vendorExtensions.isString}}{{#vendorExtensions.isInt}}params.m_{{paramName}} = std::stoi(cookieValue);{{/vendorExtensions.isInt}}{{#vendorExtensions.isLong}}params.m_{{paramName}} = std::stoll(cookieValue);{{/vendorExtensions.isLong}}{{#vendorExtensions.isBool}}params.m_{{paramName}} = (cookieValue == "true");{{/vendorExtensions.isBool}}{{#vendorExtensions.isDouble}}params.m_{{paramName}} = std::stod(cookieValue);{{/vendorExtensions.isDouble}}{{#vendorExtensions.isFloat}}params.m_{{paramName}} = std::stof(cookieValue);{{/vendorExtensions.isFloat}}{{^vendorExtensions.isObject}}{{^vendorExtensions.isArray}}{{^vendorExtensions.isEnum}}{{^vendorExtensions.isString}}{{^vendorExtensions.isInt}}{{^vendorExtensions.isLong}}{{^vendorExtensions.isBool}}{{^vendorExtensions.isDouble}}{{^vendorExtensions.isFloat}}params.m_{{paramName}} = cookieValue;{{/vendorExtensions.isFloat}}{{/vendorExtensions.isDouble}}{{/vendorExtensions.isBool}}{{/vendorExtensions.isLong}}{{/vendorExtensions.isInt}}{{/vendorExtensions.isString}}{{/vendorExtensions.isEnum}}{{/vendorExtensions.isArray}}{{/vendorExtensions.isObject}}
+ }{{^vendorExtensions.isRequired}}{{#vendorExtensions.hasDefaultValue}}
+ else
+ {
+ // Use default value for optional parameter
+ params.m_{{paramName}} = {{{vendorExtensions.defaultValue}}};
+ }{{/vendorExtensions.hasDefaultValue}}{{/vendorExtensions.isRequired}}
+ }{{^vendorExtensions.isRequired}}{{#vendorExtensions.hasDefaultValue}}
+ else
+ {
+ // Use default value for optional parameter
+ params.m_{{paramName}} = {{{vendorExtensions.defaultValue}}};
+ }{{/vendorExtensions.hasDefaultValue}}{{/vendorExtensions.isRequired}}
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid cookie parameter '{{baseName}}': " + std::string(e.what()));
+ }
+{{/cookieParams}}
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+{{/hasAnyRequestSchema}}
+{{#hasAnyResponseSchema}}
+void {{apiClassnameInPascalCase}}::handle{{responseType}}(const {{responseType}}& result, httplib::Response& res)
+{
+{{#hasSingleResponseType}}
+ // Single response type
+ res.status = {{#successCodeToTypes}}{{successConstName}}{{/successCodeToTypes}}{{#errorCodeToTypes}}{{errorConstName}}{{/errorCodeToTypes}};
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+{{/hasSingleResponseType}}
+{{^hasSingleResponseType}}
+ std::visit([&](const auto& value)
+ {
+ using T = std::decay_t;
+{{#successCodeToTypes}}{{#-first}}
+ // Success types
+ {{/-first}}{{^-first}}else {{/-first}}if constexpr (std::is_same_v)
+ {
+ res.status = {{successConstName}};
+ nlohmann::json responseJson;
+ to_json(responseJson, value);
+ res.set_content(responseJson.dump(), "application/json");
+ }{{/successCodeToTypes}}{{#errorCodeToTypes}}{{#-first}}
+ // Error types{{/-first}}
+ else if constexpr (std::is_same_v)
+ {
+ res.status = {{errorConstName}};
+ nlohmann::json errorJson = value;
+ res.set_content(errorJson.dump(), "application/json");
+ }{{/errorCodeToTypes}}{{^successCodeToTypes}}{{^errorCodeToTypes}}
+ // No response types defined
+ res.status = {{statusCodeToConst.500}};
+ res.set_content("{\"error\": \"No response handler defined\"}", "application/json");
+{{/errorCodeToTypes}}{{/successCodeToTypes}}
+ }, result);
+{{/hasSingleResponseType}}
+}
+{{/hasAnyResponseSchema}}
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+
+{{#hasAnyAuth}}
+bool {{apiClassnameInPascalCase}}::performAuthentication(
+ const httplib::Request& req,
+ std::shared_ptr auth,
+ httplib::Response& res)
+{
+ if (!auth)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "AuthenticationManager not configured";
+ res.status = {{statusCodeToConst.500}};
+ res.set_content(errorJson.dump(), "application/json");
+ return false;
+ }
+
+ {{#hasApiKeyAuth}}
+ {{#apiKeyAuth}}
+ {{#isKeyInHeader}}
+ if (req.has_header("{{keyParamName}}"))
+ {
+ if (auth->validateApiKey(req.get_header_value("{{keyParamName}}"))) return true;
+ }
+ {{/isKeyInHeader}}
+ {{#isKeyInQuery}}
+ if (req.has_param("{{keyParamName}}"))
+ {
+ if (auth->validateApiKey(req.get_param_value("{{keyParamName}}"))) return true;
+ }
+ {{/isKeyInQuery}}
+ {{#isKeyInCookie}}
+ {
+ auto cookieHeader = req.get_header_value("Cookie");
+ std::string key = "{{keyParamName}}=";
+ size_t start = cookieHeader.find(key);
+ if (start != std::string::npos)
+ {
+ start += key.length();
+ size_t end = cookieHeader.find(";", start);
+ if (end == std::string::npos) end = cookieHeader.length();
+ std::string cookieValue = cookieHeader.substr(start, end - start);
+ if (auth->validateApiKey(cookieValue)) return true;
+ }
+ }
+ {{/isKeyInCookie}}
+ {{/apiKeyAuth}}
+ {{/hasApiKeyAuth}}
+
+ {{#hasBasicAuth}}
+ if (req.has_header("Authorization"))
+ {
+ auto authHeader = req.get_header_value("Authorization");
+ if (authHeader.find("Basic ") == 0)
+ {
+ std::string credentials = authHeader.substr(6);
+ if (auth->validateBasicAuth(credentials, "")) return true;
+ }
+ }
+ {{/hasBasicAuth}}
+
+ {{#hasBearerAuth}}
+ if (req.has_header("Authorization"))
+ {
+ auto authHeader = req.get_header_value("Authorization");
+ if (authHeader.find("Bearer ") == 0)
+ {
+ std::string token = authHeader.substr(7);
+ if (auth->validateBearerToken(token)) return true;
+ }
+ }
+ {{/hasBearerAuth}}
+
+ {{#hasOAuth2}}
+ if (req.has_header("Authorization"))
+ {
+ auto authHeader = req.get_header_value("Authorization");
+ if (authHeader.find("Bearer ") == 0)
+ {
+ std::string token = authHeader.substr(7);
+ {{#oauth2Auth}}
+ std::vector requiredScopes = { {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} };
+ if (auth->validateOAuth2(token, requiredScopes)) return true;
+ {{/oauth2Auth}}
+ }
+ }
+ {{/hasOAuth2}}
+
+ return false;
+}
+{{/hasAnyAuth}}
+
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions}}
+void {{apiClassnameInPascalCase}}::handle{{operationIdPascalCase}}Request([[maybe_unused]] const httplib::Request& req, httplib::Response& res{{#hasAuth}}, std::shared_ptr auth{{/hasAuth}})
+{
+ try
+ {
+{{#hasAuth}}
+ if (!performAuthentication(req, auth, res))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Authentication required";
+ res.status = {{statusCodeToConst.401}};
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+{{/hasAuth}}{{#hasAnyRequestSchema}}
+ {{requestType}} params;
+ std::vector paramErrors;
+ if (!parse{{operationIdPascalCase}}Params(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+{{#hasAnyResponseSchema}}
+ auto result = {{handlerFunctionName}}(params);
+ handle{{responseType}}(result, res);
+{{/hasAnyResponseSchema}}{{^hasAnyResponseSchema}}
+ {{handlerFunctionName}}(params);
+ res.status = {{statusCodeToConst.204}};
+{{/hasAnyResponseSchema}}{{/hasAnyRequestSchema}}{{^hasAnyRequestSchema}}{{#hasAnyResponseSchema}}
+ auto result = {{handlerFunctionName}}();
+ handle{{responseType}}(result, res);
+{{/hasAnyResponseSchema}}{{^hasAnyResponseSchema}}
+ {{handlerFunctionName}}();
+ res.status = {{statusCodeToConst.204}};
+{{/hasAnyResponseSchema}}{{/hasAnyRequestSchema}}
+ }{{#requestModel}}
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }{{/requestModel}}{{#vendorExtensions.hasPrimitiveParams}}
+ catch (const std::invalid_argument& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameter: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const std::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameter: " + std::string(e.what());
+ res.status = {{statusCodeToConst.400}};
+ res.set_content(errorJson.dump(), "application/json");
+ }{{/vendorExtensions.hasPrimitiveParams}}{{^requestModel}}{{^vendorExtensions.hasPrimitiveParams}}
+ catch (const std::exception& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Internal error: " + std::string(e.what());
+ res.status = {{statusCodeToConst.500}};
+ res.set_content(errorJson.dump(), "application/json");
+ }{{/vendorExtensions.hasPrimitiveParams}}{{/requestModel}}
+}
+
+{{/vendorExtensions}}
+{{/operation}}
+{{/operations}}
+
+void {{apiClassnameInPascalCase}}::registerRoutes(httplib::Server& svr{{#hasAnyAuth}}, std::shared_ptr auth{{/hasAnyAuth}})
+{
+ {{#operations}}
+ {{#operation}}
+ {{#vendorExtensions}}
+ svr.{{httpMethod}}("{{path}}", [this{{#hasAuth}}, auth{{/hasAuth}}]([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+ {
+ handle{{operationIdPascalCase}}Request(req, res{{#hasAuth}}, auth{{/hasAuth}});
+ });
+ {{/vendorExtensions}}
+ {{/operation}}
+ {{/operations}}
+}
+
+} // namespace {{apiNamespace}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/main.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/main.mustache
new file mode 100644
index 000000000000..c3b12ffc5640
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/main.mustache
@@ -0,0 +1,94 @@
+{{>License}}
+
+#include
+#include
+#include
+{{#apiInfo}}
+{{#apis}}
+{{#operations}}
+#include "api/{{apiClassnameInPascalCase}}Impl.h"
+{{/operations}}
+{{/apis}}
+{{/apiInfo}}
+{{#hasAuthMethods}}
+#include "api/AuthenticationManager.h"
+
+// Example AuthenticationManager implementation
+class SimpleAuthManager : public {{#apiInfo}}{{#apis}}{{#-first}}{{apiNamespace}}{{/-first}}{{/apis}}{{/apiInfo}}::AuthenticationManager {
+public:
+ bool validateApiKey(const std::string& key) override {
+ // TODO: Implement your API key validation logic
+ return key == "your-api-key"; // Replace with actual validation
+ }
+
+ bool validateBearerToken(const std::string& token) override {
+ // TODO: Implement your Bearer token validation logic
+ return !token.empty(); // Replace with actual validation
+ }
+
+ bool validateBasicAuth(const std::string& username, const std::string& password) override {
+ // TODO: Implement your Basic auth validation logic
+ return username == "admin" && password == "admin"; // Replace with actual validation
+ }
+
+ bool validateOAuth2(const std::string& token, const std::vector& scopes) override {
+ // TODO: Implement your OAuth2 validation logic
+ return !token.empty(); // Replace with actual validation
+ }
+};
+{{/hasAuthMethods}}
+
+int main() {
+ httplib::Server svr;
+
+ std::cout << "Starting {{projectName}} server..." << std::endl;
+
+{{#hasAuthMethods}}
+ // Create authentication manager
+ auto authManager = std::make_shared();
+{{/hasAuthMethods}}
+
+{{#apiInfo}}
+{{#apis}}
+{{#operations}}
+ // Register {{apiClassnameInPascalCase}} routes
+ auto {{#lambda.camelcase}}{{apiClassnameInPascalCase}}{{/lambda.camelcase}}Impl = std::make_shared<{{apiNamespace}}::{{apiClassnameInPascalCase}}Impl>();
+ {{#lambda.camelcase}}{{apiClassnameInPascalCase}}{{/lambda.camelcase}}Impl->registerRoutes(svr{{#hasAnyAuth}}, authManager{{/hasAnyAuth}});
+{{/operations}}
+{{/apis}}
+{{/apiInfo}}
+
+ // Health check endpoint
+ svr.Get("/health", [](const httplib::Request&, httplib::Response& res) {
+ res.set_content("{\"status\": \"ok\"}", "application/json");
+ });
+
+ // Configure server
+ const char* host = "0.0.0.0";
+ int port = 8080;
+
+ // Get port from environment variable if available
+ const char* portEnv = std::getenv("PORT");
+ if (portEnv != nullptr)
+ {
+ try
+ {
+ port = std::stoi(portEnv);
+ }
+ catch (...)
+ {
+ std::cerr << "Invalid PORT environment variable, using default: " << port << std::endl;
+ }
+ }
+
+ std::cout << "Server listening on " << host << ":" << port << std::endl;
+ std::cout << "Health check available at: http://" << host << ":" << port << "/health" << std::endl;
+
+ // Start server
+ if (!svr.listen(host, port)) {
+ std::cerr << "Failed to start server on " << host << ":" << port << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache
new file mode 100644
index 000000000000..c109c8ec073c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-header.mustache
@@ -0,0 +1,249 @@
+{{>License}}
+
+#pragma once
+{{#models}}
+{{#model}}
+// System headers
+#include
+{{#vendorExtensions.filteredImports}}
+{{{.}}}
+{{/vendorExtensions.filteredImports}}
+
+
+{{#parent}}{{#-first}}// Project headers{{/-first}}
+#include "{{parent}}.h"
+{{/parent}}
+
+namespace {{vendorExtensions.modelNamespace}} {
+
+{{#vendorExtensions.isOneOfSchema}}
+// oneOf schema - type alias for std::variant
+using {{vendorExtensions.modelClassName}} = std::variant<
+ {{#vendorExtensions.oneOfTypes}}{{.}}{{^-last}},
+ {{/-last}}{{/vendorExtensions.oneOfTypes}}>;
+
+// JSON serialization functions (inline for header-only)
+{{#vendorExtensions.discriminatorProperty}}
+inline void from_json(const nlohmann::json& j, {{vendorExtensions.modelClassName}}& obj) {
+ if (!j.contains("{{vendorExtensions.discriminatorProperty}}")) {
+ throw nlohmann::json::type_error::create(302, "Discriminator property '{{vendorExtensions.discriminatorProperty}}' not found in JSON", &j);
+ }
+ std::string discriminatorValue = j["{{vendorExtensions.discriminatorProperty}}"].get();
+ {{#vendorExtensions.discriminatorMapping}}
+ if (discriminatorValue == "{{value}}") {
+ obj = j.get<{{key}}>();
+ return;
+ }
+ {{/vendorExtensions.discriminatorMapping}}
+ throw nlohmann::json::type_error::create(302, "Unknown discriminator value: " + discriminatorValue, &j);
+}
+{{/vendorExtensions.discriminatorProperty}}
+{{^vendorExtensions.discriminatorProperty}}
+inline void from_json(const nlohmann::json& j, {{vendorExtensions.modelClassName}}& obj)
+{
+ {{#vendorExtensions.oneOfTypes}}
+ try
+ {
+ obj = j.get<{{.}}>();
+ return;
+ }
+ catch (const nlohmann::json::exception&)
+ {
+ // Type mismatch, try next variant alternative
+ }
+ {{/vendorExtensions.oneOfTypes}}
+ throw nlohmann::json::type_error::create(302, "Could not deserialize into any variant type of {{vendorExtensions.modelClassName}}", &j);
+}
+{{/vendorExtensions.discriminatorProperty}}
+
+inline void to_json(nlohmann::json& j, const {{vendorExtensions.modelClassName}}& obj)
+{
+ std::visit([&j](const auto& val) { j = nlohmann::json(val); }, obj);
+}
+
+{{/vendorExtensions.isOneOfSchema}}
+{{#vendorExtensions.isAnyOfSchema}}
+// anyOf schema - type alias for std::variant
+using {{vendorExtensions.modelClassName}} = std::variant<
+{{#vendorExtensions.anyOfTypes}} {{.}}{{^-last}},
+{{/-last}}{{/vendorExtensions.anyOfTypes}} >;
+
+// JSON serialization functions (inline for header-only)
+{{#vendorExtensions.discriminatorProperty}}
+inline void from_json(const nlohmann::json& j, {{vendorExtensions.modelClassName}}& obj) {
+ if (!j.contains("{{vendorExtensions.discriminatorProperty}}")) {
+ throw nlohmann::json::type_error::create(302, "Discriminator property '{{vendorExtensions.discriminatorProperty}}' not found in JSON", &j);
+ }
+ std::string discriminatorValue = j["{{vendorExtensions.discriminatorProperty}}"].get();
+ {{#vendorExtensions.discriminatorMapping}}
+ if (discriminatorValue == "{{value}}") {
+ obj = j.get<{{key}}>();
+ return;
+ }
+ {{/vendorExtensions.discriminatorMapping}}
+ throw nlohmann::json::type_error::create(302, "Unknown discriminator value: " + discriminatorValue, &j);
+}
+{{/vendorExtensions.discriminatorProperty}}
+{{^vendorExtensions.discriminatorProperty}}
+inline void from_json(const nlohmann::json& j, {{vendorExtensions.modelClassName}}& obj)
+{
+ {{#vendorExtensions.anyOfTypes}}
+ try
+ {
+ obj = j.get<{{.}}>();
+ return;
+ }
+ catch (const nlohmann::json::exception&)
+ {
+ // Type mismatch, try next variant alternative
+ }
+ {{/vendorExtensions.anyOfTypes}}
+ throw nlohmann::json::type_error::create(302, "Could not deserialize into any variant type of {{vendorExtensions.modelClassName}}", &j);
+}
+{{/vendorExtensions.discriminatorProperty}}
+
+inline void to_json(nlohmann::json& j, const {{vendorExtensions.modelClassName}}& obj)
+{
+ std::visit([&j](const auto& val) { j = nlohmann::json(val); }, obj);
+}
+
+{{/vendorExtensions.isAnyOfSchema}}
+{{^vendorExtensions.isOneOfSchema}}
+{{^vendorExtensions.isAnyOfSchema}}
+{{#vars}}
+{{#isUnionType}}
+// Union type for anyOf/oneOf property
+using {{unionTypeName}} = std::variant<
+{{#unionTypes}} {{.}}{{^-last}},{{/-last}}
+{{/unionTypes}}>;
+
+// Inline serialization for union type
+inline void to_json(nlohmann::json& j, const {{unionTypeName}}& v)
+{
+ std::visit([&j](const auto& val) { j = nlohmann::json(val); }, v);
+}
+
+inline void from_json(const nlohmann::json& j, {{unionTypeName}}& v)
+{
+ {{#unionTypes}}
+ try
+ {
+ v = j.get<{{.}}>();
+ return;
+ }
+ catch (...)
+ {
+ // Try next type
+ }
+ {{/unionTypes}}
+ throw nlohmann::json::type_error::create(302, "Could not deserialize into any variant type of {{unionTypeName}}", &j);
+}
+
+{{/isUnionType}}
+{{/vars}}
+
+class {{vendorExtensions.modelClassName}}{{#parent}} : public {{parent}}{{/parent}}
+{
+public:
+ {{#vars}}
+ {{#isEnum}}
+ {{#-first}}
+ // ===================
+ // ===== Enums =======
+ // ===================
+ {{/-first}}
+ enum class {{enumName}} {
+ {{#allowableValues.values}}
+ {{.}}{{^-last}},{{/-last}}
+ {{/allowableValues.values}}
+ };
+
+ // Enum conversion functions (definitions in .cpp)
+ static std::string {{enumName}}ToString({{enumName}} value);
+ static {{enumName}} {{enumName}}FromString(const std::string& str);
+
+ {{/isEnum}}
+ {{/vars}}
+
+ {{vendorExtensions.modelClassName}}();
+ virtual ~{{vendorExtensions.modelClassName}}() = default;
+
+ // Getters and setters
+ {{#vars}}
+ [[nodiscard]] {{#isUnionType}}{{unionTypeName}}{{/isUnionType}}{{^isUnionType}}{{{datatypeWithEnum}}}{{/isUnionType}} {{getter}}() const;
+ void {{setter}}(const {{#isUnionType}}{{unionTypeName}}{{/isUnionType}}{{^isUnionType}}{{{datatypeWithEnum}}}{{/isUnionType}}& {{nameInCamelCase}});
+ {{/vars}}
+{{#vars}}{{#isEnum}}{{^isArray}}{{#-first}}
+ // =============================================================================
+ // Friend Enum Serialization Helpers (Required for NLOHMANN JSON serialization)
+ // =============================================================================
+ // These friend functions must be declared inside the class because:
+ // 1. The enum types are nested and don't exist before the class is defined
+ // 2. NLOHMANN macros need custom serialization for enum types (uses ToString/FromString)
+ // 3. The 'friend' keyword makes them free functions in the namespace (not member functions)
+ // so ADL (Argument-Dependent Lookup) can find them during JSON serialization
+ // =============================================================================
+
+{{/-first}}{{^vendorExtensions.isOptionalEnum}}
+ // Serialization helpers for {{enumName}}
+ friend inline void to_json(nlohmann::json& j, const {{enumName}}& e)
+ {
+ j = {{enumName}}ToString(e);
+ }
+
+ friend inline void from_json(const nlohmann::json& j, {{enumName}}& e)
+ {
+ e = {{enumName}}FromString(j.get());
+ }
+{{/vendorExtensions.isOptionalEnum}}
+{{#vendorExtensions.isOptionalEnum}}
+ // Serialization helpers for std::optional<{{enumName}}>
+ friend inline void to_json(nlohmann::json& j, const std::optional<{{enumName}}>& opt)
+ {
+ if (opt.has_value())
+ {
+ j = {{enumName}}ToString(opt.value());
+ }
+ else
+ {
+ j = nullptr;
+ }
+ }
+
+ friend inline void from_json(const nlohmann::json& j, std::optional<{{enumName}}>& opt)
+ {
+ if (j.is_null())
+ {
+ opt = std::nullopt;
+ }
+ else
+ {
+ opt = {{enumName}}FromString(j.get());
+ }
+ }
+{{/vendorExtensions.isOptionalEnum}}
+
+{{/isArray}}{{/isEnum}}{{/vars}}
+
+ // JSON serialization using NLOHMANN INTRUSIVE macro (must be inside class to access private members)
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE({{vendorExtensions.modelClassName}},
+ {{#vars}} {{nameInCamelCase}}{{^-last}},{{/-last}}{{/vars}})
+
+private:
+ {{#vars}}
+ {{#isUnionType}}
+ {{unionTypeName}} {{nameInCamelCase}};
+ {{/isUnionType}}
+ {{^isUnionType}}
+ {{{datatypeWithEnum}}} {{nameInCamelCase}};
+ {{/isUnionType}}
+ {{/vars}}
+};
+
+{{/vendorExtensions.isAnyOfSchema}}
+{{/vendorExtensions.isOneOfSchema}}
+
+} // namespace {{vendorExtensions.modelNamespace}}
+
+{{/model}}
+{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache
new file mode 100644
index 000000000000..414becd4a421
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/cpp-httplib-server/model-source.mustache
@@ -0,0 +1,83 @@
+
+{{>License}}
+
+{{#models}}
+{{#model}}
+{{^vendorExtensions.isOneOfSchema}}
+{{^vendorExtensions.isAnyOfSchema}}
+#include "{{vendorExtensions.modelClassName}}.h"
+
+namespace {{vendorExtensions.modelNamespace}} {
+
+{{vendorExtensions.modelClassName}}::{{vendorExtensions.modelClassName}}()
+{{#vars}}
+{{#defaultValue}}
+{{#-first}}:{{/-first}}{{^-first}},{{/-first}} {{nameInCamelCase}}({{{defaultValue}}})
+{{/defaultValue}}
+{{/vars}}
+{
+}
+// =========================================
+// ===== Getters/Setters =====
+// =========================================
+{{#vars}}
+{{#-first}}
+// ===================
+// ===== Getters =====
+// ===================
+{{/-first}}
+{{! For enums, construct return type with proper class qualification }}
+{{#isEnum}}{{#isContainer}}std::vector<{{model.vendorExtensions.modelClassName}}::{{enumName}}>{{/isContainer}}{{^isContainer}}{{#vendorExtensions.isOptionalEnum}}std::optional<{{model.vendorExtensions.modelClassName}}::{{enumName}}>{{/vendorExtensions.isOptionalEnum}}{{^vendorExtensions.isOptionalEnum}}{{model.vendorExtensions.modelClassName}}::{{enumName}}{{/vendorExtensions.isOptionalEnum}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{datatypeWithEnum}}}{{/isEnum}} {{model.vendorExtensions.modelClassName}}::{{getter}}() const
+{
+ return {{nameInCamelCase}};
+}
+{{/vars}}
+
+{{#vars}}
+{{#-first}}
+// ===================
+// ===== Setters =====
+// ===================
+{{/-first}}
+void {{model.vendorExtensions.modelClassName}}::{{setter}}(const {{{datatypeWithEnum}}}& {{nameInCamelCase}}Obj)
+{
+ {{nameInCamelCase}} = {{nameInCamelCase}}Obj;
+}
+{{/vars}}
+
+{{#vars}}{{#isEnum}}{{^isArray}}
+// =========================================
+// ===== Enum {{enumName}} Conversions =====
+// =========================================
+std::string {{classname}}::{{enumName}}ToString({{classname}}::{{enumName}} value)
+{
+ switch (value)
+ {
+ {{#vendorExtensions.enumCases}}
+ case {{enumName}}::{{name}}: return "{{value}}";
+ {{/vendorExtensions.enumCases}}
+ default: return {};
+ }
+}
+
+{{classname}}::{{enumName}} {{classname}}::{{enumName}}FromString(const std::string& str)
+{
+ {{#vendorExtensions.enumCases}}
+ if (str == "{{value}}")
+ {
+ return {{enumName}}::{{name}};
+ }
+ {{/vendorExtensions.enumCases}}
+ throw std::invalid_argument("Invalid enum value");
+}
+{{/isArray}}{{/isEnum}}{{/vars}}
+
+
+
+{{/vendorExtensions.isAnyOfSchema}}
+{{/vendorExtensions.isOneOfSchema}}
+{{^vendorExtensions.isOneOfSchema}}{{^vendorExtensions.isAnyOfSchema}}
+} // namespace {{vendorExtensions.modelNamespace}}
+{{/vendorExtensions.isAnyOfSchema}}{{/vendorExtensions.isOneOfSchema}}
+{{/model}}
+{{/models}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenModelTest.java
new file mode 100644
index 000000000000..72d9e5cb5a5c
--- /dev/null
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenModelTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2026 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.cpphttplib;
+
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.languages.CppHttplibServerCodegen;
+import io.swagger.v3.oas.models.media.*;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("static-method")
+public class CppHttplibServerCodegenModelTest {
+ @Test(description = "convert model with enum property")
+ public void enumPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+ StringSchema statusSchema = new StringSchema();
+ statusSchema.setEnum(java.util.Arrays.asList("available", "pending", "sold"));
+ schema.addProperty("status", statusSchema);
+
+ final CodegenModel model = codegen.fromModel("PetStatus", schema);
+
+ Assert.assertEquals(model.name, "PetStatus");
+ Assert.assertNotNull(model.vars);
+ Assert.assertEquals(model.vars.size(), 1);
+
+ CodegenProperty statusProp = model.vars.get(0);
+ // Check that isEnum flag is set
+ Assert.assertTrue(statusProp.isEnum, "isEnum flag should be true for enum properties");
+ // Check vendor extensions for enum handling
+ Assert.assertNotNull(statusProp.vendorExtensions, "vendorExtensions should not be null");
+ Assert.assertTrue((boolean) statusProp.vendorExtensions.getOrDefault("isEnum", false),
+ "isEnum vendor extension should be true");
+ // Check enum values
+ java.util.List> enumValues = (java.util.List>) statusProp.vendorExtensions.getOrDefault("values", statusProp._enum);
+ Assert.assertNotNull(enumValues, "enum values should be present");
+ Assert.assertEquals(enumValues.size(), 3);
+ }
+
+ @Test(description = "convert a simple model with properties")
+ public void simpleModelTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ // Create a simple schema with properties
+ ObjectSchema schema = new ObjectSchema();
+ schema.description("a sample model");
+ schema.addProperty("id", new IntegerSchema().format("int64"));
+ schema.addProperty("name", new StringSchema());
+ schema.addProperty("isActive", new BooleanSchema());
+ schema.setRequired(java.util.Arrays.asList("id", "name"));
+
+ final CodegenModel model = codegen.fromModel("User", schema);
+
+ Assert.assertEquals(model.name, "User");
+ Assert.assertEquals(model.classname, "User");
+ Assert.assertNotNull(model.vars);
+ Assert.assertEquals(model.vars.size(), 3);
+
+ // Check id property
+ CodegenProperty idProp = model.vars.get(0);
+ Assert.assertEquals(idProp.name, "Id");
+ Assert.assertEquals(idProp.baseName, "id");
+ Assert.assertEquals(idProp.dataType, "long");
+ Assert.assertTrue(idProp.required);
+
+ // Check name property
+ CodegenProperty nameProp = model.vars.get(1);
+ Assert.assertEquals(nameProp.name, "Name");
+ Assert.assertEquals(nameProp.baseName, "name");
+ Assert.assertEquals(nameProp.dataType, "std::string");
+ Assert.assertTrue(nameProp.required);
+
+ // Check isActive property
+ CodegenProperty activeProp = model.vars.get(2);
+ Assert.assertEquals(activeProp.name, "IsActive");
+ Assert.assertEquals(activeProp.baseName, "isActive");
+ Assert.assertEquals(activeProp.dataType, "bool");
+ Assert.assertFalse(activeProp.required);
+ }
+
+ @Test(description = "convert model with array property")
+ public void arrayPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+ schema.addProperty("tags", new ArraySchema().items(new StringSchema()));
+
+ final CodegenModel model = codegen.fromModel("ModelWithArray", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty arrayProp = model.vars.get(0);
+ Assert.assertEquals(arrayProp.baseName, "tags");
+ Assert.assertEquals(arrayProp.dataType, "std::vector");
+ Assert.assertTrue(arrayProp.isArray);
+ // Verify array vendor extensions are set
+ Assert.assertTrue((boolean) arrayProp.vendorExtensions.getOrDefault("isArray", false),
+ "isArray vendor extension should be true");
+ }
+
+ @Test(description = "convert model with array of enums")
+ public void arrayOfEnumsPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+ StringSchema enumItemSchema = new StringSchema();
+ enumItemSchema.setEnum(java.util.Arrays.asList("ACTIVE", "INACTIVE", "PENDING"));
+ schema.addProperty("statuses", new ArraySchema().items(enumItemSchema));
+
+ final CodegenModel model = codegen.fromModel("ModelWithEnumArray", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty arrayProp = model.vars.get(0);
+ Assert.assertTrue(arrayProp.isArray);
+ Assert.assertTrue((boolean) arrayProp.vendorExtensions.getOrDefault("isArray", false));
+ // Verify it detects array of enums
+ Assert.assertTrue((boolean) arrayProp.vendorExtensions.getOrDefault("isArrayOfEnum", false),
+ "isArrayOfEnum vendor extension should be true");
+ }
+
+ @Test(description = "convert model with map property")
+ public void mapPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+ schema.addProperty("metadata", new MapSchema().additionalProperties(new StringSchema()));
+
+ final CodegenModel model = codegen.fromModel("ModelWithMap", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty mapProp = model.vars.get(0);
+ Assert.assertEquals(mapProp.name, "Metadata");
+ Assert.assertEquals(mapProp.dataType, "std::map");
+ Assert.assertTrue(mapProp.isMap);
+ // Verify map container flag
+ Assert.assertTrue((boolean) mapProp.vendorExtensions.getOrDefault("isContainer", false),
+ "isContainer vendor extension should be true for maps");
+ }
+
+ @Test(description = "convert model with numeric enum property")
+ public void numericEnumPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+ IntegerSchema statusSchema = new IntegerSchema();
+ statusSchema.setEnum(java.util.Arrays.asList(0, 1, 2));
+ schema.addProperty("userStatus", statusSchema);
+
+ final CodegenModel model = codegen.fromModel("UserStatusModel", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty statusProp = model.vars.get(0);
+ Assert.assertTrue(statusProp.isEnum);
+ Assert.assertTrue((boolean) statusProp.vendorExtensions.getOrDefault("isEnum", false));
+ // Numeric enum values should be converted to valid C++ names (prefixed with underscore)
+ java.util.List> enumValues = (java.util.List>) statusProp.vendorExtensions.getOrDefault("values", statusProp._enum);
+ Assert.assertNotNull(enumValues);
+ // Check that numeric values are properly converted
+ Assert.assertTrue(enumValues.stream().anyMatch(v -> v.toString().startsWith("_")),
+ "Numeric enum values should be prefixed with underscore");
+ }
+
+ @Test(description = "convert enum model")
+ public void enumModelTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ StringSchema enumSchema = new StringSchema();
+ enumSchema.setEnum(java.util.Arrays.asList("ACTIVE", "INACTIVE", "PENDING"));
+
+ final CodegenModel model = codegen.fromModel("Status", enumSchema);
+
+ // Note: The C++ httplib server generator may not process enum-only models
+ // in the same way as regular object models. The model might be null or empty.
+ if (model != null) {
+ Assert.assertEquals(model.name, "Status");
+ // Check if it's marked as an enum in vendor extensions
+ if (model.vendorExtensions.containsKey("x-is-enum")) {
+ Assert.assertEquals(model.vendorExtensions.get("x-is-enum"), true);
+ }
+ }
+ }
+
+ @Test(description = "convert model with nullable property")
+ public void nullablePropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ StringSchema nullableStringSchema = new StringSchema();
+ nullableStringSchema.setNullable(true);
+
+ ObjectSchema schema = new ObjectSchema();
+ schema.addProperty("optionalField", nullableStringSchema);
+
+ final CodegenModel model = codegen.fromModel("ModelWithNullable", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty nullableProp = model.vars.get(0);
+ Assert.assertEquals(nullableProp.name, "OptionalField");
+ Assert.assertTrue(nullableProp.isNullable);
+ // Check that isOptional vendor extension is set
+ Assert.assertTrue((boolean) nullableProp.vendorExtensions.getOrDefault("isOptional", false),
+ "isOptional vendor extension should be true for nullable fields");
+ }
+
+ @Test(description = "convert model with nested object property")
+ public void nestedObjectPropertyTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema nestedSchema = new ObjectSchema();
+ nestedSchema.addProperty("id", new IntegerSchema());
+ nestedSchema.addProperty("name", new StringSchema());
+
+ ObjectSchema schema = new ObjectSchema();
+ schema.addProperty("user", nestedSchema);
+
+ final CodegenModel model = codegen.fromModel("ModelWithNested", schema);
+
+ Assert.assertEquals(model.vars.size(), 1);
+ CodegenProperty nestedProp = model.vars.get(0);
+ Assert.assertEquals(nestedProp.name, "User");
+ Assert.assertTrue(nestedProp.isModel);
+ }
+
+ @Test(description = "convert model with composed schema (allOf)")
+ public void composedSchemaTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ // Create base schema
+ ObjectSchema baseSchema = new ObjectSchema();
+ baseSchema.addProperty("id", new IntegerSchema());
+ baseSchema.addProperty("name", new StringSchema());
+
+ // Create allOf with additional properties
+ io.swagger.v3.oas.models.media.ComposedSchema composedSchema = new io.swagger.v3.oas.models.media.ComposedSchema();
+ composedSchema.addAllOfItem(new Schema<>().$ref("#/components/schemas/Base"));
+ composedSchema.addProperty("email", new StringSchema());
+
+ final CodegenModel model = codegen.fromModel("ExtendedUser", composedSchema);
+
+ Assert.assertNotNull(model);
+ Assert.assertEquals(model.name, "ExtendedUser");
+ }
+
+ @Test(description = "convert model with complex types")
+ public void complexTypesTest() {
+ final CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+ codegen.processOpts();
+
+ ObjectSchema schema = new ObjectSchema();
+
+ // Add various property types
+ schema.addProperty("intValue", new IntegerSchema());
+ schema.addProperty("longValue", new IntegerSchema().format("int64"));
+ schema.addProperty("doubleValue", new NumberSchema());
+ schema.addProperty("floatValue", new NumberSchema().format("float"));
+ schema.addProperty("boolValue", new BooleanSchema());
+ schema.addProperty("dateValue", new DateSchema());
+ schema.addProperty("dateTimeValue", new DateTimeSchema());
+
+ final CodegenModel model = codegen.fromModel("ComplexModel", schema);
+
+ Assert.assertEquals(model.vars.size(), 7);
+
+ // Verify type mappings
+ java.util.Map dataTypeMap = new java.util.HashMap<>();
+ for (CodegenProperty var : model.vars) {
+ dataTypeMap.put(var.baseName, var.dataType);
+ }
+
+ Assert.assertEquals(dataTypeMap.get("intValue"), "int");
+ Assert.assertEquals(dataTypeMap.get("longValue"), "long");
+ Assert.assertEquals(dataTypeMap.get("doubleValue"), "double");
+ Assert.assertEquals(dataTypeMap.get("floatValue"), "float");
+ Assert.assertEquals(dataTypeMap.get("boolValue"), "bool");
+ Assert.assertEquals(dataTypeMap.get("dateValue"), "std::string");
+ Assert.assertEquals(dataTypeMap.get("dateTimeValue"), "std::string");
+ }
+}
+
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenTest.java
new file mode 100644
index 000000000000..1496f6e9d582
--- /dev/null
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2026 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.cpphttplib;
+
+import static org.openapitools.codegen.utils.StringUtils.underscore;
+
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.languages.CppHttplibServerCodegen;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class CppHttplibServerCodegenTest {
+
+ CppHttplibServerCodegen codegen = new CppHttplibServerCodegen();
+
+ @Test
+ public void testInitialConfigValues() throws Exception {
+ codegen.processOpts();
+
+ Assert.assertEquals(codegen.additionalProperties().get("projectName"), "cpp-httplib-server");
+ Assert.assertEquals(codegen.modelPackage(), "cpp-httplib-server");
+ Assert.assertEquals(codegen.additionalProperties().get("cmakeProjectName"), "cpp_httplib_server");
+ }
+
+ @Test
+ public void testSettersForConfigValues() throws Exception {
+ codegen.additionalProperties().put("projectName", "TestProject");
+ codegen.additionalProperties().put("modelNamespace", "test::models");
+ codegen.additionalProperties().put("apiNamespace", "test::apis");
+ codegen.processOpts();
+
+ Assert.assertEquals(codegen.additionalProperties().get("projectName"), "TestProject");
+ Assert.assertEquals(codegen.additionalProperties().get("modelNamespace"), "test::models");
+ Assert.assertEquals(codegen.additionalProperties().get("apiNamespace"), "test::apis");
+ }
+
+ @Test
+ public void testToModelName() {
+ Assert.assertEquals(codegen.toModelName("User"), "User");
+ Assert.assertEquals(codegen.toModelName("user"), "User");
+ Assert.assertEquals(codegen.toModelName("user_model"), "UserModel");
+ Assert.assertEquals(codegen.toModelName("object"), "InlineModel");
+ Assert.assertEquals(codegen.toModelName("object_1"), "InlineModel1");
+ Assert.assertEquals(codegen.toModelName("inline_object_1"), "InlineModel1");
+ Assert.assertEquals(codegen.toModelName("_inline_model"), "Model");
+ }
+
+ @Test
+ public void testToApiName() {
+ Assert.assertEquals(codegen.toApiName("users"), "Users");
+ Assert.assertEquals(codegen.toApiName("testproject"), "Testproject");
+ }
+
+ @Test
+ public void testGetTypeDeclaration() {
+ Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.StringSchema()), "std::string");
+ Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.IntegerSchema()), "int");
+ Assert.assertEquals(codegen.getTypeDeclaration(new io.swagger.v3.oas.models.media.BooleanSchema()), "bool");
+
+ io.swagger.v3.oas.models.media.ArraySchema arraySchema = new io.swagger.v3.oas.models.media.ArraySchema();
+ arraySchema.setItems(new io.swagger.v3.oas.models.media.StringSchema());
+ Assert.assertEquals(codegen.getTypeDeclaration(arraySchema), "std::vector");
+ }
+
+ @Test
+ public void testStringUtilityMethods() {
+ Assert.assertEquals(codegen.toPascalCase("user_name"), "UserName");
+ Assert.assertEquals(codegen.toPascalCase("userName"), "UserName");
+ Assert.assertEquals(codegen.toCamelCase("user_name"), "userName");
+ Assert.assertEquals(codegen.toPascalCase("UserName"), "UserName");
+ Assert.assertEquals(codegen.toCamelCase("UserName"), "userName");
+ Assert.assertEquals(underscore("UserName"), "user_name");
+ }
+
+ @Test
+ public void testInlineObjectNaming() {
+ // Test the enhanced inline object naming that we implemented
+ Assert.assertEquals(codegen.toModelName("object"), "InlineModel");
+ Assert.assertEquals(codegen.toModelName("object_1"), "InlineModel1");
+ Assert.assertEquals(codegen.toModelName("object_123"), "InlineModel123");
+ Assert.assertEquals(codegen.toModelName("inline_object"), "InlineModel");
+ Assert.assertEquals(codegen.toModelName("inline_object_5"), "InlineModel5");
+ }
+
+ @Test
+ public void testModelFilename() {
+ Assert.assertEquals(codegen.toModelFilename("User"), "User");
+ Assert.assertEquals(codegen.toModelFilename("UserModel"), "UserModel");
+ Assert.assertEquals(codegen.toModelFilename("object"), "Object");
+ Assert.assertEquals(codegen.toModelFilename("usermodel"), "Usermodel");
+ }
+
+ @Test
+ public void testApiFilename() {
+ Assert.assertEquals(codegen.toApiFilename("User"), "UserApi");
+ Assert.assertEquals(codegen.toApiFilename("Default"), "DefaultApi");
+ }
+
+ @Test
+ public void testTypeDeclarationWithAnyOf() {
+ io.swagger.v3.oas.models.media.ComposedSchema anyOfSchema = new io.swagger.v3.oas.models.media.ComposedSchema();
+ anyOfSchema.addAnyOfItem(new io.swagger.v3.oas.models.media.StringSchema());
+ anyOfSchema.addAnyOfItem(new io.swagger.v3.oas.models.media.IntegerSchema());
+
+ String result = codegen.getTypeDeclaration(anyOfSchema);
+ Assert.assertTrue(result.contains("std::variant"), "anyOf should generate std::variant");
+ }
+
+ @Test
+ public void testTypeDeclarationWithOneOf() {
+ io.swagger.v3.oas.models.media.ComposedSchema oneOfSchema = new io.swagger.v3.oas.models.media.ComposedSchema();
+ oneOfSchema.addOneOfItem(new io.swagger.v3.oas.models.media.StringSchema());
+ oneOfSchema.addOneOfItem(new io.swagger.v3.oas.models.media.NumberSchema());
+
+ String result = codegen.getTypeDeclaration(oneOfSchema);
+ Assert.assertTrue(result.contains("std::variant"), "oneOf should generate std::variant");
+ }
+
+ @Test
+ public void testModelNamespaceHandling() {
+ codegen.additionalProperties().put("modelNamespace", "myapp::models");
+ codegen.processOpts();
+
+ Assert.assertEquals(codegen.additionalProperties().get("modelNamespace"), "myapp::models");
+ }
+
+ @Test
+ public void testApiNamespaceHandling() {
+ codegen.additionalProperties().put("apiNamespace", "myapp::api");
+ codegen.processOpts();
+
+ Assert.assertEquals(codegen.additionalProperties().get("apiNamespace"), "myapp::api");
+ }
+}
diff --git a/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/feature-test.json b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/feature-test.json
new file mode 100644
index 000000000000..2ffb304ceb53
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/feature-test.json
@@ -0,0 +1,1279 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": "C++ httplib Server Feature Test API",
+ "description": "Comprehensive test specification for all cpp-httplib-server generator features",
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "http://localhost:8080/api/v1"
+ }
+ ],
+ "tags": [
+ {
+ "name": "datatypes",
+ "description": "Test all data types"
+ },
+ {
+ "name": "parameters",
+ "description": "Test parameter types and styles"
+ },
+ {
+ "name": "composition",
+ "description": "Test schema composition (allOf, anyOf, oneOf)"
+ },
+ {
+ "name": "security",
+ "description": "Test security schemes"
+ }
+ ],
+ "paths": {
+ "/datatypes/primitives": {
+ "post": {
+ "tags": ["datatypes"],
+ "operationId": "testPrimitiveTypes",
+ "summary": "Test all primitive data types",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PrimitiveTypes"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PrimitiveTypes"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/datatypes/arrays": {
+ "post": {
+ "tags": ["datatypes"],
+ "operationId": "testArrayTypes",
+ "summary": "Test array data types",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ArrayTypes"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ArrayTypes"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/datatypes/enums": {
+ "post": {
+ "tags": ["datatypes"],
+ "operationId": "testEnumTypes",
+ "summary": "Test enum types including numeric enums",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/EnumTypes"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/EnumTypes"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/parameters/query/{pathId}": {
+ "get": {
+ "tags": ["parameters"],
+ "operationId": "testQueryParameters",
+ "summary": "Test all query parameter types and styles",
+ "parameters": [
+ {
+ "name": "pathId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "stringParam",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "intParam",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "default": 42
+ }
+ },
+ {
+ "name": "boolParam",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "arrayParam",
+ "in": "query",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "name": "spaceDelimited",
+ "in": "query",
+ "required": false,
+ "style": "spaceDelimited",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "name": "pipeDelimited",
+ "in": "query",
+ "required": false,
+ "style": "pipeDelimited",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ },
+ {
+ "name": "deepObject",
+ "in": "query",
+ "required": false,
+ "style": "deepObject",
+ "explode": true,
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "age": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/parameters/headers": {
+ "get": {
+ "tags": ["parameters"],
+ "operationId": "testHeaderParameters",
+ "summary": "Test header parameters",
+ "parameters": [
+ {
+ "name": "X-Api-Version",
+ "in": "header",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "X-Request-Id",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "X-Rate-Limit",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "X-Tags",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "NoSuccess",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/parameters/cookies": {
+ "get": {
+ "tags": ["parameters"],
+ "operationId": "testCookieParameters",
+ "summary": "Test cookie parameters",
+ "parameters": [
+ {
+ "name": "sessionId",
+ "in": "cookie",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userId",
+ "in": "cookie",
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "preferences",
+ "in": "cookie",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/parameters/combined/{resourceId}": {
+ "post": {
+ "tags": ["parameters"],
+ "operationId": "testAllParameterTypes",
+ "summary": "Test all parameter types combined (path + query + header + cookie + body)",
+ "parameters": [
+ {
+ "name": "resourceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "filter",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "default": 10
+ }
+ },
+ {
+ "name": "X-Correlation-Id",
+ "in": "header",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "X-Client-Version",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "authToken",
+ "in": "cookie",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userPrefs",
+ "in": "cookie",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SimpleObject"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "resourceId": {
+ "type": "integer"
+ },
+ "filter": {
+ "type": "string"
+ },
+ "correlationId": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/composition/allof": {
+ "post": {
+ "tags": ["composition"],
+ "operationId": "testAllOfComposition",
+ "summary": "Test allOf schema composition",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Dog"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Dog"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/composition/anyof": {
+ "post": {
+ "tags": ["composition"],
+ "operationId": "testAnyOfComposition",
+ "summary": "Test anyOf schema composition",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StringOrNumber"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StringOrNumber"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/composition/oneof": {
+ "post": {
+ "tags": ["composition"],
+ "operationId": "testOneOfComposition",
+ "summary": "Test oneOf schema composition",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaymentMethod"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaymentMethod"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/security/apikey": {
+ "get": {
+ "tags": ["security"],
+ "operationId": "testApiKeySecurity",
+ "summary": "Test API Key authentication",
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/security/bearer": {
+ "get": {
+ "tags": ["security"],
+ "operationId": "testBearerSecurity",
+ "summary": "Test Bearer token authentication",
+ "security": [
+ {
+ "BearerAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/security/basic": {
+ "get": {
+ "tags": ["security"],
+ "operationId": "testBasicSecurity",
+ "summary": "Test Basic authentication",
+ "security": [
+ {
+ "BasicAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/security/oauth2": {
+ "get": {
+ "tags": ["security"],
+ "operationId": "testOAuth2Security",
+ "summary": "Test OAuth2 authentication",
+ "security": [
+ {
+ "OAuth2": ["read:users", "write:users"]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/responses/multiple": {
+ "get": {
+ "tags": ["datatypes"],
+ "operationId": "testMultipleResponses",
+ "summary": "Test multiple response status codes",
+ "parameters": [
+ {
+ "name": "scenario",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "enum": ["success", "created", "notfound", "error"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SuccessResponse"
+ }
+ }
+ }
+ },
+ "201": {
+ "description": "Created",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreatedResponse"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NotFoundResponse"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/responses/nocontent": {
+ "delete": {
+ "tags": ["datatypes"],
+ "operationId": "testNoContentResponse",
+ "summary": "Test 204 No Content response",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content - successful deletion"
+ }
+ }
+ }
+ },
+ "/nullable/optional": {
+ "post": {
+ "tags": ["datatypes"],
+ "operationId": "testNullableOptional",
+ "summary": "Test nullable and optional fields",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NullableOptionalTypes"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NullableOptionalTypes"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/nested/objects": {
+ "post": {
+ "tags": ["datatypes"],
+ "operationId": "testNestedObjects",
+ "summary": "Test deeply nested object structures",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Company"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Company"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "ApiKeyAuth": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "X-API-Key"
+ },
+ "BearerAuth": {
+ "type": "http",
+ "scheme": "bearer"
+ },
+ "BasicAuth": {
+ "type": "http",
+ "scheme": "basic"
+ },
+ "OAuth2": {
+ "type": "oauth2",
+ "flows": {
+ "authorizationCode": {
+ "authorizationUrl": "https://example.com/oauth/authorize",
+ "tokenUrl": "https://example.com/oauth/token",
+ "scopes": {
+ "read:users": "Read user data",
+ "write:users": "Write user data"
+ }
+ }
+ }
+ }
+ },
+ "schemas": {
+ "PrimitiveTypes": {
+ "type": "object",
+ "required": ["stringField", "intField", "boolField"],
+ "properties": {
+ "stringField": {
+ "type": "string",
+ "description": "A string field"
+ },
+ "intField": {
+ "type": "integer",
+ "description": "An integer field"
+ },
+ "longField": {
+ "type": "integer",
+ "format": "int64",
+ "description": "A long field"
+ },
+ "floatField": {
+ "type": "number",
+ "format": "float",
+ "description": "A float field"
+ },
+ "doubleField": {
+ "type": "number",
+ "format": "double",
+ "description": "A double field"
+ },
+ "boolField": {
+ "type": "boolean",
+ "description": "A boolean field"
+ },
+ "byteField": {
+ "type": "string",
+ "format": "byte",
+ "description": "A byte field"
+ },
+ "binaryField": {
+ "type": "string",
+ "format": "binary",
+ "description": "A binary field"
+ },
+ "dateField": {
+ "type": "string",
+ "format": "date",
+ "description": "A date field"
+ },
+ "dateTimeField": {
+ "type": "string",
+ "format": "date-time",
+ "description": "A datetime field"
+ },
+ "passwordField": {
+ "type": "string",
+ "format": "password",
+ "description": "A password field"
+ }
+ }
+ },
+ "ArrayTypes": {
+ "type": "object",
+ "properties": {
+ "stringArray": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "intArray": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "objectArray": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/SimpleObject"
+ }
+ },
+ "enumArray": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["red", "green", "blue"]
+ }
+ },
+ "nestedArray": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "EnumTypes": {
+ "type": "object",
+ "properties": {
+ "stringEnum": {
+ "type": "string",
+ "enum": ["pending", "approved", "rejected"]
+ },
+ "numericEnum": {
+ "type": "integer",
+ "enum": [100, 200, 300, 404, 500]
+ },
+ "statusCode": {
+ "type": "string",
+ "enum": ["200", "404", "500"]
+ }
+ }
+ },
+ "SimpleObject": {
+ "type": "object",
+ "required": ["id", "name"],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ },
+ "Animal": {
+ "type": "object",
+ "required": ["name", "type"],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ },
+ "Dog": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Animal"
+ },
+ {
+ "type": "object",
+ "required": ["breed"],
+ "properties": {
+ "breed": {
+ "type": "string"
+ },
+ "barkVolume": {
+ "type": "integer"
+ }
+ }
+ }
+ ]
+ },
+ "StringOrNumber": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "number"
+ }
+ ]
+ },
+ "CreditCard": {
+ "type": "object",
+ "required": ["paymentType", "cardNumber", "cardType"],
+ "properties": {
+ "paymentType": {
+ "type": "string"
+ },
+ "cardNumber": {
+ "type": "string"
+ },
+ "cardType": {
+ "type": "string",
+ "enum": ["visa", "mastercard", "amex"]
+ }
+ }
+ },
+ "BankAccount": {
+ "type": "object",
+ "required": ["paymentType", "accountNumber", "bankName"],
+ "properties": {
+ "paymentType": {
+ "type": "string"
+ },
+ "accountNumber": {
+ "type": "string"
+ },
+ "bankName": {
+ "type": "string"
+ }
+ }
+ },
+ "PaymentMethod": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/CreditCard"
+ },
+ {
+ "$ref": "#/components/schemas/BankAccount"
+ }
+ ],
+ "discriminator": {
+ "propertyName": "paymentType",
+ "mapping": {
+ "credit_card": "#/components/schemas/CreditCard",
+ "bank_account": "#/components/schemas/BankAccount"
+ }
+ }
+ },
+ "NullableOptionalTypes": {
+ "type": "object",
+ "required": ["requiredField"],
+ "properties": {
+ "requiredField": {
+ "type": "string"
+ },
+ "optionalField": {
+ "type": "string"
+ },
+ "nullableField": {
+ "type": "string",
+ "nullable": true
+ },
+ "optionalNullableField": {
+ "type": "string",
+ "nullable": true
+ },
+ "fieldWithDefault": {
+ "type": "integer",
+ "default": 42
+ }
+ }
+ },
+ "Address": {
+ "type": "object",
+ "required": ["street", "city"],
+ "properties": {
+ "street": {
+ "type": "string"
+ },
+ "city": {
+ "type": "string"
+ },
+ "zipCode": {
+ "type": "string"
+ },
+ "country": {
+ "type": "string",
+ "default": "USA"
+ }
+ }
+ },
+ "Employee": {
+ "type": "object",
+ "required": ["id", "name", "email"],
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "address": {
+ "$ref": "#/components/schemas/Address"
+ },
+ "skills": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "Department": {
+ "type": "object",
+ "required": ["name", "manager"],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "manager": {
+ "$ref": "#/components/schemas/Employee"
+ },
+ "employees": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Employee"
+ }
+ }
+ }
+ },
+ "Company": {
+ "type": "object",
+ "required": ["name"],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "headquarters": {
+ "$ref": "#/components/schemas/Address"
+ },
+ "departments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Department"
+ }
+ },
+ "metadata": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ },
+ "SuccessResponse": {
+ "type": "object",
+ "required": ["status", "data"],
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "data": {
+ "type": "object"
+ }
+ }
+ },
+ "CreatedResponse": {
+ "type": "object",
+ "required": ["status", "id"],
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "location": {
+ "type": "string"
+ }
+ }
+ },
+ "NotFoundResponse": {
+ "type": "object",
+ "required": ["error"],
+ "properties": {
+ "error": {
+ "type": "string"
+ },
+ "resource": {
+ "type": "string"
+ }
+ }
+ },
+ "ErrorResponse": {
+ "type": "object",
+ "required": ["message"],
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "code": {
+ "type": "integer"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json
new file mode 100644
index 000000000000..cd38233806cc
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/cpp-httplib-server/petstore.json
@@ -0,0 +1,316 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Petstore API",
+ "version": "1.0.0"
+ },
+ "servers": [
+ { "url": "http://petstore.swagger.io/v2" }
+ ],
+ "paths": {
+ "/pet/{petId}": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Get pet by ID",
+ "operationId": "getPetById",
+ "parameters": [
+ { "name": "petId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" } },
+ { "name": "customHeader", "in": "header", "required": false, "schema": { "type": "string" } },
+ { "name": "cookieParam", "in": "cookie", "required": false, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } },
+ "400": { "description": "Invalid ID", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } },
+ "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }
+ }
+ },
+ "delete": {
+ "tags": ["pet"],
+ "summary": "Delete pet by ID",
+ "operationId": "deletePetById",
+ "parameters": [
+ { "name": "petId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" } },
+ { "name": "api_key", "in": "query", "required": false, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": { "description": "Pet deleted" },
+ "400": { "description": "Invalid ID" }
+ }
+ }
+ },
+ "/pet": {
+ "post": {
+ "tags": ["pet"],
+ "summary": "Add a new pet",
+ "operationId": "addPet",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
+ }
+ },
+ "responses": {
+ "201": { "description": "Pet created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } },
+ "400": { "description": "Invalid input", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }
+ }
+ },
+ "put": {
+ "tags": ["pet"],
+ "summary": "Update an existing pet",
+ "operationId": "updatePet",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
+ }
+ },
+ "responses": {
+ "200": { "description": "Pet updated" },
+ "400": { "description": "Invalid input" }
+ }
+ }
+ },
+ "/pet/findByStatus": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Find pets by status (enum, array)",
+ "operationId": "findPetsByStatus",
+ "parameters": [
+ { "name": "status", "in": "query", "required": true, "schema": { "type": "string", "enum": ["available", "pending", "sold"] } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } },
+ "400": { "description": "Invalid status" }
+ }
+ }
+ },
+ "/pet/findByTags": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Find pets by tags",
+ "operationId": "findPetsByTags",
+ "parameters": [
+ { "name": "tags", "in": "query", "required": true, "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string" } } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } }
+ }
+ }
+ },
+ "/pet/complex": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Test nested object, enum, array, header, cookie",
+ "operationId": "complexParamTest",
+ "parameters": [
+ { "name": "deepObj", "in": "query", "required": false, "style": "deepObject", "explode": true, "schema": { "$ref": "#/components/schemas/DeepObj" } },
+ { "name": "enumParam", "in": "query", "required": false, "schema": { "type": "string", "enum": ["A", "B", "C"] } },
+ { "name": "pipeArr", "in": "query", "required": false, "style": "pipeDelimited", "explode": false, "schema": { "type": "array", "items": { "type": "string" } } },
+ { "name": "spaceArr", "in": "query", "required": false, "style": "spaceDelimited", "explode": false, "schema": { "type": "array", "items": { "type": "integer" } } },
+ { "name": "x-enum-header", "in": "header", "required": false, "schema": { "type": "string", "enum": ["X", "Y", "Z"] } },
+ { "name": "cookieEnum", "in": "cookie", "required": false, "schema": { "type": "string", "enum": ["cookieA", "cookieB"] } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComplexParamsResponse" } } } }
+ }
+ }
+ },
+ "/store/order": {
+ "post": {
+ "tags": ["store"],
+ "summary": "Place an order",
+ "operationId": "placeOrder",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/Order" } }
+ }
+ },
+ "responses": {
+ "200": { "description": "Order placed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } }
+ }
+ }
+ },
+ "/store/order/{orderId}": {
+ "get": {
+ "tags": ["store"],
+ "summary": "Get order by ID",
+ "operationId": "getOrderById",
+ "parameters": [
+ { "name": "orderId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } }
+ }
+ },
+ "delete": {
+ "tags": ["store"],
+ "summary": "Delete order",
+ "operationId": "deleteOrder",
+ "parameters": [
+ { "name": "orderId", "in": "path", "required": true, "schema": { "type": "integer", "format": "int64" } }
+ ],
+ "responses": {
+ "200": { "description": "Order deleted" }
+ }
+ }
+ },
+ "/user": {
+ "post": {
+ "tags": ["user"],
+ "summary": "Create user",
+ "operationId": "createUser",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/User" } }
+ }
+ },
+ "responses": {
+ "200": { "description": "User created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }
+ }
+ }
+ },
+ "/user/{username}": {
+ "get": {
+ "tags": ["user"],
+ "summary": "Get user by name",
+ "operationId": "getUserByName",
+ "parameters": [
+ { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }
+ }
+ },
+ "put": {
+ "tags": ["user"],
+ "summary": "Update user",
+ "operationId": "updateUser",
+ "parameters": [
+ { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/User" } }
+ }
+ },
+ "responses": {
+ "200": { "description": "User updated" }
+ }
+ },
+ "delete": {
+ "tags": ["user"],
+ "summary": "Delete user",
+ "operationId": "deleteUser",
+ "parameters": [
+ { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } }
+ ],
+ "responses": {
+ "200": { "description": "User deleted" }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Pet": {
+ "type": "object",
+ "required": ["name", "photoUrls"],
+ "properties": {
+ "id": { "type": "integer", "format": "int64" },
+ "name": { "type": "string" },
+ "photoUrls": { "type": "array", "items": { "type": "string" } },
+ "status": { "type": "string", "enum": ["available", "pending", "sold"], "description": "Pet status in store" },
+ "category": { "$ref": "#/components/schemas/Category" },
+ "tags": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } }
+ }
+ },
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer", "format": "int64" },
+ "name": { "type": "string" }
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer", "format": "int64" },
+ "name": { "type": "string" }
+ }
+ },
+ "Order": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer", "format": "int64" },
+ "petId": { "type": "integer", "format": "int64" },
+ "quantity": { "type": "integer", "format": "int32" },
+ "shipDate": { "type": "string", "format": "date-time" },
+ "status": { "type": "string", "enum": ["placed", "approved", "delivered"], "description": "Order status" },
+ "complete": { "type": "boolean" }
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer", "format": "int64" },
+ "username": { "type": "string" },
+ "firstName": { "type": "string" },
+ "lastName": { "type": "string" },
+ "email": { "type": "string", "format": "email" },
+ "password": { "type": "string" },
+ "phone": { "type": "string" },
+ "userStatus": { "type": "integer", "format": "int32", "enum": [0, 1, 2], "description": "User status (0=inactive, 1=active, 2=admin)" }
+ }
+ },
+ "ApiResponse": {
+ "type": "object",
+ "properties": {
+ "code": { "type": "integer", "format": "int32" },
+ "type": { "type": "string" },
+ "message": { "type": "string" }
+ }
+ },
+ "DeepObj": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" },
+ "bar": { "type": "integer" },
+ "baz": {
+ "type": "object",
+ "properties": {
+ "x": { "type": "string" },
+ "y": { "type": "integer" }
+ }
+ }
+ }
+ },
+ "ComplexParamsResponse": {
+ "type": "object",
+ "properties": {
+ "deepObj": { "$ref": "#/components/schemas/DeepObj" },
+ "enumParam": { "type": "string", "enum": ["A", "B", "C"] },
+ "pipeArr": { "type": "array", "items": { "type": "string" } },
+ "spaceArr": { "type": "array", "items": { "type": "integer" } },
+ "x-enum-header": { "type": "string", "enum": ["X", "Y", "Z"] },
+ "cookieEnum": { "type": "string", "enum": ["cookieA", "cookieB"] }
+ }
+ },
+ "PetOrCategory": {
+ "oneOf": [
+ { "$ref": "#/components/schemas/Pet" },
+ { "$ref": "#/components/schemas/Category" }
+ ],
+ "description": "Polymorphic type: Pet or Category"
+ },
+ "NullableExample": {
+ "type": "object",
+ "properties": {
+ "maybeString": { "type": "string", "nullable": true, "description": "A nullable string property" }
+ }
+ }
+ }
+ }
+}
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator-ignore b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator-ignore
new file mode 100644
index 000000000000..7484ee590a38
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator-ignore
@@ -0,0 +1,23 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/FILES b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/FILES
new file mode 100644
index 000000000000..05151c84f82a
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/FILES
@@ -0,0 +1,70 @@
+CMakeLists.txt
+LICENSE
+README.md
+api/AuthenticationManager.h
+api/CompositionApi.cpp
+api/CompositionApi.h
+api/DatatypesApi.cpp
+api/DatatypesApi.h
+api/ParametersApi.cpp
+api/ParametersApi.h
+api/SecurityApi.cpp
+api/SecurityApi.h
+models/Address.cpp
+models/Address.h
+models/Animal.cpp
+models/Animal.h
+models/ArrayTypes.cpp
+models/ArrayTypes.h
+models/BankAccount.cpp
+models/BankAccount.h
+models/Company.cpp
+models/Company.h
+models/CreatedResponse.cpp
+models/CreatedResponse.h
+models/CreditCard.cpp
+models/CreditCard.h
+models/Department.cpp
+models/Department.h
+models/Dog.cpp
+models/Dog.h
+models/Employee.cpp
+models/Employee.h
+models/EnumTypes.cpp
+models/EnumTypes.h
+models/ErrorResponse.cpp
+models/ErrorResponse.h
+models/NotFoundResponse.cpp
+models/NotFoundResponse.h
+models/NullableOptionalTypes.cpp
+models/NullableOptionalTypes.h
+models/PaymentMethod.cpp
+models/PaymentMethod.h
+models/PrimitiveTypes.cpp
+models/PrimitiveTypes.h
+models/SimpleObject.cpp
+models/SimpleObject.h
+models/StringOrNumber.cpp
+models/StringOrNumber.h
+models/SuccessResponse.cpp
+models/SuccessResponse.h
+models/TestAllParameterTypes200Response.cpp
+models/TestAllParameterTypes200Response.h
+models/TestApiKeySecurity200Response.cpp
+models/TestApiKeySecurity200Response.h
+models/TestBasicSecurity200Response.cpp
+models/TestBasicSecurity200Response.h
+models/TestBearerSecurity200Response.cpp
+models/TestBearerSecurity200Response.h
+models/TestCookieParameters200Response.cpp
+models/TestCookieParameters200Response.h
+models/TestHeaderParameters200Response.cpp
+models/TestHeaderParameters200Response.h
+models/TestHeaderParameters401Response.cpp
+models/TestHeaderParameters401Response.h
+models/TestOAuth2Security200Response.cpp
+models/TestOAuth2Security200Response.h
+models/TestQueryParameters200Response.cpp
+models/TestQueryParameters200Response.h
+models/TestQueryParametersDeepObjectParameter.cpp
+models/TestQueryParametersDeepObjectParameter.h
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/VERSION b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/VERSION
new file mode 100644
index 000000000000..5e5282953086
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/.openapi-generator/VERSION
@@ -0,0 +1 @@
+7.16.0-SNAPSHOT
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/CMakeLists.txt b/samples/server/petstore/cpp-httplib-server/feature-test/CMakeLists.txt
new file mode 100644
index 000000000000..ef3f87c8c1d0
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/CMakeLists.txt
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 3.14)
+project(cpp_httplib_server_feature_test LANGUAGES CXX)
+
+include(FetchContent)
+
+# Fetch nlohmann_json
+FetchContent_Declare(
+ nlohmann_json
+ GIT_REPOSITORY https://github.com/nlohmann/json.git
+ GIT_TAG v3.11.3
+)
+FetchContent_MakeAvailable(nlohmann_json)
+
+# Fetch cpp-httplib
+FetchContent_Declare(
+ httplib
+ GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
+ GIT_TAG v0.15.3
+)
+FetchContent_MakeAvailable(httplib)
+
+# System libraries - install with: sudo apt-get install libssl-dev zlib1g-dev
+find_package(OpenSSL REQUIRED)
+find_package(ZLIB REQUIRED)
+
+set(TARGET_NAME cpp_httplib_server_feature_test_openapi_lib)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+file(GLOB API_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/api/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/api/*.cpp
+)
+file(GLOB MODEL_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/models/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp
+)
+add_library(${TARGET_NAME} ${API_SRCS} ${MODEL_SRCS})
+target_include_directories (${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+# Make sure these libraries/headers are available in the build environment before linking
+# Required libraries/headers are httplib,ssl,nlohmann::json,zlib
+target_link_libraries(${TARGET_NAME}
+ PRIVATE
+ httplib::httplib
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ nlohmann_json::nlohmann_json
+ ZLIB::ZLIB)
+
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/LICENSE b/samples/server/petstore/cpp-httplib-server/feature-test/LICENSE
new file mode 100644
index 000000000000..286685933869
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/LICENSE
@@ -0,0 +1,5 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
\ No newline at end of file
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/README.md b/samples/server/petstore/cpp-httplib-server/feature-test/README.md
new file mode 100644
index 000000000000..8889a4d4cb6f
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/README.md
@@ -0,0 +1,1080 @@
+# cpp-httplib-server-feature-test - C++ Server
+
+## Overview
+
+This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project.
+It uses the [cpp-httplib](https://github.com/yhirose/cpp-httplib) library to implement a lightweight HTTP server
+with JSON request/response handling via [nlohmann/json](https://github.com/nlohmann/json).
+
+## Requirements
+
+- C++17 compatible compiler
+- CMake (3.14 or higher)
+- OpenSSL (for HTTPS support)
+- ZLIB (for compression support)
+
+**Note:** The following libraries are automatically downloaded via CMake FetchContent:
+- [cpp-httplib](https://github.com/yhirose/cpp-httplib) v0.15.3
+- [nlohmann/json](https://github.com/nlohmann/json) v3.11.3
+
+### Platform-Specific Installation
+
+**Linux (Ubuntu/Debian):**
+```bash
+sudo apt-get update
+sudo apt-get install -y libssl-dev zlib1g-dev cmake build-essential
+```
+
+**macOS:**
+```bash
+brew install openssl zlib cmake
+```
+
+**Windows:**
+```powershell
+# Using vcpkg
+vcpkg install openssl:x64-windows zlib:x64-windows
+
+# Then configure CMake with vcpkg toolchain:
+cmake -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg_root]/scripts/buildsystems/vcpkg.cmake
+```
+
+## Project Structure
+
+```
+├── CMakeLists.txt # Project build configuration
+├── README.md # This file
+├── models/ # Generated model classes
+└── api/ # Generated API handler classes
+```
+
+## Building the Project
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+
+## Working with Models
+
+### Model Classes
+
+#### models::Address
+
+```cpp
+// Create a model
+auto model = models::Address();
+model.setStreet(/* value */); // Set street
+model.setCity(/* value */); // Set city
+model.setZipCode(/* value */); // Set zipCode
+model.setCountry(/* value */); // Set country
+
+// Serialize to JSON
+nlohmann::json json = models::Address::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Address::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::Animal
+
+```cpp
+// Create a model
+auto model = models::Animal();
+model.setName(/* value */); // Set name
+model.setType(/* value */); // Set type
+
+// Serialize to JSON
+nlohmann::json json = models::Animal::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Animal::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::ArrayTypes
+
+```cpp
+// Create a model
+auto model = models::ArrayTypes();
+model.setStringArray(/* value */); // Set stringArray
+model.setIntArray(/* value */); // Set intArray
+model.setObjectArray(/* value */); // Set objectArray
+model.setEnumArray(/* value */); // Set enumArray
+model.setNestedArray(/* value */); // Set nestedArray
+
+// Serialize to JSON
+nlohmann::json json = models::ArrayTypes::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::ArrayTypes::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::BankAccount
+
+```cpp
+// Create a model
+auto model = models::BankAccount();
+model.setPaymentType(/* value */); // Set paymentType
+model.setAccountNumber(/* value */); // Set accountNumber
+model.setBankName(/* value */); // Set bankName
+
+// Serialize to JSON
+nlohmann::json json = models::BankAccount::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::BankAccount::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::Company
+
+```cpp
+// Create a model
+auto model = models::Company();
+model.setName(/* value */); // Set name
+model.setHeadquarters(/* value */); // Set headquarters
+model.setDepartments(/* value */); // Set departments
+model.setMetadata(/* value */); // Set metadata
+
+// Serialize to JSON
+nlohmann::json json = models::Company::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Company::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::CreatedResponse
+
+```cpp
+// Create a model
+auto model = models::CreatedResponse();
+model.setStatus(/* value */); // Set status
+model.setId(/* value */); // Set id
+model.setLocation(/* value */); // Set location
+
+// Serialize to JSON
+nlohmann::json json = models::CreatedResponse::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::CreatedResponse::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::CreditCard
+
+```cpp
+// Create a model
+auto model = models::CreditCard();
+model.setPaymentType(/* value */); // Set paymentType
+model.setCardNumber(/* value */); // Set cardNumber
+model.setCardType(/* value */); // Set cardType
+
+// Serialize to JSON
+nlohmann::json json = models::CreditCard::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::CreditCard::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::Department
+
+```cpp
+// Create a model
+auto model = models::Department();
+model.setName(/* value */); // Set name
+model.setManager(/* value */); // Set manager
+model.setEmployees(/* value */); // Set employees
+
+// Serialize to JSON
+nlohmann::json json = models::Department::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Department::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::Dog
+
+```cpp
+// Create a model
+auto model = models::Dog();
+model.setBreed(/* value */); // Set breed
+model.setBarkVolume(/* value */); // Set barkVolume
+
+// Serialize to JSON
+nlohmann::json json = models::Dog::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Dog::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::Employee
+
+```cpp
+// Create a model
+auto model = models::Employee();
+model.setId(/* value */); // Set id
+model.setName(/* value */); // Set name
+model.setEmail(/* value */); // Set email
+model.setAddress(/* value */); // Set address
+model.setSkills(/* value */); // Set skills
+
+// Serialize to JSON
+nlohmann::json json = models::Employee::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::Employee::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::EnumTypes
+
+```cpp
+// Create a model
+auto model = models::EnumTypes();
+model.setStringEnum(/* value */); // Set stringEnum
+model.setNumericEnum(/* value */); // Set numericEnum
+model.setStatusCode(/* value */); // Set statusCode
+
+// Serialize to JSON
+nlohmann::json json = models::EnumTypes::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::EnumTypes::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::ErrorResponse
+
+```cpp
+// Create a model
+auto model = models::ErrorResponse();
+model.setMessage(/* value */); // Set message
+model.setCode(/* value */); // Set code
+model.setDetails(/* value */); // Set details
+
+// Serialize to JSON
+nlohmann::json json = models::ErrorResponse::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::ErrorResponse::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::NotFoundResponse
+
+```cpp
+// Create a model
+auto model = models::NotFoundResponse();
+model.setError(/* value */); // Set error
+model.setResource(/* value */); // Set resource
+
+// Serialize to JSON
+nlohmann::json json = models::NotFoundResponse::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::NotFoundResponse::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::NullableOptionalTypes
+
+```cpp
+// Create a model
+auto model = models::NullableOptionalTypes();
+model.setRequiredField(/* value */); // Set requiredField
+model.setOptionalField(/* value */); // Set optionalField
+model.setNullableField(/* value */); // Set nullableField
+model.setOptionalNullableField(/* value */); // Set optionalNullableField
+model.setFieldWithDefault(/* value */); // Set fieldWithDefault
+
+// Serialize to JSON
+nlohmann::json json = models::NullableOptionalTypes::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::NullableOptionalTypes::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::PaymentMethod
+
+```cpp
+// Create a model
+auto model = models::PaymentMethod();
+model.setPaymentType(/* value */); // Set paymentType
+model.setCardNumber(/* value */); // Set cardNumber
+model.setCardType(/* value */); // Set cardType
+model.setAccountNumber(/* value */); // Set accountNumber
+model.setBankName(/* value */); // Set bankName
+
+// Serialize to JSON
+nlohmann::json json = models::PaymentMethod::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::PaymentMethod::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::PrimitiveTypes
+
+```cpp
+// Create a model
+auto model = models::PrimitiveTypes();
+model.setStringField(/* value */); // Set stringField
+model.setIntField(/* value */); // Set intField
+model.setLongField(/* value */); // Set longField
+model.setFloatField(/* value */); // Set floatField
+model.setDoubleField(/* value */); // Set doubleField
+model.setBoolField(/* value */); // Set boolField
+model.setByteField(/* value */); // Set byteField
+model.setBinaryField(/* value */); // Set binaryField
+model.setDateField(/* value */); // Set dateField
+model.setDateTimeField(/* value */); // Set dateTimeField
+model.setPasswordField(/* value */); // Set passwordField
+
+// Serialize to JSON
+nlohmann::json json = models::PrimitiveTypes::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::PrimitiveTypes::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::SimpleObject
+
+```cpp
+// Create a model
+auto model = models::SimpleObject();
+model.setId(/* value */); // Set id
+model.setName(/* value */); // Set name
+model.setDescription(/* value */); // Set description
+
+// Serialize to JSON
+nlohmann::json json = models::SimpleObject::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::SimpleObject::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::StringOrNumber
+
+```cpp
+// Create a model
+auto model = models::StringOrNumber();
+
+// Serialize to JSON
+nlohmann::json json = models::StringOrNumber::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::StringOrNumber::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::SuccessResponse
+
+```cpp
+// Create a model
+auto model = models::SuccessResponse();
+model.setStatus(/* value */); // Set status
+model.setData(/* value */); // Set data
+
+// Serialize to JSON
+nlohmann::json json = models::SuccessResponse::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::SuccessResponse::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestAllParameterTypes200Response
+
+```cpp
+// Create a model
+auto model = models::TestAllParameterTypes200Response();
+model.setMessage(/* value */); // Set message
+model.setResourceId(/* value */); // Set resourceId
+model.setFilter(/* value */); // Set filter
+model.setCorrelationId(/* value */); // Set correlationId
+
+// Serialize to JSON
+nlohmann::json json = models::TestAllParameterTypes200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestAllParameterTypes200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestApiKeySecurity200Response
+
+```cpp
+// Create a model
+auto model = models::TestApiKeySecurity200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestApiKeySecurity200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestApiKeySecurity200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestBasicSecurity200Response
+
+```cpp
+// Create a model
+auto model = models::TestBasicSecurity200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestBasicSecurity200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestBasicSecurity200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestBearerSecurity200Response
+
+```cpp
+// Create a model
+auto model = models::TestBearerSecurity200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestBearerSecurity200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestBearerSecurity200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestCookieParameters200Response
+
+```cpp
+// Create a model
+auto model = models::TestCookieParameters200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestCookieParameters200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestCookieParameters200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestHeaderParameters200Response
+
+```cpp
+// Create a model
+auto model = models::TestHeaderParameters200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestHeaderParameters200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestHeaderParameters200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestHeaderParameters401Response
+
+```cpp
+// Create a model
+auto model = models::TestHeaderParameters401Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestHeaderParameters401Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestHeaderParameters401Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestOAuth2Security200Response
+
+```cpp
+// Create a model
+auto model = models::TestOAuth2Security200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestOAuth2Security200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestOAuth2Security200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestQueryParameters200Response
+
+```cpp
+// Create a model
+auto model = models::TestQueryParameters200Response();
+model.setMessage(/* value */); // Set message
+
+// Serialize to JSON
+nlohmann::json json = models::TestQueryParameters200Response::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestQueryParameters200Response::fromJson(nlohmann::json::parse(jsonString));
+```
+#### models::TestQueryParametersDeepObjectParameter
+
+```cpp
+// Create a model
+auto model = models::TestQueryParametersDeepObjectParameter();
+model.setName(/* value */); // Set name
+model.setAge(/* value */); // Set age
+
+// Serialize to JSON
+nlohmann::json json = models::TestQueryParametersDeepObjectParameter::toJson(model);
+std::string jsonString = json.dump();
+
+// Deserialize from JSON
+auto parsedModel = models::TestQueryParametersDeepObjectParameter::fromJson(nlohmann::json::parse(jsonString));
+```
+
+## Implementing API Handlers
+
+### API Classes
+
+Each API is generated as an abstract base class with pure virtual methods that you must implement.
+
+#### Composition
+
+Create a class that inherits from the generated base class:
+
+```cpp
+#include "api/CompositionApi.h"
+
+class CompositionImpl : public api::Composition {
+public:
+ CompositionallofPostResponse handlePostForCompositionallof(const CompositionallofPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_DOG):
+ models::Dog successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ CompositionanyofPostResponse handlePostForCompositionanyof(const CompositionanyofPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_STRING_OR_NUMBER):
+ models::StringOrNumber successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ CompositiononeofPostResponse handlePostForCompositiononeof(const CompositiononeofPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_PAYMENT_METHOD):
+ models::PaymentMethod successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+};
+```
+#### Datatypes
+
+Create a class that inherits from the generated base class:
+
+```cpp
+#include "api/DatatypesApi.h"
+
+class DatatypesImpl : public api::Datatypes {
+public:
+ DatatypesarraysPostResponse handlePostForDatatypesarrays(const DatatypesarraysPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_ARRAY_TYPES):
+ models::ArrayTypes successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ DatatypesenumsPostResponse handlePostForDatatypesenums(const DatatypesenumsPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_ENUM_TYPES):
+ models::EnumTypes successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ ResponsesmultipleGetResponse handleGetForResponsesmultiple(const ResponsesmultipleGetRequest& params) override {
+ // Access request parameters:
+ // Query: params.m_scenario
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_SUCCESS_RESPONSE):
+ models::SuccessResponse successResponse;
+ // ... populate response ...
+ return successResponse;
+ // Return success response (HTTP HTTP_RESPONSE_CODE_CREATED_RESPONSE):
+ models::CreatedResponse successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_NOT_FOUND_RESPONSE):
+ // models::NotFoundResponse errorResponse;
+ // return errorResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+ NestedobjectsPostResponse handlePostForNestedobjects(const NestedobjectsPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_COMPANY):
+ models::Company successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ void handleDeleteForResponsesnocontent(const ResponsesnocontentDeleteRequest& params) override {
+ // Access request parameters from params struct
+ // Implement your logic here
+ }
+
+ NullableoptionalPostResponse handlePostForNullableoptional(const NullableoptionalPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_NULLABLE_OPTIONAL_TYPES):
+ models::NullableOptionalTypes successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ DatatypesprimitivesPostResponse handlePostForDatatypesprimitives(const DatatypesprimitivesPostRequest& params) override {
+ // Access request parameters:
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_PRIMITIVE_TYPES):
+ models::PrimitiveTypes successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+};
+```
+#### Parameters
+
+Create a class that inherits from the generated base class:
+
+```cpp
+#include "api/ParametersApi.h"
+
+class ParametersImpl : public api::Parameters {
+public:
+ ParameterscombinedresourceIdPostResponse handlePostForParameterscombinedresourceId(const ParameterscombinedresourceIdPostRequest& params) override {
+ // Access request parameters:
+ // Path: params.m_resourceId
+ // Query: params.m_filter
+ // Query: params.m_limit (optional)
+ // Header: params.m_xCorrelationId
+ // Header: params.m_xClientVersion (optional)
+ // Body: params.m_request (std::optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_ALL_PARAMETER_TYPES200_RESPONSE):
+ models::TestAllParameterTypes200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+ ParameterscookiesGetResponse handleGetForParameterscookies(const ParameterscookiesGetRequest& params) override {
+ // Access request parameters:
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_COOKIE_PARAMETERS200_RESPONSE):
+ models::TestCookieParameters200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+ ParametersheadersGetResponse handleGetForParametersheaders(const ParametersheadersGetRequest& params) override {
+ // Access request parameters:
+ // Header: params.m_xApiVersion
+ // Header: params.m_xRequestId (optional)
+ // Header: params.m_xRateLimit (optional)
+ // Header: params.m_xTags (optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_HEADER_PARAMETERS200_RESPONSE):
+ models::TestHeaderParameters200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_TEST_HEADER_PARAMETERS401_RESPONSE):
+ // models::TestHeaderParameters401Response errorResponse;
+ // return errorResponse;
+ }
+
+ ParametersquerypathIdGetResponse handleGetForParametersquerypathId(const ParametersquerypathIdGetRequest& params) override {
+ // Access request parameters:
+ // Path: params.m_pathId
+ // Query: params.m_stringParam
+ // Query: params.m_intParam (optional)
+ // Query: params.m_boolParam (optional)
+ // Query: params.m_arrayParam (optional)
+ // Query: params.m_spaceDelimited (optional)
+ // Query: params.m_pipeDelimited (optional)
+ // Query: params.m_deepObject (optional)
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_QUERY_PARAMETERS200_RESPONSE):
+ models::TestQueryParameters200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+ }
+
+};
+```
+#### Security
+
+Create a class that inherits from the generated base class:
+
+```cpp
+#include "api/SecurityApi.h"
+
+class SecurityImpl : public api::Security {
+public:
+ SecurityapikeyGetResponse handleGetForSecurityapikey() override {
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_API_KEY_SECURITY200_RESPONSE):
+ models::TestApiKeySecurity200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+ SecuritybasicGetResponse handleGetForSecuritybasic() override {
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_BASIC_SECURITY200_RESPONSE):
+ models::TestBasicSecurity200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+ SecuritybearerGetResponse handleGetForSecuritybearer() override {
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_BEARER_SECURITY200_RESPONSE):
+ models::TestBearerSecurity200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+ Securityoauth2getResponse handleGetForSecurityoauth2() override {
+
+ // Implement your business logic here
+
+ // Return success response (HTTP HTTP_RESPONSE_CODE_TEST_O_AUTH2_SECURITY200_RESPONSE):
+ models::TestOAuth2Security200Response successResponse;
+ // ... populate response ...
+ return successResponse;
+
+ // Or return error response (HTTP HTTP_RESPONSE_CODE_ERROR_RESPONSE):
+ // models::ErrorResponse errorResponse;
+ // return errorResponse;
+ }
+
+};
+```
+
+## Running the Server
+
+Here's a complete example of setting up and running the server:
+
+```cpp
+#include
+#include
+
+#include "api/CompositionApi.h"
+
+#include "api/DatatypesApi.h"
+
+#include "api/ParametersApi.h"
+
+#include "api/SecurityApi.h"
+
+#include "api/AuthenticationManager.h"
+
+int main() {
+ httplib::Server server;
+
+ // Create authentication manager (required for this API)
+ auto authMgr = std::make_shared();
+
+ // Create API implementations
+
+ CompositionImpl composition;
+
+ DatatypesImpl datatypes;
+
+ ParametersImpl parameters;
+
+ SecurityImpl security;
+
+
+ // Register routes
+
+ composition.registerRoutes(server, authMgr);
+
+ datatypes.registerRoutes(server, authMgr);
+
+ parameters.registerRoutes(server, authMgr);
+
+ security.registerRoutes(server, authMgr);
+
+
+ // Start server
+ std::cout << "Server starting on http://localhost:8080" << std::endl;
+ server.listen("localhost", 8080);
+
+ return 0;
+}
+```
+
+### With Authentication
+
+When authentication is required, you must:
+1. Implement the `AuthenticationManager` interface (see Authentication section below)
+2. Pass the authentication manager to `registerRoutes()`
+
+
+## Authentication
+
+This API requires authentication. Implement the `AuthenticationManager` interface to provide your authentication logic:
+
+```cpp
+#include "api/AuthenticationManager.h"
+
+class MyAuthManager : public api::AuthenticationManager {
+public:
+ bool validateApiKey(const std::string& key) override {
+ // Validate API key from header, query, or cookie
+ // Example: check against database or cache
+ return checkApiKeyInDatabase(key);
+ }
+
+ bool validateBearerToken(const std::string& token) override {
+ // Validate JWT or other bearer tokens
+ // Example: verify signature and expiration
+ return jwt::verify(token, secret_key);
+ }
+
+ bool validateBasicAuth(const std::string& username, const std::string& password) override {
+ // Validate username/password credentials
+ // Example: check against user database with hashed passwords
+ auto user = findUser(username);
+ return user && bcrypt::verify(password, user->passwordHash);
+ }
+
+ bool validateOAuth2(const std::string& token, const std::vector& scopes) override {
+ // Validate OAuth2 token and check required scopes
+ // Example: introspect token and verify scopes
+ auto introspection = oauthProvider.introspect(token);
+ return introspection.active && hasAllScopes(introspection.scopes, scopes);
+ }
+};
+```
+
+### Authentication Flow
+
+1. The server automatically extracts credentials from requests (headers, query params, cookies)
+2. Before calling your handler, it validates credentials using your `AuthenticationManager`
+3. If validation fails, the server returns HTTP 401 Unauthorized automatically
+4. If validation succeeds, your handler is called
+
+### Security Schemes
+
+The generated code supports:
+- **API Key**: Header, query parameter, or cookie-based authentication
+- **Bearer Token**: Authorization header with "Bearer" scheme (e.g., JWT)
+- **Basic Auth**: HTTP Basic authentication (username:password)
+- **OAuth2**: OAuth 2.0 token-based authentication with scope validation
+
+
+## Error Handling
+
+### Response Variants
+
+Each API endpoint that returns data uses `std::variant` to represent multiple possible response types (success and errors):
+
+```cpp
+// Example: endpoint returns success (User) or errors (NotFound, ServerError)
+using GetUserResponse = std::variant;
+
+GetUserResponse handleGetUser(const GetUserRequest& params) override {
+ if (userExists(params.m_userId)) {
+ User user = fetchUser(params.m_userId);
+ return user; // Automatically sets HTTP 200
+ } else {
+ NotFound error;
+ error.setMessage("User not found");
+ return error; // Automatically sets HTTP 404
+ }
+}
+```
+
+The server automatically:
+- Detects which type is returned from the variant
+- Sets the appropriate HTTP status code
+- Serializes the response to JSON
+
+### HTTP Status Codes
+
+Status codes are automatically set based on the response type you return. Each model type is associated with a specific HTTP status code defined in your OpenAPI specification.
+
+**Optimized Status Code Constants:**
+The generator only creates HTTP status code constants (e.g., `HTTP_RESPONSE_CODE_200`, `HTTP_RESPONSE_CODE_404`) for codes actually used by your API operations. This reduces code bloat and compilation time compared to generating all possible HTTP status codes.
+
+### Parameter Validation
+
+The generated code automatically validates:
+- **Required parameters**: Returns HTTP 400 if missing
+- **Type conversion**: Returns HTTP 400 if parameter cannot be converted to expected type
+- **JSON parsing**: Returns HTTP 400 if request body is invalid JSON
+
+Custom validation logic should be implemented in your handler methods.
+
+### Working with Optional Parameters
+
+Optional parameters and model fields use `std::optional`:
+
+```cpp
+void handleRequest(const RequestParams& params) override {
+ // Check if optional query parameter is present
+ if (params.m_optionalParam) {
+ auto value = *params.m_optionalParam; // Dereference to get value
+ // Use value...
+ }
+
+ // Check if optional request body is present
+ if (params.m_request) {
+ auto body = *params.m_request; // Dereference to get body
+ // Use body...
+ }
+}
+```
+
+## Advanced Features
+
+### Parameter Serialization Styles
+
+The generator supports various parameter serialization styles as defined in OpenAPI:
+
+- **simple**: Comma-separated values (default for path/header)
+- **form**: Ampersand-separated values (default for query)
+- **spaceDelimited**: Space-separated values
+- **pipeDelimited**: Pipe-separated values
+- **deepObject**: Nested object notation for query parameters
+
+These are automatically handled during parameter parsing.
+
+### Enum Handling
+
+All generated enums automatically include an `UNSPECIFIED` value as the first enum entry for safe initialization:
+
+```cpp
+enum class Status {
+ UNSPECIFIED = 0, // Added automatically for safety
+ PENDING,
+ APPROVED,
+ REJECTED
+};
+
+// Safe default initialization
+Status status; // Defaults to UNSPECIFIED (0)
+
+// Explicit initialization
+Status activeStatus = Status::APPROVED;
+
+// Enum serialization/deserialization
+// UNSPECIFIED is not a valid API value and indicates uninitialized state
+```
+
+**Why UNSPECIFIED?**
+- Provides a safe default value for uninitialized enums
+- Prevents undefined behavior from using uninitialized enum values
+- Makes it clear when an enum hasn't been set vs. having a valid API value
+- Does not appear in OpenAPI spec - internal C++ implementation detail
+
+### Union Types (anyOf/oneOf)
+
+When your OpenAPI spec uses `anyOf` or `oneOf`, the generated code uses `std::variant`:
+
+```cpp
+// OpenAPI: { "anyOf": [{"type": "string"}, {"type": "number"}] }
+using MyUnionType = std::variant;
+
+// In your model:
+MyUnionType value;
+
+// Use std::visit to handle different types:
+std::visit([](const auto& v) {
+ using T = std::decay_t;
+ if constexpr (std::is_same_v) {
+ std::cout << "String: " << v << std::endl;
+ } else if constexpr (std::is_same_v) {
+ std::cout << "Number: " << v << std::endl;
+ }
+}, value);
+```
+
+## Additional Resources
+
+- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib)
+- [nlohmann/json Documentation](https://github.com/nlohmann/json)
+- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/cpp-httplib-server)
+- [OpenAPI Specification](https://swagger.io/specification/)
+
+- [cpp-httplib Documentation](https://github.com/yhirose/cpp-httplib)
+- [nlohmann/json Documentation](https://github.com/nlohmann/json)
+- [OpenAPI Generator Documentation](https://openapi-generator.tech/docs/generators/)
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/api/AuthenticationManager.h b/samples/server/petstore/cpp-httplib-server/feature-test/api/AuthenticationManager.h
new file mode 100644
index 000000000000..63a50f854e00
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/api/AuthenticationManager.h
@@ -0,0 +1,79 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace api {
+
+/**
+ * @brief Authentication Manager Interface
+ *
+ * This interface defines the contract for authentication validation.
+ * Users must implement this interface to provide their own authentication logic.
+ *
+ * Example implementation:
+ * @code
+ * class MyAuthManager : public AuthenticationManager {
+ * public:
+ * bool validateApiKey(const std::string& key) override {
+ * return database.checkApiKey(key);
+ * }
+ *
+ * bool validateBearerToken(const std::string& token) override {
+ * return jwt::verify(token, secret);
+ * }
+ *
+ * bool validateBasicAuth(const std::string& username, const std::string& password) override {
+ * return bcrypt::verify(password, database.getPasswordHash(username));
+ * }
+ *
+ * bool validateOAuth2(const std::string& token, const std::vector& scopes) override {
+ * auto introspection = oauthProvider.introspect(token);
+ * return introspection.active && hasRequiredScopes(introspection, scopes);
+ * }
+ * };
+ * @endcode
+ */
+class AuthenticationManager {
+public:
+ virtual ~AuthenticationManager() = default;
+
+ /**
+ * @brief Validate an API key
+ * @param key The API key to validate
+ * @return true if the API key is valid, false otherwise
+ */
+ virtual bool validateApiKey(const std::string& key) = 0;
+
+ /**
+ * @brief Validate a Bearer token (e.g., JWT)
+ * @param token The bearer token to validate
+ * @return true if the token is valid, false otherwise
+ */
+ virtual bool validateBearerToken(const std::string& token) = 0;
+
+ /**
+ * @brief Validate Basic authentication credentials
+ * @param username The username
+ * @param password The password
+ * @return true if the credentials are valid, false otherwise
+ */
+ virtual bool validateBasicAuth(const std::string& username, const std::string& password) = 0;
+
+ /**
+ * @brief Validate an OAuth2 token with required scopes
+ * @param token The OAuth2 access token
+ * @param scopes The required scopes for this operation
+ * @return true if the token is valid and has required scopes, false otherwise
+ */
+ virtual bool validateOAuth2(const std::string& token, const std::vector& scopes) = 0;
+};
+
+} // namespace api
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.cpp b/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.cpp
new file mode 100644
index 000000000000..43744d360a5f
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.cpp
@@ -0,0 +1,323 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+
+// System headers
+#include
+#include
+#include
+
+// Project headers
+#include "CompositionApi.h"
+
+constexpr int HTTP_RESPONSE_CODE_DOG = 200;
+constexpr int HTTP_RESPONSE_CODE_STRING_OR_NUMBER = 200;
+constexpr int HTTP_RESPONSE_CODE_PAYMENT_METHOD = 200;
+constexpr int HTTP_RESPONSE_CODE_BAD_REQUEST = 400;
+constexpr int HTTP_RESPONSE_CODE_INTERNAL_SERVER_ERROR = 500;
+
+namespace api {
+
+using namespace models;
+
+bool Composition::parseCompositionallofPostParams(const httplib::Request& req, Composition::CompositionallofPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Composition::handleCompositionallofPostResponse(const CompositionallofPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_DOG;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Composition::parseCompositionanyofPostParams(const httplib::Request& req, Composition::CompositionanyofPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Composition::handleCompositionanyofPostResponse(const CompositionanyofPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_STRING_OR_NUMBER;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Composition::parseCompositiononeofPostParams(const httplib::Request& req, Composition::CompositiononeofPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Composition::handleCompositiononeofPostResponse(const CompositiononeofPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_PAYMENT_METHOD;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+
+
+void Composition::handleCompositionallofPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ CompositionallofPostRequest params;
+ std::vector paramErrors;
+ if (!parseCompositionallofPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForCompositionallof(params);
+ handleCompositionallofPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Composition::handleCompositionanyofPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ CompositionanyofPostRequest params;
+ std::vector paramErrors;
+ if (!parseCompositionanyofPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForCompositionanyof(params);
+ handleCompositionanyofPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Composition::handleCompositiononeofPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ CompositiononeofPostRequest params;
+ std::vector paramErrors;
+ if (!parseCompositiononeofPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForCompositiononeof(params);
+ handleCompositiononeofPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+
+void Composition::registerRoutes(httplib::Server& svr)
+{
+ svr.Post("/composition/allof", [this]([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+ {
+ handleCompositionallofPostRequest(req, res);
+ });
+ svr.Post("/composition/anyof", [this]([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+ {
+ handleCompositionanyofPostRequest(req, res);
+ });
+ svr.Post("/composition/oneof", [this]([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+ {
+ handleCompositiononeofPostRequest(req, res);
+ });
+}
+
+} // namespace api
\ No newline at end of file
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.h b/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.h
new file mode 100644
index 000000000000..9a6a96e71eef
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.h
@@ -0,0 +1,113 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+
+#pragma once
+
+// System headers
+#include
+#include
+#include
+#include
+
+// Project headers
+#include "models/Dog.h"
+#include "models/PaymentMethod.h"
+#include "models/StringOrNumber.h"
+
+namespace api {
+ class AuthenticationManager;
+class Composition {
+public:
+ Composition() = default;
+ virtual ~Composition() = default;
+ /**
+ * @brief Register all routes for this API
+ * @param svr The httplib::Server instance to register routes on
+ */
+ void registerRoutes(httplib::Server& svr);
+ // =========================
+ // ===== Request types =====
+ // =========================
+
+ /**
+ * @brief Request type for handlePostForCompositionallof.
+ */
+ struct CompositionallofPostRequest
+ {
+ models::Dog m_request; //Request Body (required)
+ };
+
+ /**
+ * @brief Request type for handlePostForCompositionanyof.
+ */
+ struct CompositionanyofPostRequest
+ {
+ models::StringOrNumber m_request; //Request Body (required)
+ };
+
+ /**
+ * @brief Request type for handlePostForCompositiononeof.
+ */
+ struct CompositiononeofPostRequest
+ {
+ models::PaymentMethod m_request; //Request Body (required)
+ };
+
+ // ==========================
+ // ===== Response types =====
+ // ==========================
+
+ /**
+ * @brief Response type for handlePostForCompositionallof.
+ */
+ using CompositionallofPostResponse = models::Dog;
+
+ /**
+ * @brief Response type for handlePostForCompositionanyof.
+ */
+ using CompositionanyofPostResponse = models::StringOrNumber;
+
+ /**
+ * @brief Response type for handlePostForCompositiononeof.
+ */
+ using CompositiononeofPostResponse = models::PaymentMethod;
+ // ============================================================
+ // ===== Pure virtual functions to be handled by the user =====
+ // ============================================================
+ /**
+ * CompositionallofPostRequest - struct containing all the query parameters and headers and schemas as available.
+ * @return CompositionallofPostResponse The response type returned by the handler.
+ */
+ virtual CompositionallofPostResponse handlePostForCompositionallof(const CompositionallofPostRequest& params)=0;
+
+ /**
+ * CompositionanyofPostRequest - struct containing all the query parameters and headers and schemas as available.
+ * @return CompositionanyofPostResponse The response type returned by the handler.
+ */
+ virtual CompositionanyofPostResponse handlePostForCompositionanyof(const CompositionanyofPostRequest& params)=0;
+
+ /**
+ * CompositiononeofPostRequest - struct containing all the query parameters and headers and schemas as available.
+ * @return CompositiononeofPostResponse The response type returned by the handler.
+ */
+ virtual CompositiononeofPostResponse handlePostForCompositiononeof(const CompositiononeofPostRequest& params)=0;
+
+private:
+ // ========================================
+ // ===== Helper function declarations =====
+ // ========================================
+ static bool parseCompositionallofPostParams(const httplib::Request& req, CompositionallofPostRequest& params, std::vector& paramErrors);
+ void handleCompositionallofPostRequest(const httplib::Request& req, httplib::Response& res);
+ static void handleCompositionallofPostResponse(const CompositionallofPostResponse& result, httplib::Response& res);
+ static bool parseCompositionanyofPostParams(const httplib::Request& req, CompositionanyofPostRequest& params, std::vector& paramErrors);
+ void handleCompositionanyofPostRequest(const httplib::Request& req, httplib::Response& res);
+ static void handleCompositionanyofPostResponse(const CompositionanyofPostResponse& result, httplib::Response& res);
+ static bool parseCompositiononeofPostParams(const httplib::Request& req, CompositiononeofPostRequest& params, std::vector& paramErrors);
+ void handleCompositiononeofPostRequest(const httplib::Request& req, httplib::Response& res);
+ static void handleCompositiononeofPostResponse(const CompositiononeofPostResponse& result, httplib::Response& res);
+};
+
+} // namespace api
\ No newline at end of file
diff --git a/samples/server/petstore/cpp-httplib-server/feature-test/api/DatatypesApi.cpp b/samples/server/petstore/cpp-httplib-server/feature-test/api/DatatypesApi.cpp
new file mode 100644
index 000000000000..9eb36ae26c40
--- /dev/null
+++ b/samples/server/petstore/cpp-httplib-server/feature-test/api/DatatypesApi.cpp
@@ -0,0 +1,690 @@
+/**
+* This file is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+* https://openapi-generator.tech
+* Do not edit the class manually.
+*/
+
+// System headers
+#include
+#include
+#include
+
+// Project headers
+#include "DatatypesApi.h"
+
+constexpr int HTTP_RESPONSE_CODE_ARRAY_TYPES = 200;
+constexpr int HTTP_RESPONSE_CODE_ENUM_TYPES = 200;
+constexpr int HTTP_RESPONSE_CODE_SUCCESS_RESPONSE = 200;
+constexpr int HTTP_RESPONSE_CODE_CREATED_RESPONSE = 201;
+constexpr int HTTP_RESPONSE_CODE_NOT_FOUND_RESPONSE = 404;
+constexpr int HTTP_RESPONSE_CODE_ERROR_RESPONSE = 500;
+constexpr int HTTP_RESPONSE_CODE_COMPANY = 200;
+constexpr int HTTP_RESPONSE_CODE_NULLABLE_OPTIONAL_TYPES = 200;
+constexpr int HTTP_RESPONSE_CODE_PRIMITIVE_TYPES = 200;
+constexpr int HTTP_RESPONSE_CODE_NO_CONTENT = 204;
+constexpr int HTTP_RESPONSE_CODE_BAD_REQUEST = 400;
+
+namespace api {
+
+using namespace models;
+
+bool Datatypes::parseDatatypesarraysPostParams(const httplib::Request& req, Datatypes::DatatypesarraysPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleDatatypesarraysPostResponse(const DatatypesarraysPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_ARRAY_TYPES;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Datatypes::parseDatatypesenumsPostParams(const httplib::Request& req, Datatypes::DatatypesenumsPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleDatatypesenumsPostResponse(const DatatypesenumsPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_ENUM_TYPES;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Datatypes::parseResponsesmultipleGetParams(const httplib::Request& req, Datatypes::ResponsesmultipleGetRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+
+ // Query Parameters - scenario
+ if (req.has_param("scenario"))
+ {
+ try
+ {
+ params.m_scenario = (req.get_param_value("scenario"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter 'scenario': " + std::string(e.what()));
+ }
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleResponsesmultipleGetResponse(const ResponsesmultipleGetResponse& result, httplib::Response& res)
+{
+ std::visit([&](const auto& value)
+ {
+ using T = std::decay_t;
+
+ // Success types
+ if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_SUCCESS_RESPONSE;
+ nlohmann::json responseJson;
+ to_json(responseJson, value);
+ res.set_content(responseJson.dump(), "application/json");
+ }else if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_CREATED_RESPONSE;
+ nlohmann::json responseJson;
+ to_json(responseJson, value);
+ res.set_content(responseJson.dump(), "application/json");
+ }
+ // Error types
+ else if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_NOT_FOUND_RESPONSE;
+ nlohmann::json errorJson = value;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ else if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_ERROR_RESPONSE;
+ nlohmann::json errorJson = value;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ }, result);
+}
+bool Datatypes::parseNestedobjectsPostParams(const httplib::Request& req, Datatypes::NestedobjectsPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleNestedobjectsPostResponse(const NestedobjectsPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_COMPANY;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Datatypes::parseResponsesnocontentDeleteParams(const httplib::Request& req, Datatypes::ResponsesnocontentDeleteRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+
+ // Query Parameters - id
+ if (req.has_param("id"))
+ {
+ try
+ {
+ params.m_id = std::stoi(req.get_param_value("id"));
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid query parameter 'id': " + std::string(e.what()));
+ }
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+bool Datatypes::parseNullableoptionalPostParams(const httplib::Request& req, Datatypes::NullableoptionalPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleNullableoptionalPostResponse(const NullableoptionalPostResponse& result, httplib::Response& res)
+{
+ // Single response type
+ res.status = HTTP_RESPONSE_CODE_NULLABLE_OPTIONAL_TYPES;
+ nlohmann::json responseJson;
+ to_json(responseJson, result);
+ res.set_content(responseJson.dump(), "application/json");
+}
+bool Datatypes::parseDatatypesprimitivesPostParams(const httplib::Request& req, Datatypes::DatatypesprimitivesPostRequest& params, std::vector& paramErrors)
+{
+ std::vector errors;
+ if (!req.body.empty())
+ {
+ try
+ {
+ nlohmann::json json = nlohmann::json::parse(req.body);
+ from_json(json, params.m_request);
+ }
+ catch (const std::exception& e)
+ {
+ errors.push_back("Invalid request body: " + std::string(e.what()));
+ }
+ }
+ else
+ {
+ errors.push_back("Missing required request body");
+ }
+
+ // Return errors via out-parameter, return false if any errors
+ if (!errors.empty())
+ {
+ paramErrors = std::move(errors);
+ return false;
+ }
+ return true;
+}
+void Datatypes::handleDatatypesprimitivesPostResponse(const DatatypesprimitivesPostResponse& result, httplib::Response& res)
+{
+ std::visit([&](const auto& value)
+ {
+ using T = std::decay_t;
+
+ // Success types
+ if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_PRIMITIVE_TYPES;
+ nlohmann::json responseJson;
+ to_json(responseJson, value);
+ res.set_content(responseJson.dump(), "application/json");
+ }
+ // Error types
+ else if constexpr (std::is_same_v)
+ {
+ res.status = HTTP_RESPONSE_CODE_ERROR_RESPONSE;
+ nlohmann::json errorJson = value;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ }, result);
+}
+
+
+void Datatypes::handleDatatypesarraysPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ DatatypesarraysPostRequest params;
+ std::vector paramErrors;
+ if (!parseDatatypesarraysPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForDatatypesarrays(params);
+ handleDatatypesarraysPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Datatypes::handleDatatypesenumsPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ DatatypesenumsPostRequest params;
+ std::vector paramErrors;
+ if (!parseDatatypesenumsPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForDatatypesenums(params);
+ handleDatatypesenumsPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Datatypes::handleResponsesmultipleGetRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ ResponsesmultipleGetRequest params;
+ std::vector paramErrors;
+ if (!parseResponsesmultipleGetParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handleGetForResponsesmultiple(params);
+ handleResponsesmultipleGetResponse(result, res);
+
+ }
+ catch (const std::exception& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Internal error: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_ERROR_RESPONSE;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Datatypes::handleNestedobjectsPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ NestedobjectsPostRequest params;
+ std::vector paramErrors;
+ if (!parseNestedobjectsPostParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+ auto result = handlePostForNestedobjects(params);
+ handleNestedobjectsPostResponse(result, res);
+
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::invalid_iterator& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::type_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::out_of_range& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+ catch (const nlohmann::json::other_error& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid JSON: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Datatypes::handleResponsesnocontentDeleteRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ ResponsesnocontentDeleteRequest params;
+ std::vector paramErrors;
+ if (!parseResponsesnocontentDeleteParams(req, params, paramErrors))
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Invalid parameters";
+ errorJson["errors"] = paramErrors;
+ res.status = HTTP_RESPONSE_CODE_BAD_REQUEST;
+ res.set_content(errorJson.dump(), "application/json");
+ return;
+ }
+
+ handleDeleteForResponsesnocontent(params);
+ res.status = HTTP_RESPONSE_CODE_NO_CONTENT;
+
+ }
+ catch (const std::exception& e)
+ {
+ nlohmann::json errorJson = nlohmann::json::object();
+ errorJson["message"] = "Internal error: " + std::string(e.what());
+ res.status = HTTP_RESPONSE_CODE_ERROR_RESPONSE;
+ res.set_content(errorJson.dump(), "application/json");
+ }
+}
+
+void Datatypes::handleNullableoptionalPostRequest([[maybe_unused]] const httplib::Request& req, httplib::Response& res)
+{
+ try
+ {
+
+ NullableoptionalPostRequest params;
+ std::vector