1use std::any::{Any, TypeId};
8use std::collections::HashMap;
9use std::sync::OnceLock;
10
11use serde::{de::DeserializeOwned, Serialize};
12
13use crate::types::{TypeNode, TYPE_ANY};
14
15#[derive(Debug)]
19pub enum SlotValueError {
20 EncodeFailed(Box<dyn std::error::Error + Send + Sync>),
23 DecodeFailed(Box<dyn std::error::Error + Send + Sync>),
26 UnknownTypeHash(u64),
29}
30
31impl std::fmt::Display for SlotValueError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 Self::EncodeFailed(err) => write!(f, "SlotValue::to_wire_bytes failed: {err}"),
35 Self::DecodeFailed(err) => write!(f, "SlotValue decode failed: {err}"),
36 Self::UnknownTypeHash(hash) => write!(
37 f,
38 "SlotValue decode: no registered decoder for type_hash {hash:#018x}",
39 ),
40 }
41 }
42}
43
44impl std::error::Error for SlotValueError {
45 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46 match self {
47 Self::EncodeFailed(err) => Some(err.as_ref()),
48 Self::DecodeFailed(err) => Some(err.as_ref()),
49 Self::UnknownTypeHash(_) => None,
50 }
51 }
52}
53
54pub trait SlotValue: Any + Send + Sync {
59 fn as_any(&self) -> &dyn Any;
61
62 fn into_any_boxed(self: Box<Self>) -> Box<dyn Any + Send + Sync>;
66
67 fn clone_boxed(&self) -> Box<dyn SlotValue>;
69
70 fn to_wire_bytes(&self) -> Result<Vec<u8>, SlotValueError>;
73
74 fn type_hash(&self) -> u64;
78
79 fn runtime_type(&self) -> &'static TypeNode {
84 let tid = self.as_any().type_id();
85 runtime_type_registry()
86 .get(&tid)
87 .copied()
88 .unwrap_or(&TYPE_ANY)
89 }
90
91 fn charged_bytes(&self) -> usize {
97 let any = self.as_any();
98 let tid = any.type_id();
99 match charged_bytes_registry().get(&tid) {
100 Some(f) => f(any),
101 None => 0,
102 }
103 }
104}
105
106pub struct RuntimeTypeBinding {
109 pub type_id_fn: fn() -> TypeId,
111 pub type_node: &'static TypeNode,
113}
114
115inventory::collect!(RuntimeTypeBinding);
116
117pub fn runtime_type_registry() -> &'static HashMap<TypeId, &'static TypeNode> {
120 static REG: OnceLock<HashMap<TypeId, &'static TypeNode>> = OnceLock::new();
121 REG.get_or_init(|| {
122 let mut m: HashMap<TypeId, &'static TypeNode> = HashMap::new();
123 for binding in inventory::iter::<RuntimeTypeBinding> {
124 m.insert((binding.type_id_fn)(), binding.type_node);
125 }
126 m
127 })
128}
129
130pub type ChargedBytesFn = fn(&dyn Any) -> usize;
133
134pub struct ChargedBytesBinding {
137 pub type_id_fn: fn() -> TypeId,
139 pub resolve_fn: ChargedBytesFn,
141}
142
143inventory::collect!(ChargedBytesBinding);
144
145pub fn charged_bytes_registry() -> &'static HashMap<TypeId, ChargedBytesFn> {
148 static REG: OnceLock<HashMap<TypeId, ChargedBytesFn>> = OnceLock::new();
149 REG.get_or_init(|| {
150 let mut m: HashMap<TypeId, ChargedBytesFn> = HashMap::new();
151 for binding in inventory::iter::<ChargedBytesBinding> {
152 m.insert((binding.type_id_fn)(), binding.resolve_fn);
153 }
154 m
155 })
156}
157
158#[macro_export]
164macro_rules! register_charged_bytes {
165 ($t:ty, $resolve:expr) => {
166 $crate::inventory::submit! {
167 $crate::slot_value::ChargedBytesBinding {
168 type_id_fn: || ::std::any::TypeId::of::<$t>(),
169 resolve_fn: |any| {
170 let resolve: fn(&$t) -> usize = $resolve;
171 match any.downcast_ref::<$t>() {
172 Some(v) => resolve(v),
173 None => 0,
174 }
175 },
176 }
177 }
178 };
179}
180
181pub type WireDecodeFn = fn(&[u8]) -> Result<Box<dyn SlotValue>, SlotValueError>;
183
184pub struct WireDecoderBinding {
187 pub type_hash_fn: fn() -> u64,
189 pub decode_fn: WireDecodeFn,
191}
192
193inventory::collect!(WireDecoderBinding);
194
195pub fn wire_decoder_registry() -> &'static HashMap<u64, WireDecodeFn> {
198 static REG: OnceLock<HashMap<u64, WireDecodeFn>> = OnceLock::new();
199 REG.get_or_init(|| {
200 let mut m: HashMap<u64, WireDecodeFn> = HashMap::new();
201 for binding in inventory::iter::<WireDecoderBinding> {
202 m.insert((binding.type_hash_fn)(), binding.decode_fn);
203 }
204 m
205 })
206}
207
208#[macro_export]
219macro_rules! register_type_node {
220 ($t:ty, $node:expr) => {
221 $crate::inventory::submit! {
222 $crate::slot_value::RuntimeTypeBinding {
223 type_id_fn: || ::std::any::TypeId::of::<$t>(),
224 type_node: $node,
225 }
226 }
227 $crate::inventory::submit! {
228 $crate::slot_value::WireDecoderBinding {
229 type_hash_fn: || $crate::slot_value::type_hash_of::<$t>(),
230 decode_fn: |bytes| {
231 $crate::bincode::deserialize::<$t>(bytes)
232 .map(|v| Box::new(v) as Box<dyn $crate::slot_value::SlotValue>)
233 .map_err(|e| $crate::slot_value::SlotValueError::DecodeFailed(Box::new(e)))
234 },
235 }
236 }
237 };
238}
239
240impl<T> SlotValue for T
243where
244 T: Any + Send + Sync + Clone + Serialize + DeserializeOwned,
245{
246 fn as_any(&self) -> &dyn Any {
247 self
248 }
249
250 fn into_any_boxed(self: Box<Self>) -> Box<dyn Any + Send + Sync> {
251 self
252 }
253
254 fn clone_boxed(&self) -> Box<dyn SlotValue> {
255 Box::new(self.clone())
256 }
257
258 fn to_wire_bytes(&self) -> Result<Vec<u8>, SlotValueError> {
259 bincode::serialize(self).map_err(|e| SlotValueError::EncodeFailed(Box::new(e)))
260 }
261
262 fn type_hash(&self) -> u64 {
263 type_hash_of::<T>()
264 }
265}
266
267#[inline]
271pub fn type_hash_of<T: ?Sized + 'static>() -> u64 {
272 fnv1a_64(std::any::type_name::<T>().as_bytes())
273}
274
275#[inline]
278pub const fn fnv1a_64(bytes: &[u8]) -> u64 {
279 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
280 let mut i = 0;
281 while i < bytes.len() {
282 h ^= bytes[i] as u64;
283 h = h.wrapping_mul(0x0000_0100_0000_01b3);
284 i += 1;
285 }
286 h
287}