use crate::bridge::envelope::ErrorCode;
pub(in crate::data::executor) struct FieldSetComputation {
pub new_value: Vec<u8>,
pub fields_added: u64,
}
pub(in crate::data::executor) fn merge_field_updates(
current: Option<&[u8]>,
updates: &[(String, Vec<u8>)],
) -> Result<FieldSetComputation, ErrorCode> {
let mut doc: serde_json::Map<String, serde_json::Value> = current
.and_then(|v| nodedb_types::json_from_msgpack(v).ok())
.and_then(|v| {
if let serde_json::Value::Object(m) = v {
Some(m)
} else {
None
}
})
.unwrap_or_default();
let mut fields_added = 0u64;
for (field, value_bytes) in updates {
let new_value = if value_bytes.is_empty() {
serde_json::Value::Null
} else {
nodedb_types::json_from_msgpack(value_bytes).map_err(|e| ErrorCode::Internal {
detail: format!("field set '{field}': msgpack decode: {e}"),
})?
};
if !doc.contains_key(field) {
fields_added += 1;
}
doc.insert(field.clone(), new_value);
}
let new_value =
nodedb_types::json_to_msgpack(&serde_json::Value::Object(doc)).map_err(|e| {
ErrorCode::Internal {
detail: format!("field set serialization: {e}"),
}
})?;
Ok(FieldSetComputation {
new_value,
fields_added,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn merges_into_empty_document() {
let val = nodedb_types::json_to_msgpack(&serde_json::json!(42)).unwrap();
let result = merge_field_updates(None, &[("score".to_string(), val)]).unwrap();
assert_eq!(result.fields_added, 1);
let doc: serde_json::Value = nodedb_types::json_from_msgpack(&result.new_value).unwrap();
assert_eq!(doc["score"], serde_json::json!(42));
}
#[test]
fn overwrite_does_not_count_as_added() {
let existing = nodedb_types::json_to_msgpack(&serde_json::json!({"score": 1})).unwrap();
let val = nodedb_types::json_to_msgpack(&serde_json::json!(2)).unwrap();
let result = merge_field_updates(Some(&existing), &[("score".to_string(), val)]).unwrap();
assert_eq!(result.fields_added, 0);
let doc: serde_json::Value = nodedb_types::json_from_msgpack(&result.new_value).unwrap();
assert_eq!(doc["score"], serde_json::json!(2));
}
#[test]
fn empty_value_bytes_set_null() {
let result = merge_field_updates(None, &[("f".to_string(), Vec::new())]).unwrap();
let doc: serde_json::Value = nodedb_types::json_from_msgpack(&result.new_value).unwrap();
assert!(doc["f"].is_null());
}
}