Skip to main content

dyn_encoding/
value.rs

1//! Wire value, type id, and codec traits.
2
3use std::any::Any;
4use std::fmt;
5
6use crate::error::CodecError;
7
8/// Stable identifier for a structured wire-message type.
9///
10/// `WireTypeId` is a thin newtype around a `&'static str` so that the
11/// registry-side hash-map is cheap to look up and the value is
12/// printable in diagnostics. Type ids should be namespaced
13/// (`"riak.GetReq"`, `"riak.PutResp"`, ...) to avoid collisions
14/// between protocol layers.
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16pub struct WireTypeId(&'static str);
17
18impl WireTypeId {
19    /// Construct a type id from a static string.
20    #[must_use]
21    pub const fn new(s: &'static str) -> Self {
22        Self(s)
23    }
24
25    /// Borrow the underlying string.
26    #[must_use]
27    pub const fn as_str(self) -> &'static str {
28        self.0
29    }
30}
31
32impl fmt::Display for WireTypeId {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.write_str(self.0)
35    }
36}
37
38/// A value that can travel on the wire under some [`WireCodec`].
39///
40/// Implementors give every concrete message type a stable
41/// [`WireTypeId`]. The trait carries no encode/decode methods of its
42/// own: per-codec bounds (such as `serde::Serialize` for JSON/CBOR or
43/// `prost::Message` for protobuf) are enforced at codec
44/// registration time.
45pub trait WireValue: fmt::Debug + Send + Sync + 'static {
46    /// Stable identifier for the message type. The id is used by the
47    /// codec registry to dispatch to the correct decoder when bytes
48    /// arrive on the wire.
49    fn wire_type_id() -> WireTypeId
50    where
51        Self: Sized;
52}
53
54/// Object-safe view over [`WireValue`].
55///
56/// Codec implementations work in terms of `&dyn ErasedWireValue` so
57/// they can be invoked through `&dyn WireCodec` without becoming
58/// generic. The blanket impl below makes the conversion implicit at
59/// call sites: any `&T` where `T: WireValue` coerces to
60/// `&dyn ErasedWireValue`.
61pub trait ErasedWireValue: fmt::Debug + Send + Sync + 'static {
62    /// The wire type id of the underlying concrete value.
63    fn type_id(&self) -> WireTypeId;
64
65    /// Provide an `Any` view so codec impls can downcast to the
66    /// concrete type they registered.
67    fn as_any(&self) -> &dyn Any;
68}
69
70impl<T> ErasedWireValue for T
71where
72    T: WireValue,
73{
74    fn type_id(&self) -> WireTypeId {
75        <T as WireValue>::wire_type_id()
76    }
77
78    fn as_any(&self) -> &dyn Any {
79        self
80    }
81}
82
83/// A wire-format codec that turns a structured request or response
84/// value into bytes and back.
85///
86/// Schema-first codecs (protobuf today, plus the deferred
87/// FlatBuffers / Cap'n Proto / Bebop) and schema-less codecs
88/// (JSON / CBOR / BSON) are both addressable through this trait.
89/// Per-type registration happens on the concrete codec; the trait
90/// itself is intentionally narrow so it can be stored as
91/// `Box<dyn WireCodec>` inside the [`crate::CodecRegistry`].
92pub trait WireCodec: Send + Sync + 'static {
93    /// Content-type header value that identifies this codec on the
94    /// wire (for example `"application/x-protobuf"`,
95    /// `"application/json"`, `"application/cbor"`).
96    fn content_type(&self) -> &'static str;
97
98    /// Encode a [`WireValue`] to bytes.
99    fn encode(&self, value: &dyn ErasedWireValue) -> Result<Vec<u8>, CodecError>;
100
101    /// Decode `bytes` into a value of the requested type.
102    ///
103    /// The caller is expected to know what shape it asked for and
104    /// pass the matching [`WireTypeId`].
105    fn decode(
106        &self,
107        type_id: WireTypeId,
108        bytes: &[u8],
109    ) -> Result<Box<dyn ErasedWireValue>, CodecError>;
110}