use crate::value::{BitSequence, Composite, Primitive, Value, ValueDef, Variant};
use crate::{Type, TypeId};
use codec::{Compact, Decode};
use scale_info::{
form::PortableForm, Field, PortableRegistry, TypeDefArray, TypeDefBitSequence, TypeDefCompact, TypeDefComposite,
TypeDefPrimitive, TypeDefSequence, TypeDefTuple, TypeDefVariant,
};
type TypeDef = scale_info::TypeDef<PortableForm>;
#[derive(Debug, Clone, thiserror::Error, PartialEq)]
pub enum DecodeValueError {
#[error("{0}")]
CodecError(#[from] codec::Error),
#[error("{0} is expected to be a valid char, but is not")]
InvalidChar(u32),
#[error("Cannot find type with ID {0}")]
TypeIdNotFound(u32),
#[error("Ran out of data during decoding")]
Eof,
#[error("Could not find variant with index {0} in {1:?}")]
VariantNotFound(u8, scale_info::TypeDefVariant<PortableForm>),
#[error("Could not decode compact encoded type into {0:?}")]
CannotDecodeCompactIntoType(Type),
}
pub fn decode_value_by_id<Id: Into<TypeId>>(
data: &mut &[u8],
ty_id: Id,
types: &PortableRegistry,
) -> Result<Value<TypeId>, DecodeValueError> {
let ty_id = ty_id.into();
let ty = types.resolve(ty_id.id()).ok_or_else(|| DecodeValueError::TypeIdNotFound(ty_id.id()))?;
let value = match ty.type_def() {
TypeDef::Composite(inner) => decode_composite_value(data, inner, types).map(ValueDef::Composite),
TypeDef::Sequence(inner) => decode_sequence_value(data, inner, types).map(ValueDef::Composite),
TypeDef::Array(inner) => decode_array_value(data, inner, types).map(ValueDef::Composite),
TypeDef::Tuple(inner) => decode_tuple_value(data, inner, types).map(ValueDef::Composite),
TypeDef::Variant(inner) => decode_variant_value(data, inner, types).map(ValueDef::Variant),
TypeDef::Primitive(inner) => decode_primitive_value(data, inner).map(ValueDef::Primitive),
TypeDef::Compact(inner) => decode_compact_value(data, inner, types),
TypeDef::BitSequence(inner) => decode_bit_sequence_value(data, inner, types).map(ValueDef::BitSequence),
}?;
Ok(Value { value, context: ty_id })
}
fn decode_composite_value(
data: &mut &[u8],
ty: &TypeDefComposite<PortableForm>,
types: &PortableRegistry,
) -> Result<Composite<TypeId>, DecodeValueError> {
decode_fields(data, ty.fields(), types)
}
fn decode_variant_value(
data: &mut &[u8],
ty: &TypeDefVariant<PortableForm>,
types: &PortableRegistry,
) -> Result<Variant<TypeId>, DecodeValueError> {
let index = *data.get(0).ok_or(DecodeValueError::Eof)?;
*data = &data[1..];
let variant = ty
.variants()
.iter()
.find(|v| v.index() == index)
.ok_or_else(|| DecodeValueError::VariantNotFound(index, ty.clone()))?;
let fields = decode_fields(data, variant.fields(), types)?;
Ok(Variant { name: variant.name().clone(), values: fields })
}
fn decode_fields(
data: &mut &[u8],
fields: &[Field<PortableForm>],
types: &PortableRegistry,
) -> Result<Composite<TypeId>, DecodeValueError> {
let are_named = fields.iter().any(|f| f.name().is_some());
let named_field_vals = fields.iter().map(|f| {
let name = f.name().cloned().unwrap_or_default();
decode_value_by_id(data, f.ty(), types).map(|val| (name, val))
});
if are_named {
let vals = named_field_vals.collect::<Result<_, _>>()?;
Ok(Composite::Named(vals))
} else {
let vals = named_field_vals.map(|r| r.map(|(_, v)| v)).collect::<Result<_, _>>()?;
Ok(Composite::Unnamed(vals))
}
}
fn decode_sequence_value(
data: &mut &[u8],
ty: &TypeDefSequence<PortableForm>,
types: &PortableRegistry,
) -> Result<Composite<TypeId>, DecodeValueError> {
let len = Compact::<u64>::decode(data)?;
let values: Vec<_> =
(0..len.0).map(|_| decode_value_by_id(data, ty.type_param(), types)).collect::<Result<_, _>>()?;
Ok(Composite::Unnamed(values))
}
fn decode_array_value(
data: &mut &[u8],
ty: &TypeDefArray<PortableForm>,
types: &PortableRegistry,
) -> Result<Composite<TypeId>, DecodeValueError> {
let values: Vec<_> =
(0..ty.len()).map(|_| decode_value_by_id(data, ty.type_param(), types)).collect::<Result<_, _>>()?;
Ok(Composite::Unnamed(values))
}
fn decode_tuple_value(
data: &mut &[u8],
ty: &TypeDefTuple<PortableForm>,
types: &PortableRegistry,
) -> Result<Composite<TypeId>, DecodeValueError> {
let values: Vec<_> = ty.fields().iter().map(|f| decode_value_by_id(data, f, types)).collect::<Result<_, _>>()?;
Ok(Composite::Unnamed(values))
}
fn decode_primitive_value(data: &mut &[u8], ty: &TypeDefPrimitive) -> Result<Primitive, DecodeValueError> {
let val = match ty {
TypeDefPrimitive::Bool => Primitive::Bool(bool::decode(data)?),
TypeDefPrimitive::Char => {
let val = u32::decode(data)?;
Primitive::Char(char::from_u32(val).ok_or(DecodeValueError::InvalidChar(val))?)
}
TypeDefPrimitive::Str => Primitive::Str(String::decode(data)?),
TypeDefPrimitive::U8 => Primitive::U8(u8::decode(data)?),
TypeDefPrimitive::U16 => Primitive::U16(u16::decode(data)?),
TypeDefPrimitive::U32 => Primitive::U32(u32::decode(data)?),
TypeDefPrimitive::U64 => Primitive::U64(u64::decode(data)?),
TypeDefPrimitive::U128 => Primitive::U128(u128::decode(data)?),
TypeDefPrimitive::U256 => Primitive::U256(<[u8; 32]>::decode(data)?),
TypeDefPrimitive::I8 => Primitive::I8(i8::decode(data)?),
TypeDefPrimitive::I16 => Primitive::I16(i16::decode(data)?),
TypeDefPrimitive::I32 => Primitive::I32(i32::decode(data)?),
TypeDefPrimitive::I64 => Primitive::I64(i64::decode(data)?),
TypeDefPrimitive::I128 => Primitive::I128(i128::decode(data)?),
TypeDefPrimitive::I256 => Primitive::I256(<[u8; 32]>::decode(data)?),
};
Ok(val)
}
fn decode_compact_value(
data: &mut &[u8],
ty: &TypeDefCompact<PortableForm>,
types: &PortableRegistry,
) -> Result<ValueDef<TypeId>, DecodeValueError> {
fn decode_compact(
data: &mut &[u8],
inner: &Type,
types: &PortableRegistry,
) -> Result<ValueDef<TypeId>, DecodeValueError> {
use TypeDefPrimitive::*;
let val = match inner.type_def() {
TypeDef::Primitive(U8) => ValueDef::Primitive(Primitive::U8(Compact::<u8>::decode(data)?.0)),
TypeDef::Primitive(U16) => ValueDef::Primitive(Primitive::U16(Compact::<u16>::decode(data)?.0)),
TypeDef::Primitive(U32) => ValueDef::Primitive(Primitive::U32(Compact::<u32>::decode(data)?.0)),
TypeDef::Primitive(U64) => ValueDef::Primitive(Primitive::U64(Compact::<u64>::decode(data)?.0)),
TypeDef::Primitive(U128) => ValueDef::Primitive(Primitive::U128(Compact::<u128>::decode(data)?.0)),
TypeDef::Composite(composite) => {
if composite.fields().len() != 1 {
return Err(DecodeValueError::CannotDecodeCompactIntoType(inner.clone()));
}
let field = &composite.fields()[0];
let field_type_id = field.ty().id();
let inner_ty = types.resolve(field_type_id).ok_or(DecodeValueError::TypeIdNotFound(field_type_id))?;
let inner_value = Value { value: decode_compact(data, inner_ty, types)?, context: field.ty().into() };
let composite = match field.name() {
Some(name) => Composite::Named(vec![(name.clone(), inner_value)]),
None => Composite::Unnamed(vec![inner_value]),
};
ValueDef::Composite(composite)
}
_cannot_decode_from => return Err(DecodeValueError::CannotDecodeCompactIntoType(inner.clone())),
};
Ok(val)
}
let inner =
types.resolve(ty.type_param().id()).ok_or_else(|| DecodeValueError::TypeIdNotFound(ty.type_param().id()))?;
decode_compact(data, inner, types)
}
fn decode_bit_sequence_value(
data: &mut &[u8],
_ty: &TypeDefBitSequence<PortableForm>,
_types: &PortableRegistry,
) -> Result<BitSequence, DecodeValueError> {
let bit_vec: BitSequence = Decode::decode(data)?;
Ok(bit_vec)
}
#[cfg(test)]
mod test {
use super::*;
use codec::Encode;
fn make_type<T: scale_info::TypeInfo + 'static>() -> (TypeId, PortableRegistry) {
let m = scale_info::MetaType::new::<T>();
let mut types = scale_info::Registry::new();
let id = types.register_type(&m);
let portable_registry: PortableRegistry = types.into();
(id.into(), portable_registry)
}
fn encode_decode_check<T: Encode + scale_info::TypeInfo + 'static>(val: T, exp: Value<()>) {
encode_decode_check_explicit_info::<T, _>(val, exp)
}
fn encode_decode_check_explicit_info<Ty: scale_info::TypeInfo + 'static, T: Encode>(val: T, ex: Value<()>) {
let encoded = val.encode();
let encoded = &mut &*encoded;
let (id, portable_registry) = make_type::<Ty>();
let val = decode_value_by_id(encoded, id, &portable_registry).expect("decoding failed");
assert_eq!(val.without_context(), ex, "decoded value does not look like what we expected");
assert_eq!(encoded.len(), 0, "decoding did not consume all of the encoded bytes");
}
#[test]
fn decode_primitives() {
encode_decode_check(true, Value::bool(true));
encode_decode_check(false, Value::bool(false));
encode_decode_check_explicit_info::<char, _>('a' as u32, Value::char('a'));
encode_decode_check("hello", Value::str("hello".into()));
encode_decode_check(
"hello".to_string(), Value::str("hello".into()),
);
encode_decode_check(123u8, Value::u8(123));
encode_decode_check(123u16, Value::u16(123));
encode_decode_check(123u32, Value::u32(123));
encode_decode_check(123u64, Value::u64(123));
encode_decode_check(123i8, Value::i8(123));
encode_decode_check(123i16, Value::i16(123));
encode_decode_check(123i32, Value::i32(123));
encode_decode_check(123i64, Value::i64(123));
}
#[test]
fn decode_compact_primitives() {
encode_decode_check(Compact(123u8), Value::u8(123));
encode_decode_check(Compact(123u16), Value::u16(123));
encode_decode_check(Compact(123u32), Value::u32(123));
encode_decode_check(Compact(123u64), Value::u64(123));
encode_decode_check(Compact(123u128), Value::u128(123));
}
#[test]
fn decode_compact_named_wrapper_struct() {
#[derive(Encode, scale_info::TypeInfo)]
struct MyWrapper {
inner: u32,
}
impl From<Compact<MyWrapper>> for MyWrapper {
fn from(val: Compact<MyWrapper>) -> MyWrapper {
val.0
}
}
impl codec::CompactAs for MyWrapper {
type As = u32;
fn encode_as(&self) -> &Self::As {
&self.inner
}
fn decode_from(inner: Self::As) -> Result<Self, codec::Error> {
Ok(MyWrapper { inner })
}
}
encode_decode_check(
Compact(MyWrapper { inner: 123 }),
Value::named_composite(vec![("inner".to_string(), Value::u32(123))]),
);
}
#[test]
fn decode_compact_unnamed_wrapper_struct() {
#[derive(Encode, scale_info::TypeInfo)]
struct MyWrapper(u32);
impl From<Compact<MyWrapper>> for MyWrapper {
fn from(val: Compact<MyWrapper>) -> MyWrapper {
val.0
}
}
impl codec::CompactAs for MyWrapper {
type As = u32;
fn encode_as(&self) -> &Self::As {
&self.0
}
fn decode_from(inner: Self::As) -> Result<Self, codec::Error> {
Ok(MyWrapper(inner))
}
}
encode_decode_check(Compact(MyWrapper(123)), Value::unnamed_composite(vec![Value::u32(123)]));
}
#[test]
fn decode_sequence_array_tuple_types() {
encode_decode_check(
vec![1i32, 2, 3],
Value::unnamed_composite(vec![Value::i32(1), Value::i32(2), Value::i32(3)]),
);
encode_decode_check(
[1i32, 2, 3], Value::unnamed_composite(vec![Value::i32(1), Value::i32(2), Value::i32(3)]),
);
encode_decode_check(
(1i32, true, 123456u128),
Value::unnamed_composite(vec![Value::i32(1), Value::bool(true), Value::u128(123456)]),
);
}
#[test]
fn decode_variant_types() {
#[derive(Encode, scale_info::TypeInfo)]
enum MyEnum {
Foo(bool),
Bar { hi: String, other: u128 },
}
encode_decode_check(
MyEnum::Foo(true),
Value::variant("Foo".to_string(), Composite::Unnamed(vec![Value::bool(true)])),
);
encode_decode_check(
MyEnum::Bar { hi: "hello".to_string(), other: 123 },
Value::variant(
"Bar".to_string(),
Composite::Named(vec![
("hi".to_string(), Value::str("hello".to_string())),
("other".to_string(), Value::u128(123)),
]),
),
);
}
#[test]
fn decode_composite_types() {
#[derive(Encode, scale_info::TypeInfo)]
struct Unnamed(bool, String, Vec<u8>);
#[derive(Encode, scale_info::TypeInfo)]
struct Named {
is_valid: bool,
name: String,
bytes: Vec<u8>,
}
encode_decode_check(
Unnamed(true, "James".into(), vec![1, 2, 3]),
Value::unnamed_composite(vec![
Value::bool(true),
Value::str("James".to_string()),
Value::unnamed_composite(vec![Value::u8(1), Value::u8(2), Value::u8(3)]),
]),
);
encode_decode_check(
Named { is_valid: true, name: "James".into(), bytes: vec![1, 2, 3] },
Value::named_composite(vec![
("is_valid".into(), Value::bool(true)),
("name".into(), Value::str("James".to_string())),
("bytes".into(), Value::unnamed_composite(vec![Value::u8(1), Value::u8(2), Value::u8(3)])),
]),
);
}
#[test]
fn decode_bit_sequence() {
use bitvec::{bitvec, order::Lsb0};
encode_decode_check(
bitvec![Lsb0, u8; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![Lsb0, u8; 0, 1, 1, 0, 1, 0]),
);
}
}