asteroid_mq_model/
codec.rs

1use std::{
2    borrow::Cow,
3    collections::{HashMap, HashSet},
4    fmt::Display,
5    str::FromStr,
6    sync::Arc,
7};
8#[cfg(feature = "bincode")]
9mod bincode;
10#[cfg(feature = "bincode")]
11pub use bincode::*;
12#[cfg(feature = "cbor")]
13mod cbor;
14#[cfg(feature = "cbor")]
15pub use cbor::*;
16mod json;
17pub use json::*;
18
19use crate::EdgePayload;
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22#[repr(transparent)]
23#[derive(serde::Serialize, serde::Deserialize)]
24#[serde(transparent)]
25pub struct CodecKind(pub u8);
26
27impl CodecKind {
28    pub const CBOR: Self = Self(0x00);
29    pub const BINCODE: Self = Self(0x01);
30    pub const JSON: Self = Self(0x40);
31}
32
33impl FromStr for CodecKind {
34    type Err = &'static str;
35    fn from_str(s: &str) -> Result<Self, Self::Err> {
36        match s {
37            "cbor" => Ok(Self::CBOR),
38            "bincode" => Ok(Self::BINCODE),
39            "json" => Ok(Self::JSON),
40            _ => u8::from_str_radix(s, 16)
41                .map(Self)
42                .map_err(|_| "invalid codec kind"),
43        }
44    }
45}
46
47impl Display for CodecKind {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match *self {
50            CodecKind::CBOR => write!(f, "CBOR"),
51            CodecKind::BINCODE => write!(f, "Bincode"),
52            CodecKind::JSON => write!(f, "JSON"),
53            _ => write!(f, "Unknown({:02x})", self.0),
54        }
55    }
56}
57
58#[derive(Debug)]
59pub struct CodecError {
60    reason: Cow<'static, str>,
61}
62
63impl std::fmt::Display for CodecError {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "codec error: {}", self.reason)
66    }
67}
68impl std::error::Error for CodecError {}
69
70impl CodecError {
71    pub fn decode_error<E: std::fmt::Display>(e: E) -> Self {
72        Self {
73            reason: format!("decode error: {}", e).into(),
74        }
75    }
76    pub fn encode_error<E: std::fmt::Display>(e: E) -> Self {
77        Self {
78            reason: format!("encode error: {}", e).into(),
79        }
80    }
81    pub fn unregistered_codec(codec: CodecKind) -> Self {
82        Self {
83            reason: format!("unregistered codec: {}", codec).into(),
84        }
85    }
86}
87
88pub trait Codec: Send + Sync + 'static {
89    fn encode(&self, value: &EdgePayload) -> Result<Vec<u8>, CodecError>;
90    fn decode(&self, bytes: &[u8]) -> Result<EdgePayload, CodecError>;
91    fn kind(&self) -> CodecKind;
92}
93#[derive(Clone)]
94pub struct DynCodec {
95    codec: Arc<dyn Codec>,
96}
97
98impl std::fmt::Debug for DynCodec {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        f.debug_struct("DynCodec")
101            .field("kind", &self.codec.kind())
102            .finish()
103    }
104}
105
106impl Codec for DynCodec {
107    fn encode(&self, value: &EdgePayload) -> Result<Vec<u8>, CodecError> {
108        self.codec.encode(value)
109    }
110    fn decode(&self, bytes: &[u8]) -> Result<EdgePayload, CodecError> {
111        self.codec.decode(bytes)
112    }
113    fn kind(&self) -> CodecKind {
114        self.codec.kind()
115    }
116}
117
118impl DynCodec {
119    pub fn new<C: Codec>(codec: C) -> Self {
120        Self {
121            codec: Arc::new(codec),
122        }
123    }
124    pub fn form_kind(kind: CodecKind) -> Option<Self> {
125        match kind {
126            #[cfg(feature = "cbor")]
127            CodecKind::CBOR => Some(Self::new(cbor::Cbor)),
128            CodecKind::BINCODE => Some(Self::new(bincode::Bincode)),
129            CodecKind::JSON => Some(Self::new(json::Json)),
130            _ => None,
131        }
132    }
133}
134
135pub struct CodecRegistry {
136    registry: HashMap<CodecKind, Arc<dyn Codec>>,
137}
138
139impl std::fmt::Debug for CodecRegistry {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        f.debug_set().entries(self.registry.keys()).finish()
142    }
143}
144
145impl CodecRegistry {
146    pub fn new_empty() -> Self {
147        Self {
148            registry: HashMap::new(),
149        }
150    }
151    pub fn new_preloaded() -> Self {
152        let mut registry = Self::new_empty();
153        #[cfg(feature = "cbor")]
154        registry.register(CodecKind::CBOR, cbor::Cbor);
155        registry.register(CodecKind::BINCODE, bincode::Bincode);
156        registry.register(CodecKind::JSON, json::Json);
157        registry
158    }
159    pub fn register<C: Codec + 'static>(&mut self, kind: CodecKind, codec: C) {
160        self.registry.insert(kind, Arc::new(codec));
161    }
162    pub fn get(&self, kind: &CodecKind) -> Option<Arc<dyn Codec>> {
163        self.registry.get(kind).cloned()
164    }
165    pub fn pick_preferred_codec(&self, supported: &HashSet<CodecKind>) -> Option<CodecKind> {
166        supported
167            .iter()
168            .find(|kind| self.registry.contains_key(kind))
169            .copied()
170    }
171    pub fn encode(&self, kind: CodecKind, value: &EdgePayload) -> Result<Vec<u8>, CodecError> {
172        self.registry
173            .get(&kind)
174            .ok_or_else(|| CodecError::unregistered_codec(kind))
175            .and_then(|codec| codec.encode(value))
176    }
177    pub fn decode(&self, kind: CodecKind, bytes: &[u8]) -> Result<EdgePayload, CodecError> {
178        self.registry
179            .get(&kind)
180            .ok_or_else(|| CodecError::unregistered_codec(kind))
181            .and_then(|codec| codec.decode(bytes))
182    }
183}