Skip to content

Commit ddc7f62

Browse files
lxsaahclaude
andcommitted
fix(codegen): emit serde_json dependency for mixed JSON+postcard manifests
generate_cargo_toml suppressed the serde_json dependency whenever any record used postcard serialization ('has_non_custom_ser && !has_postcard'), but emit_linkable_json still emits bare serde_json:: calls for the JSON records — so a manifest mixing JSON and postcard records generated a crate that failed to compile under the std feature. Gate the serde_json dependency on 'any record uses JSON' instead, independent of postcard. Regression tests cover the mixed manifest and pin the postcard-only case (no serde_json). Refs #155 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 6ecedf9 commit ddc7f62

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

aimdb-codegen/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Generated `Cargo.toml` for manifests mixing `serialization = "json"` and `serialization = "postcard"` records omitted the `serde_json` dependency (postcard presence suppressed it), so the generated crate failed to compile under the `std` feature. `serde_json` is now emitted whenever any record uses JSON serialization, independent of postcard (#155).
13+
1014
### Changed (breaking)
1115

1216
- Emitted task scaffolds now use `Producer<T>` / `Consumer<T>` (no `, TokioAdapter` second parameter) and emitted doc tables show the same form, matching the M14 cleanup in `aimdb-core` (Design 029). Regenerate downstream scaffolds after upgrading.

aimdb-codegen/src/rust.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ pub fn generate_cargo_toml(state: &ArchitectureState) -> String {
127127
let has_non_custom_ser = state.records.iter().any(|r| {
128128
r.serialization.as_ref().unwrap_or(&SerializationType::Json) != &SerializationType::Custom
129129
});
130+
let has_json = state.records.iter().any(|r| {
131+
r.serialization.as_ref().unwrap_or(&SerializationType::Json) == &SerializationType::Json
132+
});
130133
let has_postcard = state
131134
.records
132135
.iter()
@@ -153,7 +156,7 @@ pub fn generate_cargo_toml(state: &ArchitectureState) -> String {
153156

154157
// Build std feature deps
155158
let mut std_deps = vec!["\"aimdb-data-contracts/std\"".to_string()];
156-
if has_non_custom_ser && !has_postcard {
159+
if has_json {
157160
std_deps.push("\"serde_json\"".to_string());
158161
}
159162
if has_observable {
@@ -162,7 +165,7 @@ pub fn generate_cargo_toml(state: &ArchitectureState) -> String {
162165
let std_features = std_deps.join(", ");
163166

164167
let mut optional_deps = String::new();
165-
if has_non_custom_ser && !has_postcard {
168+
if has_json {
166169
optional_deps.push_str("serde_json = { version = \"1.0\", optional = true }\n");
167170
}
168171
if has_postcard {
@@ -2219,6 +2222,75 @@ url = "sensors/{variant}/observation"
22192222
);
22202223
}
22212224

2225+
const MIXED_CODEC_TOML: &str = r#"
2226+
[project]
2227+
name = "mixed-codec"
2228+
2229+
[meta]
2230+
aimdb_version = "1.2.0"
2231+
created_at = "2026-07-10T00:00:00Z"
2232+
last_modified = "2026-07-10T00:00:00Z"
2233+
2234+
[[records]]
2235+
name = "JsonReading"
2236+
buffer = "SpmcRing"
2237+
capacity = 16
2238+
key_prefix = "readings.json."
2239+
key_variants = ["a"]
2240+
serialization = "json"
2241+
2242+
[[records.fields]]
2243+
name = "value"
2244+
type = "f32"
2245+
description = "Reading value"
2246+
2247+
[[records]]
2248+
name = "PostcardReading"
2249+
buffer = "SpmcRing"
2250+
capacity = 16
2251+
key_prefix = "readings.postcard."
2252+
key_variants = ["a"]
2253+
serialization = "postcard"
2254+
2255+
[[records.fields]]
2256+
name = "value"
2257+
type = "f32"
2258+
description = "Reading value"
2259+
"#;
2260+
2261+
#[test]
2262+
fn cargo_toml_mixed_json_postcard_has_both_deps() {
2263+
let state = ArchitectureState::from_toml(MIXED_CODEC_TOML).unwrap();
2264+
let toml = generate_cargo_toml(&state);
2265+
assert!(
2266+
toml.contains("serde_json = { version = \"1.0\", optional = true }"),
2267+
"JSON record present but serde_json dep missing:\n{toml}"
2268+
);
2269+
assert!(
2270+
toml.contains("\"serde_json\""),
2271+
"serde_json missing from std feature deps:\n{toml}"
2272+
);
2273+
assert!(
2274+
toml.contains("postcard = { version = \"1.0\""),
2275+
"Postcard record present but postcard dep missing:\n{toml}"
2276+
);
2277+
}
2278+
2279+
#[test]
2280+
fn cargo_toml_postcard_only_omits_serde_json() {
2281+
let mut state = ArchitectureState::from_toml(MIXED_CODEC_TOML).unwrap();
2282+
state.records.retain(|r| r.name == "PostcardReading");
2283+
let toml = generate_cargo_toml(&state);
2284+
assert!(
2285+
!toml.contains("serde_json"),
2286+
"Postcard-only crate should not depend on serde_json:\n{toml}"
2287+
);
2288+
assert!(
2289+
toml.contains("postcard = { version = \"1.0\""),
2290+
"Missing postcard dep:\n{toml}"
2291+
);
2292+
}
2293+
22222294
#[test]
22232295
fn generate_lib_rs_output() {
22242296
let lib = generate_lib_rs();

0 commit comments

Comments
 (0)