From ab11d6309990a611e22dad5511a1efb597f71b0c Mon Sep 17 00:00:00 2001 From: vyuan2037 Date: Thu, 19 Mar 2026 11:17:10 -0500 Subject: [PATCH] fix(rust): add serde flatten to base types for subclass field preservation Signed-off-by: vyuan2037 --- lib/codegen/fromcto/rust/rustvisitor.js | 27 ++++- test/codegen/__snapshots__/codegen.js.snap | 114 +++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/lib/codegen/fromcto/rust/rustvisitor.js b/lib/codegen/fromcto/rust/rustvisitor.js index 92d8956..8ac9616 100644 --- a/lib/codegen/fromcto/rust/rustvisitor.js +++ b/lib/codegen/fromcto/rust/rustvisitor.js @@ -280,7 +280,16 @@ class RustVisitor { .getAllDeclarations() .find((declaration) => declaration.isMapDeclaration?.()); - if (hasMapDeclaration) { + // Check if any class has subclasses (needs HashMap + serde_json for flatten) + const hasBaseTypes = modelFile + .getAllDeclarations() + .filter((declaration) => declaration.isClassDeclaration?.()) + .some((declaration) => { + const subs = declaration.getDirectSubclasses?.() || []; + return subs.length > 0; + }); + + if (hasMapDeclaration || hasBaseTypes) { parameters.fileWriter.writeLine( 0, 'use std::collections::HashMap;' @@ -326,6 +335,22 @@ class RustVisitor { property.accept(this, parameters); }); + // Add #[serde(flatten)] catch-all field for base types with subtypes. + // When a Concerto class has subclasses, the JSON AST for those subtypes + // contains fields from both the parent and child. Without flatten, the + // parent struct silently drops the child-specific fields during deserialization. + const subclasses = classDeclaration.getDirectSubclasses?.() || []; + if (subclasses.length > 0) { + parameters.fileWriter.writeLine(1, ''); + parameters.fileWriter.writeLine(1, '#[serde('); + parameters.fileWriter.writeLine(2, 'flatten,'); + parameters.fileWriter.writeLine(1, ')]'); + parameters.fileWriter.writeLine( + 1, + 'pub extra: std::collections::HashMap,' + ); + } + parameters.fileWriter.writeLine(0, '}'); parameters.fileWriter.writeLine(0, ''); diff --git a/test/codegen/__snapshots__/codegen.js.snap b/test/codegen/__snapshots__/codegen.js.snap index 308a77d..900015a 100644 --- a/test/codegen/__snapshots__/codegen.js.snap +++ b/test/codegen/__snapshots__/codegen.js.snap @@ -5646,6 +5646,7 @@ exports[`codegen #formats check we can convert all formats from namespace unvers use chrono::{ DateTime, Utc }; use crate::concerto_1_0_0::*; +use std::collections::HashMap; use crate::utils::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5654,6 +5655,11 @@ pub struct Decorator { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5680,6 +5686,7 @@ exports[`codegen #formats check we can convert all formats from namespace unvers use chrono::{ DateTime, Utc }; use crate::concerto_decorator_1_0_0::*; +use std::collections::HashMap; use crate::utils::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5688,6 +5695,11 @@ pub struct Concept { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5701,6 +5713,11 @@ pub struct Asset { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5714,6 +5731,11 @@ pub struct Participant { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5729,6 +5751,11 @@ pub struct Transaction { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -5744,6 +5771,11 @@ pub struct Event { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } ", @@ -5829,6 +5861,11 @@ pub struct Category { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -6052,6 +6089,11 @@ pub struct Equipment { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -6144,6 +6186,11 @@ pub struct Person { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -6246,6 +6293,11 @@ pub struct Employee { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -6441,6 +6493,11 @@ pub struct CompanyEvent { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13509,6 +13566,7 @@ exports[`codegen #formats check we can convert all formats from namespace versio use chrono::{ DateTime, Utc }; use crate::concerto_1_0_0::*; +use std::collections::HashMap; use crate::utils::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13517,6 +13575,11 @@ pub struct Decorator { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13543,6 +13606,7 @@ exports[`codegen #formats check we can convert all formats from namespace versio use chrono::{ DateTime, Utc }; use crate::concerto_decorator_1_0_0::*; +use std::collections::HashMap; use crate::utils::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13551,6 +13615,11 @@ pub struct Concept { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13564,6 +13633,11 @@ pub struct Asset { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13577,6 +13651,11 @@ pub struct Participant { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13592,6 +13671,11 @@ pub struct Transaction { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13607,6 +13691,11 @@ pub struct Event { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } ", @@ -13692,6 +13781,11 @@ pub struct Category { rename = "$class", )] pub _class: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13915,6 +14009,11 @@ pub struct Equipment { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -14007,6 +14106,11 @@ pub struct Person { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -14109,6 +14213,11 @@ pub struct Employee { rename = "$identifier", )] pub _identifier: String, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -14304,6 +14413,11 @@ pub struct CompanyEvent { deserialize_with = "deserialize_datetime", )] pub _timestamp: DateTime, + + #[serde( + flatten, + )] + pub extra: std::collections::HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)]