fix: CrdtNode derive macro defaults missing fields instead of panicking

When replaying old CRDT ops that predate new struct fields (e.g.
claimed_by, claim_ts added by story 479), node_from would call
.unwrap() on None and panic during init. Now defaults to an empty
CrdtNode::new() for missing fields, allowing schema evolution without
breaking replay of historical ops.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-04-11 00:16:10 +01:00
parent fc24da82ae
commit de738b27ed
@@ -94,14 +94,18 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream {
Ok(#ident { Ok(#ident {
path: path.clone(), path: path.clone(),
id, id,
#(#ident_literals: obj.remove(#ident_strings) #(#ident_literals: if let Some(val) = obj.remove(#ident_strings) {
.unwrap() val.into_node(
.into_node(
id, id,
#crate_name::op::join_path(path.clone(), #crate_name::op::PathSegment::Field(#ident_strings.to_string())) #crate_name::op::join_path(path.clone(), #crate_name::op::PathSegment::Field(#ident_strings.to_string()))
) )
.unwrap() .unwrap()
),* } else {
<#tys as #crate_name::json_crdt::CrdtNode>::new(
id,
#crate_name::op::join_path(path.clone(), #crate_name::op::PathSegment::Field(#ident_strings.to_string()))
)
}),*
}) })
} else { } else {
Err(format!("failed to convert {:?} -> {}<T>", value, #ident_str.to_string())) Err(format!("failed to convert {:?} -> {}<T>", value, #ident_str.to_string()))