feat(client-rust): add write_array_elements default-fill helper and document semantics

Handles the new MxSparseArray wire type (proto field 19 on MxValue::Kind):
- value.rs: map SparseArrayValue to MxValueProjection::Unset (write-only; never emitted on read path)
- session.rs: add write_array_elements() that builds the sparse proto value and delegates to write()
- tests: three unit tests asserting proto shape, empty-elements case, and read-path Unset projection
- README: document write_array_elements default-fill semantics and bare-name [] normalisation
This commit is contained in:
Joseph Doherty
2026-06-18 03:02:15 -04:00
parent b7f29f3048
commit 3a8f2bed4e
4 changed files with 191 additions and 9 deletions
+91 -2
View File
@@ -24,8 +24,8 @@ use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::{
AddItem2Reply, AddItemReply, AlarmConditionState, AlarmFeedMessage, AlarmTransitionKind,
BulkReadReply, BulkReadResult, BulkSubscribeReply, BulkWriteReply, BulkWriteResult,
CloseSessionReply, CloseSessionRequest, MxCommandKind, MxCommandReply, MxDataType, MxEvent,
MxEventFamily, MxStatusCategory, MxStatusProxy, MxStatusSource, MxValue,
OnAlarmTransitionEvent, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
MxEventFamily, MxSparseArray, MxSparseElement, MxStatusCategory, MxStatusProxy, MxStatusSource,
MxValue, OnAlarmTransitionEvent, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
ProtocolStatusCode, QueryActiveAlarmsRequest, RegisterReply, SessionState, StreamAlarmsRequest,
StreamEventsRequest, SubscribeResult, Write2BulkEntry, WriteBulkEntry, WriteSecured2BulkEntry,
WriteSecuredBulkEntry,
@@ -1091,3 +1091,92 @@ fn case_by_id<'a>(cases: &'a [Value], id: &str) -> &'a Value {
.find(|case| case["id"].as_str() == Some(id))
.unwrap_or_else(|| panic!("missing fixture case {id}"))
}
// ---------------------------------------------------------------------------
// write_array_elements — proto shape unit tests
// ---------------------------------------------------------------------------
/// Build the proto `MxValue` that `write_array_elements` would send and assert
/// the sparse oneof variant has the correct `total_length` and elements.
fn sparse_int32_value(
total_length: u32,
elements: impl IntoIterator<Item = (u32, i32)>,
) -> MxValue {
let sparse_elements: Vec<MxSparseElement> = elements
.into_iter()
.map(|(index, v)| MxSparseElement {
index,
value: Some(MxValue {
data_type: MxDataType::Integer as i32,
variant_type: "VT_I4".to_owned(),
kind: Some(Kind::Int32Value(v)),
..MxValue::default()
}),
})
.collect();
MxValue {
data_type: MxDataType::Integer as i32,
variant_type: String::new(),
kind: Some(Kind::SparseArrayValue(MxSparseArray {
element_data_type: MxDataType::Integer as i32,
total_length,
elements: sparse_elements,
})),
..MxValue::default()
}
}
#[test]
fn write_array_elements_proto_shape_has_sparse_oneof_kind() {
let proto = sparse_int32_value(5, [(0, 10), (3, 30)]);
let Kind::SparseArrayValue(ref sparse) = proto.kind.as_ref().unwrap() else {
panic!("expected SparseArrayValue kind, got {:?}", proto.kind);
};
assert_eq!(sparse.total_length, 5, "total_length must round-trip");
assert_eq!(sparse.elements.len(), 2, "two elements supplied");
assert_eq!(sparse.element_data_type, MxDataType::Integer as i32);
let elem0 = &sparse.elements[0];
assert_eq!(elem0.index, 0);
assert_eq!(
elem0.value.as_ref().unwrap().kind,
Some(Kind::Int32Value(10))
);
let elem3 = &sparse.elements[1];
assert_eq!(elem3.index, 3);
assert_eq!(
elem3.value.as_ref().unwrap().kind,
Some(Kind::Int32Value(30))
);
}
#[test]
fn write_array_elements_empty_elements_is_valid_all_defaults() {
let proto = sparse_int32_value(8, []);
let Kind::SparseArrayValue(ref sparse) = proto.kind.as_ref().unwrap() else {
panic!("expected SparseArrayValue kind");
};
assert_eq!(sparse.total_length, 8);
assert!(
sparse.elements.is_empty(),
"no elements means every index defaults"
);
}
#[test]
fn sparse_array_value_round_trips_through_client_mx_value_projection_as_unset() {
// SparseArrayValue is write-only. If it ever arrives on the read path
// (e.g. a future version bug), the projection should degrade to Unset
// rather than panic, because the enum variant is not readable.
let proto = sparse_int32_value(4, [(1, 99)]);
let client_value = ClientMxValue::from_proto(proto);
assert_eq!(
client_value.projection(),
&MxValueProjection::Unset,
"write-only SparseArrayValue must project to Unset, not panic"
);
}