ids) {
+ String id = ids.get(schemaName);
+ return id != null ? id : toValidIdentifier(schemaName) + "_component";
+ }
+
+ /**
+ * Thrown during generation when a schema branch has assertion keywords that
+ * can affect composition membership but no generated validator exists.
+ * Carries the schema location, keyword, and remediation guidance.
+ */
+ public static final class UnsupportedSchemaAssertionException
+ extends RuntimeException {
+ private final String schemaLocation;
+ private final String assertionKeyword;
+
+ public UnsupportedSchemaAssertionException(
+ String schemaLocation, String assertionKeyword) {
+ super(buildMessage(schemaLocation, assertionKeyword));
+ this.schemaLocation = schemaLocation;
+ this.assertionKeyword = assertionKeyword;
+ }
+
+ public String getSchemaLocation() { return schemaLocation; }
+ public String getAssertionKeyword() { return assertionKeyword; }
+
+ private static String buildMessage(
+ String schemaLocation, String assertionKeyword) {
+ return "Unsupported schema assertion '" + assertionKeyword
+ + "' at " + schemaLocation
+ + ". This keyword can affect composition membership but "
+ + "no generated validator exists. Add support in a later generator "
+ + "version, or restructure the schema to avoid this keyword.";
+ }
+ }
+
+ /**
+ * Exception thrown when an allOf intersection produces an unsatisfiable
+ * result on a required property, preventing model generation.
+ */
+ public static final class AllOfRequiredUnsatisfiableException
+ extends RuntimeException {
+ private final String schemaName;
+ private final String reason;
+
+ public AllOfRequiredUnsatisfiableException(
+ String schemaName, String reason) {
+ super(buildMessage(schemaName, reason));
+ this.schemaName = schemaName;
+ this.reason = reason;
+ }
+
+ public String getSchemaName() { return schemaName; }
+ public String getReason() { return reason; }
+
+ private static String buildMessage(
+ String schemaName, String reason) {
+ return "Unsatisfiable allOf intersection for schema '"
+ + schemaName + "': " + reason;
+ }
+ }
+
+ /**
+ * Resolves a type name transitively through the resolvedAliasTypes map.
+ * For example, if ModelIdsResponses → std::string and ModelIdsShared → std::string,
+ * then resolveThroughAliases("ModelIdsResponses") returns "std::string".
+ *
+ * Cyclic alias maps fail generation; an unmapped type is returned unchanged.
+ */
+ protected String resolveThroughAliases(String typeName) {
+ if (typeName == null) {
+ return null;
+ }
+ Set visited = new HashSet<>();
+ String current = typeName;
+ while (true) {
+ String resolved = resolvedAliasTypes.get(current);
+ if (resolved == null || resolved.equals(current)) {
+ return current;
+ }
+ if (!visited.add(current)) {
+ throw new IllegalStateException(
+ "Cyclic resolved alias chain starting at '" + typeName + "'");
+ }
+ current = resolved;
+ }
+ }
+
+ /**
+ * Detects whether a schema is a null union (anyOf/oneOf with [T, null] or [null, T])
+ * that should lower to std::optional<T>. Returns the lowered type string,
+ * or null if the schema is not a simple null union.
+ */
+ protected String detectNullUnion(Schema schema, String className) {
+ // Use raw List and cast explicitly because Schema is unparameterized.
+ List anyOfRaw = schema.getAnyOf();
+ List oneOfRaw = schema.getOneOf();
+ List branches = null;
+ if (anyOfRaw != null && !anyOfRaw.isEmpty()) {
+ branches = anyOfRaw;
+ } else if (oneOfRaw != null && !oneOfRaw.isEmpty()) {
+ branches = oneOfRaw;
+ }
+ if (branches == null) {
+ return null;
+ }
+ if (branches.size() != 2) {
+ return null;
+ }
+
+ // Find the non-null branch using ModelUtils for correct null-type detection
+ // (handles both OAS 3.0 nullable and OAS 3.1 type: "null")
+ Schema nonNullBranch = null;
+ for (Object brObj : branches) {
+ Schema branch = (Schema) brObj;
+ if (!ModelUtils.isNullType(branch)) {
+ nonNullBranch = branch;
+ }
+ }
+ if (nonNullBranch == null) {
+ return null; // Both branches are null
+ }
+ // Verify exactly one null branch exists
+ long nullBranchCount = 0;
+ for (Object brObj : branches) {
+ if (ModelUtils.isNullType((Schema) brObj)) nullBranchCount++;
+ }
+ if (nullBranchCount != 1) {
+ return null;
+ }
+
+ // Resolve the non-null branch type. For $ref schemas, resolve to model name.
+ String nonNullType;
+ if (nonNullBranch.get$ref() != null) {
+ nonNullType = ModelUtils.getSimpleRef(nonNullBranch.get$ref());
+ } else {
+ nonNullType = getTypeDeclaration(nonNullBranch);
+ }
+
+ // Avoid self-referencing optional (optional of the model itself)
+ if (nonNullType.equals(className)) {
+ return "boost::json::value";
+ }
+
+ return "std::optional<" + nonNullType + ">";
+ }
+
+ /**
+ * Recursively strips {@code std::shared_ptr} wrappers from a type string.
+ *
+ * - {@code std::shared_ptr} → {@code Foo}
+ * - {@code std::vector>} → {@code std::vector}
+ * - {@code std::map>} → {@code std::map}
+ * - {@code std::string} → {@code std::string} (unchanged)
+ *
+ */
+ protected static String stripSharedPtr(String type) {
+ if (type == null) {
+ return null;
+ }
+ // Direct std::shared_ptr wrapper — extract inner type and recurse.
+ if (type.startsWith("std::shared_ptr<") && type.endsWith(">")) {
+ return stripSharedPtr(type.substring(16, type.length() - 1));
+ }
+ // Check for template arguments (contains '<' and '>').
+ int firstLt = type.indexOf('<');
+ int lastGt = type.lastIndexOf('>');
+ if (firstLt > 0 && lastGt > firstLt) {
+ // Split arguments at commas at depth 0 (not inside nested angle brackets).
+ String prefix = type.substring(0, firstLt);
+ String argsSection = type.substring(firstLt + 1, lastGt);
+ List args = splitTemplateArgs(argsSection);
+ for (int i = 0; i < args.size(); i++) {
+ args.set(i, stripSharedPtr(args.get(i).trim()));
+ }
+ return prefix + "<" + String.join(", ", args) + ">";
+ }
+ return type;
+ }
+
+ /**
+ * Splits a comma-separated template argument list, respecting nested angle brackets.
+ * {@code "std::string, std::shared_ptr"} → {@code ["std::string", "std::shared_ptr"]}
+ */
+ private static List splitTemplateArgs(String args) {
+ List result = new ArrayList<>();
+ int depth = 0;
+ int start = 0;
+ for (int i = 0; i < args.length(); i++) {
+ char c = args.charAt(i);
+ if (c == '<') {
+ depth++;
+ } else if (c == '>') {
+ depth--;
+ } else if (c == ',' && depth == 0) {
+ result.add(args.substring(start, i));
+ start = i + 1;
+ }
+ }
+ result.add(args.substring(start));
+ return result;
+ }
+
+ /**
+ * Extracts the inner type from a std::optional type declaration, correctly
+ * handling nested angle brackets.
+ *
+ * - {@code std::optional} → {@code std::string}
+ * - {@code std::optional>} → {@code std::vector}
+ * - {@code std::optional} → {@code MyModel}
+ * - {@code std::string} → {@code null}
+ *
+ *
+ * @return the inner type, or null if the input does not start with "std::optional<"
+ */
+ private static String extractOptionalInnerType(String type) {
+ if (type == null || !type.startsWith("std::optional<")) {
+ return null;
+ }
+ // Strip prefix "std::optional<" (14 chars) and find matching '>'
+ int depth = 0;
+ int start = 14; // length of "std::optional<"
+ for (int i = start; i < type.length(); i++) {
+ char c = type.charAt(i);
+ if (c == '<') {
+ depth++;
+ } else if (c == '>') {
+ if (depth == 0) {
+ return type.substring(start, i);
+ }
+ depth--;
+ }
+ }
+ return null;
+ }
+}
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppBoostBeastTemplateModelAssembler.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppBoostBeastTemplateModelAssembler.java
new file mode 100644
index 000000000000..160891725c24
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppBoostBeastTemplateModelAssembler.java
@@ -0,0 +1,1206 @@
+package org.openapitools.codegen.languages;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.servers.Server;
+import io.swagger.v3.oas.models.servers.ServerVariable;
+import org.openapitools.codegen.CodegenMediaType;
+import org.openapitools.codegen.CodegenModel;
+import org.openapitools.codegen.CodegenOperation;
+import org.openapitools.codegen.CodegenParameter;
+import org.openapitools.codegen.CodegenProperty;
+import org.openapitools.codegen.CodegenResponse;
+import org.openapitools.codegen.model.ModelMap;
+import org.openapitools.codegen.model.OperationsMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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;
+
+/** Assembles operation and response vendor extensions consumed by API templates. */
+final class CppBoostBeastTemplateModelAssembler {
+ private static final String SSE_SCHEMA_MODE_JSON_EVENT_DATA = "jsonEventData";
+ private static final String X_SSE_EVENT_DATA_SCHEMA = "x-sse-event-data-schema";
+ private static final String X_SSE_REQUEST_PROPERTY = "x-sse-request-property";
+ private static final String X_SSE_EVENT_TYPE = "x-sse-event-type";
+ private static final String X_CODEGEN_CONDITIONAL_SSE = "x-codegen-conditional-sse";
+ private static final String X_CODEGEN_SSE_REQUEST_PARAM = "x-codegen-sse-request-param";
+ private static final String X_CODEGEN_SSE_REQUEST_GETTER = "x-codegen-sse-request-getter";
+ private static final String X_CODEGEN_SSE_REQUEST_SETTER = "x-codegen-sse-request-setter";
+ private static final String X_CODEGEN_SSE_REQUEST_FALSE_VALUE =
+ "x-codegen-sse-request-false-value";
+ private static final String X_CODEGEN_SSE_REQUEST_TRUE_VALUE =
+ "x-codegen-sse-request-true-value";
+ private static final String X_CODEGEN_SSE_REQUEST_TYPE = "x-codegen-sse-request-type";
+ private static final String X_CODEGEN_SSE_REQUEST_SHARED_PTR =
+ "x-codegen-sse-request-shared-ptr";
+ private static final String X_CODEGEN_SSE_REQUEST_LOCAL = "x-codegen-sse-request-local";
+ private static final String X_CODEGEN_SSE_EVENT_TYPE_OVERRIDE =
+ "x-codegen-sse-event-type-override";
+ private static final String X_CODEGEN_DEFAULT_RESPONSE_IS_RETURN_COMPATIBLE =
+ "x-codegen-default-response-is-return-compatible";
+ private static final String X_CODEGEN_EMPTY_BODY_TOLERANT = "x-codegen-empty-body-tolerant";
+ private static final String X_CODEGEN_HAS_DEFAULT_RESPONSE = "x-codegen-has-default-response";
+ private static final String X_CODEGEN_OP_SERVER = "x-codegen-op-server";
+ private static final String X_CODEGEN_OP_SECURITY_GROUPS = "x-codegen-op-security-groups";
+ private static final String X_CODEGEN_OP_HAS_SECURITY = "x-codegen-op-has-security";
+ private static final String X_CODEGEN_OP_CALLBACKS = "x-codegen-op-callbacks";
+ private static final String X_CODEGEN_OP_LINKS = "x-codegen-op-links";
+ private static final String X_CODEGEN_WEBHOOK_METADATA = "x-codegen-webhook-metadata";
+ private static final String X_CODEGEN_RESPONSE_RANGE = "x-codegen-response-range";
+ private static final String X_CODEGEN_RESPONSE_IS_ONE_OF = "x-codegen-response-is-oneof";
+ private static final String X_CODEGEN_STREAM_IS_ONE_OF = "x-codegen-stream-is-oneof";
+ private static final String X_CODEGEN_DUAL_STREAM_IS_ONE_OF = "x-codegen-dual-stream-is-oneof";
+ private static final String X_CODEGEN_RESPONSE_UNION = "x-codegen-response-union";
+ private static final String X_CODEGEN_RESPONSE_UNION_BODY_TYPE =
+ "x-codegen-response-union-body-type";
+ private static final String X_CODEGEN_RESPONSE_UNION_MEMBERS =
+ "x-codegen-response-union-members";
+ private static final String X_CODEGEN_MULTIPART_FILENAME_PARAM =
+ "x-codegen-multipart-filename-param";
+ private static final String X_CODEGEN_MULTIPART_FILENAME_PARAM_NAME =
+ "x-codegen-multipart-filename-param-name";
+ private static final String X_CODEGEN_IS_OPTIONAL_FORM_PARAMETER =
+ "x-codegen-is-optional-form-parameter";
+ private static final String X_CODEGEN_HAS_OPTIONAL_FORM_PARAMETER =
+ "x-codegen-has-optional-form-parameter";
+
+ private final OpenAPI phaseOpenApi;
+ private final List webhookPreservation;
+ private final Map> operationCallbacks;
+ private final Map> operationLinks;
+ private final Map composedKeywordsByModel;
+ private final String sseSchemaMode;
+ private final Set sseOperationIds;
+ private final Map sseRequestPropertyMappings;
+ private final Map sseEventTypeMappings;
+ private final boolean inferConditionalSseOperations;
+ private final boolean hasExplicitRootServers;
+
+
+ CppBoostBeastTemplateModelAssembler(
+ OpenAPI phaseOpenApi,
+ List webhookPreservation,
+ Map> operationCallbacks,
+ Map> operationLinks,
+ Map composedKeywordsByModel,
+ String sseSchemaMode,
+ Set sseOperationIds,
+ Map sseRequestPropertyMappings,
+ Map sseEventTypeMappings,
+ boolean inferConditionalSseOperations,
+ boolean hasExplicitRootServers) {
+ this.phaseOpenApi = phaseOpenApi;
+ this.webhookPreservation = webhookPreservation;
+ this.operationCallbacks = operationCallbacks;
+ this.operationLinks = operationLinks;
+ this.composedKeywordsByModel = composedKeywordsByModel;
+ this.sseSchemaMode = sseSchemaMode;
+ this.sseOperationIds = Collections.unmodifiableSet(new HashSet<>(sseOperationIds));
+ this.sseRequestPropertyMappings = Collections.unmodifiableMap(
+ new LinkedHashMap<>(sseRequestPropertyMappings));
+ this.sseEventTypeMappings = Collections.unmodifiableMap(
+ new LinkedHashMap<>(sseEventTypeMappings));
+ this.inferConditionalSseOperations = inferConditionalSseOperations;
+ this.hasExplicitRootServers = hasExplicitRootServers;
+ }
+
+ private static String stripSharedPtr(String type) {
+ if (type == null) {
+ return null;
+ }
+ if (type.startsWith("std::shared_ptr<") && type.endsWith(">")) {
+ return stripSharedPtr(type.substring(16, type.length() - 1));
+ }
+ int firstLt = type.indexOf('<');
+ int lastGt = type.lastIndexOf('>');
+ if (firstLt > 0 && lastGt > firstLt) {
+ String prefix = type.substring(0, firstLt);
+ List args = splitTemplateArgs(type.substring(firstLt + 1, lastGt));
+ for (int index = 0; index < args.size(); ++index) {
+ args.set(index, stripSharedPtr(args.get(index).trim()));
+ }
+ return prefix + "<" + String.join(", ", args) + ">";
+ }
+ return type;
+ }
+
+ private static List splitTemplateArgs(String args) {
+ List result = new ArrayList<>();
+ int depth = 0;
+ int start = 0;
+ for (int index = 0; index < args.length(); ++index) {
+ char character = args.charAt(index);
+ if (character == '<') {
+ ++depth;
+ } else if (character == '>') {
+ --depth;
+ } else if (character == ',' && depth == 0) {
+ result.add(args.substring(start, index));
+ start = index + 1;
+ }
+ }
+ result.add(args.substring(start));
+ return result;
+ }
+
+ private static String cppString(String value) {
+ return CppBoostBeastClientCodegen.escapeCppStringContent(
+ value == null ? "" : value);
+ }
+
+ private static String commentText(String value) {
+ return value == null ? "" : value
+ .replace("*/", "* /")
+ .replace('\r', ' ')
+ .replace('\n', ' ')
+ .replace('\u2028', ' ')
+ .replace('\u2029', ' ');
+ }
+
+ /** True when the list is exactly swagger-parser's implicit root default
+ * (a single Server with url "/") and the raw source omitted `servers`. */
+ private static boolean isParserDefaultServerList(List servers) {
+ return servers != null && servers.size() == 1
+ && "/".equals(servers.get(0).getUrl());
+ }
+
+ /** The operation's effective security requirements as template-ready
+ * groups. Each group is an OR alternative containing AND-required scheme
+ * maps. An empty group is anonymous access; operation `security: []`
+ * clears inherited requirements. */
+ private List>> effectiveSecurityGroups(CodegenOperation op) {
+ List>> groups = new ArrayList<>();
+ List requirements = null;
+ io.swagger.v3.oas.models.Operation raw = operationFor(op);
+ if (raw != null && raw.getSecurity() != null) {
+ requirements = raw.getSecurity(); // includes `[]` clears
+ } else if (phaseOpenApi != null
+ && phaseOpenApi.getSecurity() != null) {
+ requirements = phaseOpenApi.getSecurity();
+ }
+ if (requirements == null) {
+ return groups; // no security declared
+ }
+ Map schemes = phaseOpenApi != null
+ && phaseOpenApi.getComponents() != null
+ ? phaseOpenApi.getComponents().getSecuritySchemes()
+ : null;
+ for (SecurityRequirement req : requirements) {
+ List