use crate::{
envelope::{EnvelopeEvent, EnvelopeKind, EnvelopeMeta, WireEnvelope},
northward::{NorthwardData, NorthwardEvent},
};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct NorthwardEnvelope {
pub schema_version: u32,
pub envelope: Option<EnvelopeMeta>,
pub payload: NorthwardEnvelopePayload,
}
impl NorthwardEnvelope {
#[inline]
pub fn v1(payload: NorthwardEnvelopePayload) -> Self {
Self {
schema_version: 1,
envelope: None,
payload,
}
}
#[inline]
pub fn with_meta(mut self, meta: EnvelopeMeta) -> Self {
self.envelope = Some(meta);
self
}
#[inline]
pub fn kind(&self) -> EnvelopeKind {
self.payload.envelope_kind()
}
#[inline]
pub fn event(&self) -> EnvelopeEvent {
EnvelopeEvent { kind: self.kind() }
}
}
#[derive(Debug, Clone)]
pub struct NorthwardEnvelopeRef<'a> {
pub schema_version: u32,
pub envelope: Option<EnvelopeMeta>,
pub payload: NorthwardEnvelopePayloadRef<'a>,
}
impl<'a> NorthwardEnvelopeRef<'a> {
#[inline]
pub fn v1(payload: NorthwardEnvelopePayloadRef<'a>) -> Self {
Self {
schema_version: 1,
envelope: None,
payload,
}
}
#[inline]
pub fn with_meta(mut self, meta: EnvelopeMeta) -> Self {
self.envelope = Some(meta);
self
}
#[inline]
pub fn kind(&self) -> EnvelopeKind {
self.payload.envelope_kind()
}
#[inline]
pub fn event(&self) -> EnvelopeEvent {
EnvelopeEvent { kind: self.kind() }
}
}
#[derive(Debug, Clone)]
pub enum NorthwardEnvelopePayload {
Data(NorthwardData),
Event(NorthwardEvent),
Unknown { kind: EnvelopeKind, raw: Value },
}
#[derive(Debug, Clone)]
pub enum NorthwardEnvelopePayloadRef<'a> {
Data(&'a NorthwardData),
Event(&'a NorthwardEvent),
Unknown { kind: EnvelopeKind, raw: &'a Value },
}
impl NorthwardEnvelopePayloadRef<'_> {
#[inline]
pub fn envelope_kind(&self) -> EnvelopeKind {
match self {
NorthwardEnvelopePayloadRef::Data(d) => d.envelope_kind(),
NorthwardEnvelopePayloadRef::Event(e) => e.envelope_kind(),
NorthwardEnvelopePayloadRef::Unknown { kind, .. } => *kind,
}
}
}
impl NorthwardEnvelopePayload {
#[inline]
pub fn envelope_kind(&self) -> EnvelopeKind {
match self {
NorthwardEnvelopePayload::Data(d) => d.envelope_kind(),
NorthwardEnvelopePayload::Event(e) => e.envelope_kind(),
NorthwardEnvelopePayload::Unknown { kind, .. } => *kind,
}
}
}
impl From<NorthwardData> for NorthwardEnvelopePayload {
#[inline]
fn from(value: NorthwardData) -> Self {
Self::Data(value)
}
}
impl From<NorthwardEvent> for NorthwardEnvelopePayload {
#[inline]
fn from(value: NorthwardEvent) -> Self {
Self::Event(value)
}
}
#[derive(Debug, Error)]
pub enum NorthwardEnvelopeDecodeError {
#[error("payload decode failed (kind={kind:?}): {source}")]
PayloadDecode {
kind: EnvelopeKind,
#[source]
source: serde_json::Error,
},
}
struct NorthwardDataNoTag<'a>(&'a NorthwardData);
impl Serialize for NorthwardDataNoTag<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.0 {
NorthwardData::DeviceConnected(d) => d.serialize(serializer),
NorthwardData::DeviceDisconnected(d) => d.serialize(serializer),
NorthwardData::Telemetry(t) => t.serialize(serializer),
NorthwardData::Attributes(a) => a.serialize(serializer),
NorthwardData::Alarm(a) => a.serialize(serializer),
NorthwardData::RpcResponse(r) => r.serialize(serializer),
NorthwardData::WritePointResponse(r) => r.serialize(serializer),
}
}
}
struct NorthwardEventNoTag<'a>(&'a NorthwardEvent);
impl Serialize for NorthwardEventNoTag<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.0 {
NorthwardEvent::RpcResponseReceived(r) => r.serialize(serializer),
NorthwardEvent::CommandReceived(c) => c.serialize(serializer),
NorthwardEvent::WritePoint(w) => w.serialize(serializer),
}
}
}
struct NorthwardPayloadNoTag<'a>(&'a NorthwardEnvelopePayload);
impl Serialize for NorthwardPayloadNoTag<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.0 {
NorthwardEnvelopePayload::Data(d) => NorthwardDataNoTag(d).serialize(serializer),
NorthwardEnvelopePayload::Event(e) => NorthwardEventNoTag(e).serialize(serializer),
NorthwardEnvelopePayload::Unknown { raw, .. } => raw.serialize(serializer),
}
}
}
#[derive(Serialize)]
struct WirePayloadView<'a> {
data: NorthwardPayloadNoTag<'a>,
}
#[derive(Serialize)]
struct WireEnvelopeView<'a> {
schema_version: u32,
event: EnvelopeEvent,
#[serde(default, skip_serializing_if = "Option::is_none")]
envelope: &'a Option<EnvelopeMeta>,
payload: WirePayloadView<'a>,
}
impl Serialize for NorthwardEnvelope {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let wire = WireEnvelopeView {
schema_version: self.schema_version,
event: self.event(),
envelope: &self.envelope,
payload: WirePayloadView {
data: NorthwardPayloadNoTag(&self.payload),
},
};
wire.serialize(serializer)
}
}
struct NorthwardPayloadRefNoTag<'a>(&'a NorthwardEnvelopePayloadRef<'a>);
impl Serialize for NorthwardPayloadRefNoTag<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.0 {
NorthwardEnvelopePayloadRef::Data(d) => NorthwardDataNoTag(d).serialize(serializer),
NorthwardEnvelopePayloadRef::Event(e) => NorthwardEventNoTag(e).serialize(serializer),
NorthwardEnvelopePayloadRef::Unknown { raw, .. } => raw.serialize(serializer),
}
}
}
#[derive(Serialize)]
struct WirePayloadRefView<'a> {
data: NorthwardPayloadRefNoTag<'a>,
}
#[derive(Serialize)]
struct WireEnvelopeRefView<'a> {
schema_version: u32,
event: EnvelopeEvent,
#[serde(default, skip_serializing_if = "Option::is_none")]
envelope: &'a Option<EnvelopeMeta>,
payload: WirePayloadRefView<'a>,
}
impl Serialize for NorthwardEnvelopeRef<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let wire = WireEnvelopeRefView {
schema_version: self.schema_version,
event: self.event(),
envelope: &self.envelope,
payload: WirePayloadRefView {
data: NorthwardPayloadRefNoTag(&self.payload),
},
};
wire.serialize(serializer)
}
}
fn decode_payload_by_kind(
kind: EnvelopeKind,
raw: Value,
) -> Result<NorthwardEnvelopePayload, NorthwardEnvelopeDecodeError> {
use crate::northward::model::{
AlarmData, AttributeData, ClientRpcResponse, Command, DeviceConnectedData,
DeviceDisconnectedData, ServerRpcResponse, TelemetryData, WritePoint, WritePointResponse,
};
match kind {
EnvelopeKind::DeviceConnected => {
let d: DeviceConnectedData = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(
NorthwardData::DeviceConnected(d),
))
}
EnvelopeKind::DeviceDisconnected => {
let d: DeviceDisconnectedData = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(
NorthwardData::DeviceDisconnected(d),
))
}
EnvelopeKind::Telemetry => {
let t: TelemetryData = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(NorthwardData::Telemetry(t)))
}
EnvelopeKind::Attributes => {
let a: AttributeData = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(NorthwardData::Attributes(a)))
}
EnvelopeKind::Alarm => {
let a: AlarmData = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(NorthwardData::Alarm(a)))
}
EnvelopeKind::RpcResponse => {
let r: ClientRpcResponse = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(NorthwardData::RpcResponse(
r,
)))
}
EnvelopeKind::WritePointResponse => {
let r: WritePointResponse = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Data(
NorthwardData::WritePointResponse(r),
))
}
EnvelopeKind::WritePoint => {
let w: WritePoint = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Event(NorthwardEvent::WritePoint(
w,
)))
}
EnvelopeKind::CommandReceived => {
let c: Command = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Event(
NorthwardEvent::CommandReceived(c),
))
}
EnvelopeKind::RpcResponseReceived => {
let r: ServerRpcResponse = serde_json::from_value(raw)
.map_err(|source| NorthwardEnvelopeDecodeError::PayloadDecode { kind, source })?;
Ok(NorthwardEnvelopePayload::Event(
NorthwardEvent::RpcResponseReceived(r),
))
}
}
}
impl TryFrom<WireEnvelope<Value>> for NorthwardEnvelope {
type Error = NorthwardEnvelopeDecodeError;
fn try_from(value: WireEnvelope<Value>) -> Result<Self, Self::Error> {
let payload = decode_payload_by_kind(value.event.kind, value.payload.data)?;
Ok(Self {
schema_version: value.schema_version,
envelope: value.envelope,
payload,
})
}
}
impl<'de> Deserialize<'de> for NorthwardEnvelope {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let wire: WireEnvelope<Value> = WireEnvelope::deserialize(deserializer)?;
NorthwardEnvelope::try_from(wire).map_err(serde::de::Error::custom)
}
}