3434
3535import java .io .File ;
3636import java .util .*;
37+ import java .util .regex .Pattern ;
3738
3839import static org .openapitools .codegen .utils .CamelizeOption .LOWERCASE_FIRST_LETTER ;
3940import static org .openapitools .codegen .utils .StringUtils .camelize ;
@@ -167,11 +168,63 @@ public NimClientCodegen() {
167168 typeMapping .put ("DateTime" , "string" );
168169 typeMapping .put ("password" , "string" );
169170 typeMapping .put ("file" , "string" );
171+ typeMapping .put ("object" , "JsonNode" );
172+ typeMapping .put ("AnyType" , "JsonNode" );
170173 }
171174
172175 @ Override
173176 public ModelsMap postProcessModels (ModelsMap objs ) {
174- return postProcessModelsEnum (objs );
177+ objs = postProcessModelsEnum (objs );
178+
179+ // Mark top-level string enums for proper enum generation in template
180+ for (ModelMap mo : objs .getModels ()) {
181+ CodegenModel cm = mo .getModel ();
182+ if (cm .isEnum && cm .allowableValues != null && cm .allowableValues .containsKey ("enumVars" )) {
183+ cm .vendorExtensions .put ("x-is-top-level-enum" , true );
184+ }
185+
186+ // Fix dataType fields that contain underscored type names
187+ // This handles cases like Table[string, Record_string__foo__value]
188+ for (CodegenProperty var : cm .vars ) {
189+ if (var .dataType != null && var .dataType .contains ("Record_" )) {
190+ var .dataType = fixRecordTypeReferences (var .dataType );
191+ }
192+ if (var .datatypeWithEnum != null && var .datatypeWithEnum .contains ("Record_" )) {
193+ var .datatypeWithEnum = fixRecordTypeReferences (var .datatypeWithEnum );
194+ }
195+ }
196+ }
197+
198+ return objs ;
199+ }
200+
201+ /**
202+ * Fix underscored Record type references in dataType strings.
203+ * Converts Record_string__foo___value to RecordStringFooValue.
204+ */
205+ private String fixRecordTypeReferences (String typeString ) {
206+ if (typeString == null || !typeString .contains ("Record_" )) {
207+ return typeString ;
208+ }
209+
210+ // Pattern to match Record_string_... type names with underscores
211+ // These are embedded in strings like: Table[string, Record_string__foo__value]
212+ String result = typeString ;
213+
214+ // Match Record_ followed by any characters until end or comma/bracket
215+ Pattern pattern = Pattern .compile ("Record_[a-z_]+" );
216+ java .util .regex .Matcher matcher = pattern .matcher (result );
217+
218+ StringBuffer sb = new StringBuffer ();
219+ while (matcher .find ()) {
220+ String matched = matcher .group ();
221+ // Camelize the matched Record type name
222+ String camelized = camelize (matched );
223+ matcher .appendReplacement (sb , camelized );
224+ }
225+ matcher .appendTail (sb );
226+
227+ return sb .toString ();
175228 }
176229
177230 @ Override
@@ -192,6 +245,8 @@ public void processOpts() {
192245 apiPackage = File .separator + packageName + File .separator + "apis" ;
193246 modelPackage = File .separator + packageName + File .separator + "models" ;
194247 supportingFiles .add (new SupportingFile ("lib.mustache" , "" , packageName + ".nim" ));
248+ supportingFiles .add (new SupportingFile ("model_any_type.mustache" , packageName + File .separator + "models" , "model_any_type.nim" ));
249+ supportingFiles .add (new SupportingFile ("model_object.mustache" , packageName + File .separator + "models" , "model_object.nim" ));
195250 }
196251
197252 @ Override
@@ -215,34 +270,68 @@ public String escapeUnsafeCharacters(String input) {
215270
216271 @ Override
217272 public String toModelImport (String name ) {
273+ name = normalizeSchemaName (name );
218274 name = name .replaceAll ("-" , "_" );
275+
219276 if (importMapping .containsKey (name )) {
220- return "model_" + StringUtils .underscore (importMapping .get (name ));
277+ return sanitizeNimIdentifier ( "model_" + StringUtils .underscore (importMapping .get (name ) ));
221278 } else {
222- return "model_" + StringUtils .underscore (name );
279+ return sanitizeNimIdentifier ( "model_" + StringUtils .underscore (name ) );
223280 }
224281 }
225282
226283 @ Override
227284 public String toApiImport (String name ) {
228285 name = name .replaceAll ("-" , "_" );
229286 if (importMapping .containsKey (name )) {
230- return "api_" + StringUtils .underscore (importMapping .get (name ));
287+ return sanitizeNimIdentifier ( "api_" + StringUtils .underscore (importMapping .get (name ) ));
231288 } else {
232- return "api_" + StringUtils .underscore (name );
289+ return sanitizeNimIdentifier ( "api_" + StringUtils .underscore (name ) );
233290 }
234291 }
235292
293+ /**
294+ * Normalize schema names to ensure consistency across filename, import, and type name generation.
295+ * This is called early in the pipeline so downstream methods work with consistent names.
296+ */
297+ private String normalizeSchemaName (String name ) {
298+ if (name == null ) {
299+ return null ;
300+ }
301+ // Remove underscores around and before digits (HTTP status codes, version numbers, etc.)
302+ // e.g., "GetComments_200_response" -> "GetComments200response"
303+ // e.g., "Config_anyOf_1" -> "ConfiganyOf1"
304+ // This ensures consistent handling whether the name comes with or without underscores
305+ name = name .replaceAll ("_(\\ d+)_" , "$1" ); // Underscores on both sides
306+ name = name .replaceAll ("_(\\ d+)$" , "$1" ); // Trailing underscore before digits
307+ return name ;
308+ }
309+
310+ @ Override
311+ public CodegenModel fromModel (String name , Schema schema ) {
312+ // Normalize the schema name before any processing
313+ name = normalizeSchemaName (name );
314+ return super .fromModel (name , schema );
315+ }
316+
317+ @ Override
318+ public String toModelName (String name ) {
319+ // Name should be normalized by fromModel, but normalize again for safety
320+ name = normalizeSchemaName (name );
321+ return camelize (sanitizeName (name ));
322+ }
323+
236324 @ Override
237325 public String toModelFilename (String name ) {
326+ name = normalizeSchemaName (name );
238327 name = name .replaceAll ("-" , "_" );
239- return "model_" + StringUtils .underscore (name );
328+ return sanitizeNimIdentifier ( "model_" + StringUtils .underscore (name ) );
240329 }
241330
242331 @ Override
243332 public String toApiFilename (String name ) {
244333 name = name .replaceAll ("-" , "_" );
245- return "api_" + StringUtils .underscore (name );
334+ return sanitizeNimIdentifier ( "api_" + StringUtils .underscore (name ) );
246335 }
247336
248337 @ Override
@@ -262,6 +351,12 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
262351 List <CodegenOperation > operations = objectMap .getOperation ();
263352 for (CodegenOperation operation : operations ) {
264353 operation .httpMethod = operation .httpMethod .toLowerCase (Locale .ROOT );
354+
355+ // Set custom flag for DELETE operations with body to use different template logic
356+ // Nim's httpClient.delete() doesn't support a body parameter
357+ if ("delete" .equals (operation .httpMethod ) && operation .getHasBodyParam ()) {
358+ operation .vendorExtensions .put ("x-nim-delete-with-body" , true );
359+ }
265360 }
266361
267362 return objs ;
@@ -360,6 +455,24 @@ private boolean isValidIdentifier(String identifier) {
360455 return identifier .matches ("^(?:[A-Z]|[a-z]|[\\ x80-\\ xff])(_?(?:[A-Z]|[a-z]|[\\ x80-\\ xff]|[0-9]))*$" );
361456 }
362457
458+ /**
459+ * Sanitize a Nim identifier by removing trailing underscores and collapsing multiple underscores.
460+ * Nim does not allow identifiers to end with underscores.
461+ *
462+ * @param name the identifier to sanitize
463+ * @return the sanitized identifier
464+ */
465+ private String sanitizeNimIdentifier (String name ) {
466+ if (name == null || name .isEmpty ()) {
467+ return name ;
468+ }
469+ // Remove trailing underscores (Nim identifiers cannot end with underscore)
470+ name = name .replaceAll ("_+$" , "" );
471+ // Collapse multiple consecutive underscores to single underscore
472+ name = name .replaceAll ("_+" , "_" );
473+ return name ;
474+ }
475+
363476 @ Override
364477 public String toEnumVarName (String name , String datatype ) {
365478 name = name .replace (" " , "_" );
0 commit comments