sparkplug_b/codec/mod.rs
1//! Sparkplug B protobuf (proto2) codec — hand-written and `protoc`-free (ADR-1).
2//!
3//! The codec is pure: it has no notion of seq/bdSeq/STATE/transport (those live
4//! in higher layers). Encoding is total; decoding never panics on hostile input.
5
6mod arrays;
7mod decode;
8mod encode;
9mod wire;
10
11use bytes::Bytes;
12
13use crate::alias::AliasRegistry;
14use crate::error::Result;
15use crate::model::Payload;
16
17/// Options controlling how a payload is encoded.
18#[derive(Clone, Copy, Debug, Default)]
19pub struct EncodeOptions {
20 /// When `true`, per-metric `datatype` fields are omitted (DATA/CMD messages,
21 /// `tck-id-payloads-metric-datatype-not-req`). Decoding such a payload then
22 /// requires an [`AliasRegistry`] built from the corresponding birth.
23 pub strip_datatypes: bool,
24}
25
26impl EncodeOptions {
27 /// Options for a BIRTH/DEATH message — datatypes included.
28 #[must_use]
29 pub const fn birth() -> Self {
30 Self {
31 strip_datatypes: false,
32 }
33 }
34
35 /// Options for a DATA/CMD message — datatypes stripped.
36 #[must_use]
37 pub const fn data() -> Self {
38 Self {
39 strip_datatypes: true,
40 }
41 }
42}
43
44/// Encode a [`Payload`] to Sparkplug B protobuf bytes.
45#[must_use]
46pub fn encode(payload: &Payload, opts: EncodeOptions) -> Bytes {
47 encode::encode_payload(payload, opts.strip_datatypes)
48}
49
50/// Decode Sparkplug B protobuf bytes into a [`Payload`].
51///
52/// Pass `types` (an [`AliasRegistry`] built from the birth) to recover datatypes
53/// for stripped DATA/CMD metrics; pass `None` for BIRTH/DEATH payloads, which
54/// carry their own datatypes.
55///
56/// # Errors
57/// Returns a [`crate::SparkplugError`] for malformed input: truncation, invalid
58/// wire/field types, unknown datatypes, a stripped metric whose datatype cannot
59/// be recovered, an inconsistent DataSet/PropertySet, or excessive nesting.
60pub fn decode(bytes: &[u8], types: Option<&AliasRegistry>) -> Result<Payload> {
61 decode::decode_payload(bytes, types)
62}