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}