use core::any::{TypeId, type_name};
pub trait ValueDecode: 'static + Sized {
const DECODE: &'static ValueDecodeSpec;
#[doc(hidden)]
const __DECODE_REF: ValueDecodeRef = ValueDecodeRef {
describe: describe::<Self>,
};
}
#[derive(Clone, Copy, Debug)]
pub struct ValueDecodeRef {
pub describe: fn() -> ValueDecodeType,
}
impl ValueDecodeRef {
pub const fn of<T: ValueDecode>() -> Self {
Self {
describe: describe::<T>,
}
}
}
fn describe<T: ValueDecode>() -> ValueDecodeType {
ValueDecodeType {
type_id: TypeId::of::<T>(),
type_name: type_name::<T>(),
spec: T::DECODE,
}
}
#[derive(Clone, Copy, Debug)]
pub struct ValueDecodeType {
pub type_id: TypeId,
pub type_name: &'static str,
pub spec: &'static ValueDecodeSpec,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Scalar {
Bool,
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Char,
}
#[derive(Clone, Copy, Debug)]
pub enum FieldSelector {
Named(&'static str),
Index {
index: usize,
declared_fields: usize,
},
}
#[derive(Clone, Copy, Debug)]
pub struct ValueDecodeField {
pub selector: FieldSelector,
pub declaration_index: usize,
pub value: ValueDecodeRef,
}
#[derive(Clone, Copy, Debug)]
pub enum RecordShape {
Unit,
Tuple,
Newtype,
Struct,
}
#[derive(Clone, Copy, Debug)]
pub struct ValueDecodeVariant {
pub tag: u32,
pub name: &'static str,
pub shape: RecordShape,
pub fields: &'static [ValueDecodeField],
}
#[derive(Clone, Copy, Debug)]
pub enum ValueDecodeSpec {
Unit,
Scalar(Scalar),
String,
Bytes,
Record {
shape: RecordShape,
fields: &'static [ValueDecodeField],
},
Array {
element: ValueDecodeRef,
len: usize,
},
Sequence {
element: ValueDecodeRef,
count: Scalar,
capacity: Option<usize>,
},
Map {
key: ValueDecodeRef,
value: ValueDecodeRef,
},
Option(ValueDecodeRef),
Delegate(ValueDecodeRef),
Enum {
tag: Scalar,
variants: &'static [ValueDecodeVariant],
},
}
macro_rules! scalars {
($($ty:ty => $scalar:ident),* $(,)?) => {$(
impl ValueDecode for $ty { const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Scalar(Scalar::$scalar); }
)*};
}
scalars!(bool=>Bool, u8=>U8, u16=>U16, u32=>U32, u64=>U64, u128=>U128,
i8=>I8, i16=>I16, i32=>I32, i64=>I64, i128=>I128, f32=>F32, f64=>F64, char=>Char,
usize=>U64, isize=>I64);
impl ValueDecode for () {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Unit;
}
impl<T: 'static> ValueDecode for core::marker::PhantomData<T> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Unit;
}
impl<T: ValueDecode, const N: usize> ValueDecode for [T; N] {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Array {
element: ValueDecodeRef::of::<T>(),
len: N,
};
}
impl<T: ValueDecode> ValueDecode for Option<T> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Option(ValueDecodeRef::of::<T>());
}
impl<T: ValueDecode> ValueDecode for &'static T {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Delegate(ValueDecodeRef::of::<T>());
}
impl ValueDecode for &'static str {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::String;
}
impl<T: ValueDecode> ValueDecode for &'static [T] {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence {
element: ValueDecodeRef::of::<T>(),
count: Scalar::U64,
capacity: None,
};
}
#[cfg(feature = "alloc")]
mod allocated {
use super::*;
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet, VecDeque},
rc::Rc,
string::String,
sync::Arc,
vec::Vec,
};
impl ValueDecode for String {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::String;
}
macro_rules! sequence { ($($container:ident),*) => {$(
impl<T: ValueDecode> ValueDecode for $container<T> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence { element: ValueDecodeRef::of::<T>(), count: Scalar::U64, capacity: None };
}
)*}; }
sequence!(Vec, VecDeque, BTreeSet);
impl<K: ValueDecode, V: ValueDecode> ValueDecode for BTreeMap<K, V> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Map {
key: ValueDecodeRef::of::<K>(),
value: ValueDecodeRef::of::<V>(),
};
}
macro_rules! delegate { ($($container:ident),*) => {$(
impl<T: ValueDecode> ValueDecode for $container<T> { const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Delegate(ValueDecodeRef::of::<T>()); }
)*}; }
delegate!(Box, Rc, Arc);
}
macro_rules! tuple_len { ($($ty:ident),+) => { [$(stringify!($ty)),+].len() }; }
macro_rules! tuples {
($($ty:ident:$index:tt),+) => {
impl<$($ty: ValueDecode),+> ValueDecode for ($($ty,)+) {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Record { shape: RecordShape::Tuple, fields: { const LEN: usize = tuple_len!($($ty),+); &[$(
ValueDecodeField { selector: FieldSelector::Index { index: $index, declared_fields: LEN }, declaration_index: $index, value: ValueDecodeRef::of::<$ty>() }
),+] } };
}
};
}
tuples!(A:0);
tuples!(A:0,B:1);
tuples!(A:0,B:1,C:2);
tuples!(A:0,B:1,C:2,D:3);
tuples!(A:0,B:1,C:2,D:3,E:4);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10);
tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11);
#[cfg(feature = "std")]
mod standard {
use super::*;
use std::collections::{HashMap, HashSet};
impl<K: ValueDecode, V: ValueDecode, S: 'static> ValueDecode for HashMap<K, V, S> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Map {
key: ValueDecodeRef::of::<K>(),
value: ValueDecodeRef::of::<V>(),
};
}
impl<T: ValueDecode, S: 'static> ValueDecode for HashSet<T, S> {
const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence {
element: ValueDecodeRef::of::<T>(),
count: Scalar::U64,
capacity: None,
};
}
}