//! Rust-shaped wrappers around the wire `MxValue`, `MxArray`, and //! `MxStatusProxy` types. //! //! [`MxValue`] keeps both the original protobuf message and a friendly //! [`MxValueProjection`] enum, so callers can pass values through the wire //! without losing information while still being able to pattern-match on //! the typed variant. The same split applies to [`MxArrayValue`] and //! [`MxArrayProjection`]. [`MxStatus`] wraps the MXAccess `MxStatusProxy` //! status envelope. use crate::generated::mxaccess_gateway::v1::mx_array::Values; use crate::generated::mxaccess_gateway::v1::mx_value::Kind; use crate::generated::mxaccess_gateway::v1::{ BoolArray, DoubleArray, FloatArray, Int32Array, Int64Array, MxArray, MxDataType, MxStatusCategory, MxStatusProxy, MxStatusSource, MxValue as ProtoMxValue, RawArray, StringArray, TimestampArray, }; /// Owned `MxValue` carrying both the raw protobuf message and a typed /// [`MxValueProjection`] view. /// /// The constructors set both `data_type` and `variant_type` to the values /// the worker expects so values built locally round-trip through MXAccess /// without surprises. #[derive(Clone, Debug, PartialEq)] pub struct MxValue { raw: ProtoMxValue, projection: MxValueProjection, } impl MxValue { /// Wrap a protobuf [`ProtoMxValue`] and compute its /// [`MxValueProjection`]. pub fn from_proto(raw: ProtoMxValue) -> Self { let projection = MxValueProjection::from_proto(&raw); Self { raw, projection } } /// Build a boolean `MxValue` (`MxDataType::Boolean`, `VT_BOOL`). pub fn bool(value: bool) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::Boolean as i32, variant_type: "VT_BOOL".to_owned(), kind: Some(Kind::BoolValue(value)), ..ProtoMxValue::default() }) } /// Build a 32-bit integer `MxValue` (`MxDataType::Integer`, `VT_I4`). pub fn int32(value: i32) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::Integer as i32, variant_type: "VT_I4".to_owned(), kind: Some(Kind::Int32Value(value)), ..ProtoMxValue::default() }) } /// Build a 64-bit integer `MxValue` (`MxDataType::Integer`, `VT_I8`). pub fn int64(value: i64) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::Integer as i32, variant_type: "VT_I8".to_owned(), kind: Some(Kind::Int64Value(value)), ..ProtoMxValue::default() }) } /// Build a 32-bit float `MxValue` (`MxDataType::Float`, `VT_R4`). pub fn float(value: f32) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::Float as i32, variant_type: "VT_R4".to_owned(), kind: Some(Kind::FloatValue(value)), ..ProtoMxValue::default() }) } /// Build a 64-bit float `MxValue` (`MxDataType::Double`, `VT_R8`). pub fn double(value: f64) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::Double as i32, variant_type: "VT_R8".to_owned(), kind: Some(Kind::DoubleValue(value)), ..ProtoMxValue::default() }) } /// Build a string `MxValue` (`MxDataType::String`, `VT_BSTR`). pub fn string(value: impl Into) -> Self { Self::from_proto(ProtoMxValue { data_type: MxDataType::String as i32, variant_type: "VT_BSTR".to_owned(), kind: Some(Kind::StringValue(value.into())), ..ProtoMxValue::default() }) } /// Borrow the underlying protobuf message exactly as it will travel /// over the wire. pub fn raw(&self) -> &ProtoMxValue { &self.raw } /// Borrow the typed projection. pub fn projection(&self) -> &MxValueProjection { &self.projection } /// Consume the wrapper and return the underlying protobuf message. pub fn into_proto(self) -> ProtoMxValue { self.raw } } impl From for ProtoMxValue { fn from(value: MxValue) -> Self { value.into_proto() } } impl From for MxValue { fn from(value: ProtoMxValue) -> Self { Self::from_proto(value) } } /// Typed view over an [`MxValue`]. /// /// Mirrors the `MxValue::Kind` oneof on the wire, plus a [`MxValueProjection::Null`] /// variant for `is_null=true` and a [`MxValueProjection::Unset`] variant for /// values that arrive without a `kind` set. #[derive(Clone, Debug, PartialEq)] pub enum MxValueProjection { /// No `kind` was present on the wire. Unset, /// `is_null = true` on the wire. Null, /// Boolean value. Bool(bool), /// 32-bit signed integer value. Int32(i32), /// 64-bit signed integer value. Int64(i64), /// 32-bit float value. Float(f32), /// 64-bit float value. Double(f64), /// UTF-8 string value. String(String), /// Wall-clock timestamp. Timestamp(prost_types::Timestamp), /// Array value carrying a homogeneous element type. Array(MxArrayValue), /// Opaque variant payload that the gateway could not project to a typed /// scalar. Raw(Vec), } impl MxValueProjection { fn from_proto(value: &ProtoMxValue) -> Self { if value.is_null { return Self::Null; } match value.kind.as_ref() { Some(Kind::BoolValue(value)) => Self::Bool(*value), Some(Kind::Int32Value(value)) => Self::Int32(*value), Some(Kind::Int64Value(value)) => Self::Int64(*value), Some(Kind::FloatValue(value)) => Self::Float(*value), Some(Kind::DoubleValue(value)) => Self::Double(*value), Some(Kind::StringValue(value)) => Self::String(value.clone()), Some(Kind::TimestampValue(value)) => Self::Timestamp(*value), Some(Kind::ArrayValue(value)) => Self::Array(MxArrayValue::from_proto(value.clone())), Some(Kind::RawValue(value)) => Self::Raw(value.clone()), None => Self::Unset, } } } /// Owned `MxArray` carrying both the raw protobuf message and a typed /// [`MxArrayProjection`] view of its elements. #[derive(Clone, Debug, PartialEq)] pub struct MxArrayValue { raw: MxArray, projection: MxArrayProjection, } impl MxArrayValue { /// Wrap a protobuf [`MxArray`] and compute its /// [`MxArrayProjection`]. pub fn from_proto(raw: MxArray) -> Self { let projection = MxArrayProjection::from_proto(&raw); Self { raw, projection } } /// Build a one-dimensional string array (`VT_ARRAY|VT_BSTR`). pub fn string(values: Vec) -> Self { Self::from_proto(MxArray { element_data_type: MxDataType::String as i32, variant_type: "VT_ARRAY|VT_BSTR".to_owned(), dimensions: vec![values.len() as u32], values: Some(Values::StringValues(StringArray { values })), ..MxArray::default() }) } /// Borrow the underlying protobuf array message. pub fn raw(&self) -> &MxArray { &self.raw } /// Borrow the typed projection of the array's elements. pub fn projection(&self) -> &MxArrayProjection { &self.projection } } /// Typed view over an [`MxArrayValue`]. /// /// Each variant matches the corresponding `MxArray::Values` oneof arm on /// the wire. #[derive(Clone, Debug, PartialEq)] pub enum MxArrayProjection { /// No `values` oneof was present on the wire. Unset, /// Boolean elements. Bool(Vec), /// 32-bit signed integer elements. Int32(Vec), /// 64-bit signed integer elements. Int64(Vec), /// 32-bit float elements. Float(Vec), /// 64-bit float elements. Double(Vec), /// UTF-8 string elements. String(Vec), /// Timestamp elements. Timestamp(Vec), /// Opaque variant payloads, one per element. Raw(Vec>), } impl MxArrayProjection { fn from_proto(array: &MxArray) -> Self { match array.values.as_ref() { Some(Values::BoolValues(BoolArray { values })) => Self::Bool(values.clone()), Some(Values::Int32Values(Int32Array { values })) => Self::Int32(values.clone()), Some(Values::Int64Values(Int64Array { values })) => Self::Int64(values.clone()), Some(Values::FloatValues(FloatArray { values })) => Self::Float(values.clone()), Some(Values::DoubleValues(DoubleArray { values })) => Self::Double(values.clone()), Some(Values::StringValues(StringArray { values })) => Self::String(values.clone()), Some(Values::TimestampValues(TimestampArray { values })) => { Self::Timestamp(values.clone()) } Some(Values::RawValues(RawArray { values })) => Self::Raw(values.clone()), None => Self::Unset, } } } /// Typed wrapper around the MXAccess `MxStatusProxy` envelope, the /// per-value status that accompanies every `OnDataChange`/`Read` reply. #[derive(Clone, Debug, PartialEq)] pub struct MxStatus { raw: MxStatusProxy, } impl MxStatus { /// Wrap a protobuf [`MxStatusProxy`]. pub fn from_proto(raw: MxStatusProxy) -> Self { Self { raw } } /// Borrow the underlying protobuf message. pub fn raw(&self) -> &MxStatusProxy { &self.raw } /// `MXSTATUS_PROXY.Success` flag (0 = error, non-zero = good/warning). pub fn success(&self) -> i32 { self.raw.success } /// Decode the status category, or `None` if the wire value is not a /// known [`MxStatusCategory`]. pub fn category(&self) -> Option { MxStatusCategory::try_from(self.raw.category).ok() } /// Decode the source (provider, gateway, worker) that flagged the /// status, or `None` for unknown values. pub fn detected_by(&self) -> Option { MxStatusSource::try_from(self.raw.detected_by).ok() } /// `MXSTATUS_PROXY.Detail` numeric reason code. pub fn detail(&self) -> i32 { self.raw.detail } /// Raw, undecoded category integer (useful for forward compatibility). pub fn raw_category(&self) -> i32 { self.raw.raw_category } /// Raw, undecoded detected-by integer. pub fn raw_detected_by(&self) -> i32 { self.raw.raw_detected_by } /// Optional human-readable diagnostic from MXAccess. pub fn diagnostic_text(&self) -> &str { &self.raw.diagnostic_text } }