use crate::prelude::*;
use crate::value_type::{Composite, Primitive, Value, ValueDef, Variant};
use codec::{Compact, Encode};
use scale_bits::Bits;
use scale_decode::TypeResolver;
use scale_encode::error::ErrorKind;
use scale_encode::{error::Kind, EncodeAsFields, EncodeAsType, Error};
use scale_encode::{
Composite as EncodeComposite, CompositeField, FieldIter, Variant as EncodeVariant,
};
impl<T> EncodeAsType for Value<T> {
fn encode_as_type_to<R: TypeResolver>(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
match &self.value {
ValueDef::Composite(val) => encode_composite(val, type_id, types, out),
ValueDef::Variant(val) => encode_variant(val, type_id, types, out),
ValueDef::Primitive(val) => encode_primitive(val, type_id, types, out),
ValueDef::BitSequence(val) => encode_bitsequence(val, type_id, types, out),
}
}
}
impl<T> EncodeAsFields for Value<T> {
fn encode_as_fields_to<R: TypeResolver>(
&self,
fields: &mut dyn FieldIter<'_, R::TypeId>,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
match &self.value {
ValueDef::Composite(composite) => composite.encode_as_fields_to(fields, types, out),
_ => Err(Error::custom_str("Cannot encode non-composite Value shape into fields")),
}
}
}
impl<T> EncodeAsFields for Composite<T> {
fn encode_as_fields_to<R: TypeResolver>(
&self,
fields: &mut dyn FieldIter<'_, R::TypeId>,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
match self {
Composite::Named(vals) => {
let keyvals =
vals.iter().map(|(key, val)| (Some(&**key), CompositeField::new(val)));
EncodeComposite::new(keyvals).encode_composite_fields_to(fields, types, out)
}
Composite::Unnamed(vals) => {
let vals = vals.iter().map(|val| (None, CompositeField::new(val)));
EncodeComposite::new(vals).encode_composite_fields_to(fields, types, out)
}
}
}
}
fn encode_composite<T, R: TypeResolver>(
value: &Composite<T>,
mut type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
fn do_encode_composite<T, R: TypeResolver>(
value: &Composite<T>,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
let ctx = (type_id.clone(), out);
let visit_composite_or_tuple =
|(type_id, out): (R::TypeId, &mut Vec<u8>)| -> Result<(), Error> {
match value {
Composite::Named(vals) => {
let keyvals =
vals.iter().map(|(key, val)| (Some(&**key), CompositeField::new(val)));
EncodeComposite::new(keyvals)
.encode_composite_as_type_to(type_id, types, out)
}
Composite::Unnamed(vals) => {
let vals = vals.iter().map(|val| (None, CompositeField::new(val)));
EncodeComposite::new(vals).encode_composite_as_type_to(type_id, types, out)
}
}
};
let visitor = scale_type_resolver::visitor::new::<
'_,
(R::TypeId, &mut Vec<u8>),
R::TypeId,
Result<(), Error>,
_,
>(ctx, |(type_id, out), _| {
let mut values = value.values();
match (values.next(), values.next()) {
(Some(value), None) => value.encode_as_type_to(type_id, types, out),
_ => Err(Error::new(ErrorKind::WrongShape {
actual: Kind::Tuple,
expected_id: format!("{:?}", type_id),
})),
}
})
.visit_composite(|ctx, _, _| visit_composite_or_tuple(ctx))
.visit_tuple(|ctx, _| visit_composite_or_tuple(ctx))
.visit_sequence(|(_, out), _path, type_id| {
Compact(value.len() as u32).encode_to(out);
match value {
Composite::Named(named_vals) => {
for (name, val) in named_vals {
val.encode_as_type_to(type_id.clone(), types, out)
.map_err(|e| e.at_field(name.to_string()))?;
}
}
Composite::Unnamed(vals) => {
for (idx, val) in vals.iter().enumerate() {
val.encode_as_type_to(type_id.clone(), types, out)
.map_err(|e| e.at_idx(idx))?;
}
}
}
Ok(())
})
.visit_array(|(_, out), type_id, array_len| {
if value.len() != array_len {
return Err(Error::new(ErrorKind::WrongLength {
actual_len: value.len(),
expected_len: array_len,
}));
}
for (idx, val) in value.values().enumerate() {
val.encode_as_type_to(type_id.clone(), types, out).map_err(|e| e.at_idx(idx))?;
}
Ok(())
})
.visit_bit_sequence(|(_, out), store, order| {
let format = scale_bits::Format { store, order };
encode_vals_to_bitsequence(value.values(), out, format)
});
match types.resolve_type(type_id, visitor) {
Ok(Ok(())) => Ok(()),
Ok(Err(err)) => Err(err),
Err(err) => Err(Error::new(ErrorKind::TypeResolvingError(format!("{:?}", err)))),
}
}
let original_error = {
let mut temp_out = Vec::new();
match do_encode_composite(value, type_id.clone(), types, &mut temp_out) {
Ok(()) => {
out.extend_from_slice(&temp_out);
return Ok(());
}
Err(e) => e,
}
};
{
let (inner_type_id, inner_is_different) =
find_single_entry_with_same_repr::<R>(type_id.clone(), types);
if inner_is_different {
let mut temp_out = Vec::new();
if let Ok(()) = do_encode_composite(value, inner_type_id.clone(), types, &mut temp_out)
{
out.extend_from_slice(&temp_out);
return Ok(());
}
type_id = inner_type_id;
}
}
if let Some(value) = get_only_value_from_composite(value) {
let mut temp_out = Vec::new();
if let Ok(()) = value.encode_as_type_to(type_id.clone(), types, &mut temp_out) {
out.extend_from_slice(&temp_out);
return Ok(());
}
}
Err(original_error)
}
fn find_single_entry_with_same_repr<R: TypeResolver>(
type_id: R::TypeId,
types: &R,
) -> (R::TypeId, bool) {
fn do_find<R: TypeResolver>(
type_id: R::TypeId,
types: &R,
type_id_has_changed: bool,
) -> (R::TypeId, bool) {
let ctx = (type_id.clone(), type_id_has_changed);
let visitor = scale_type_resolver::visitor::new(ctx.clone(), |ctx, _| ctx)
.visit_tuple(|return_unchanged, fields| {
if fields.len() == 1 {
let ty = fields.next().expect("has 1 item; qed;");
do_find(ty, types, true)
} else {
return_unchanged
}
})
.visit_composite(|return_unchanged, _, fields| {
if fields.len() == 1 {
let ty = fields.next().expect("has 1 item; qed;").id;
do_find(ty, types, true)
} else {
return_unchanged
}
})
.visit_array(
|return_unchanged, ty_id, len| {
if len == 1 {
do_find(ty_id, types, true)
} else {
return_unchanged
}
},
);
types.resolve_type(type_id, visitor).unwrap_or(ctx)
}
do_find(type_id, types, false)
}
fn get_only_value_from_composite<T>(value: &'_ Composite<T>) -> Option<&'_ Value<T>> {
let mut values = value.values();
match (values.next(), values.next()) {
(Some(value), None) => Some(value),
_ => None,
}
}
fn encode_vals_to_bitsequence<'a, T: 'a>(
vals: impl ExactSizeIterator<Item = &'a Value<T>>,
out: &mut Vec<u8>,
format: scale_bits::Format,
) -> Result<(), Error> {
let mut bools = Vec::with_capacity(vals.len());
for (idx, value) in vals.enumerate() {
if let Some(v) = value.as_bool() {
bools.push(v);
} else if let Some(v) = value.as_u128() {
if v == 0 || v == 1 {
bools.push(v == 1)
} else {
return Err(Error::custom_str(
"Cannot encode non-boolean/0/1 value into a bit sequence entry",
)
.at_idx(idx));
}
} else if let Some(v) = value.as_i128() {
if v == 0 || v == 1 {
bools.push(v == 1)
} else {
return Err(Error::custom_str(
"Cannot encode non-boolean/0/1 value into a bit sequence entry",
)
.at_idx(idx));
}
} else {
return Err(Error::custom_str(
"Cannot encode non-boolean/0/1 value into a bit sequence entry",
)
.at_idx(idx));
}
}
scale_bits::encode_using_format_to(bools.into_iter(), format, out);
Ok(())
}
fn encode_variant<T, R: TypeResolver>(
value: &Variant<T>,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
match &value.values {
Composite::Named(vals) => {
let keyvals = vals.iter().map(|(key, val)| (Some(&**key), CompositeField::new(val)));
EncodeVariant { name: &value.name, fields: EncodeComposite::new(keyvals) }
.encode_variant_as_type_to(type_id, types, out)
}
Composite::Unnamed(vals) => {
let vals = vals.iter().map(|val| (None, CompositeField::new(val)));
EncodeVariant { name: &value.name, fields: EncodeComposite::new(vals) }
.encode_variant_as_type_to(type_id, types, out)
}
}
}
fn encode_primitive<R: TypeResolver>(
value: &Primitive,
type_id: R::TypeId,
types: &R,
bytes: &mut Vec<u8>,
) -> Result<(), Error> {
match value {
Primitive::Bool(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::Char(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::String(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::U128(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::I128(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::U256(val) => val.encode_as_type_to(type_id, types, bytes),
Primitive::I256(val) => val.encode_as_type_to(type_id, types, bytes),
}
}
fn encode_bitsequence<R: TypeResolver>(
value: &Bits,
type_id: R::TypeId,
types: &R,
bytes: &mut Vec<u8>,
) -> Result<(), Error> {
value.encode_as_type_to(type_id, types, bytes)
}
#[cfg(test)]
mod test {
use super::*;
use crate::value;
use codec::{Compact, Encode};
use core::time::Duration;
use scale_info::PortableRegistry;
#[cfg(feature = "std")]
fn panic_after<T, F>(d: Duration, f: F) -> T
where
T: Send + 'static,
F: FnOnce() -> T,
F: Send + 'static,
{
use std::{sync::mpsc, thread};
let (done_tx, done_rx) = mpsc::channel();
let handle = thread::spawn(move || {
let val = f();
done_tx.send(()).expect("Unable to send completion signal");
val
});
match done_rx.recv_timeout(d) {
Ok(_) => handle.join().expect("Thread panicked"),
Err(_) => panic!("Thread took too long"),
}
}
fn make_type<T: scale_info::TypeInfo + 'static>() -> (u32, 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.id, portable_registry)
}
fn assert_can_encode_to_type<T: Encode + scale_info::TypeInfo + 'static>(
value: Value<()>,
ty: T,
) {
let expected = ty.encode();
let mut buf = Vec::new();
let (ty_id, types) = make_type::<T>();
value.encode_as_type_to(ty_id, &types, &mut buf).expect("error encoding value as type");
assert_eq!(expected, buf);
}
#[test]
fn can_encode_basic_primitive_values() {
assert_can_encode_to_type(Value::i128(123), 123i8);
assert_can_encode_to_type(Value::i128(123), 123i16);
assert_can_encode_to_type(Value::i128(123), 123i32);
assert_can_encode_to_type(Value::i128(123), 123i64);
assert_can_encode_to_type(Value::i128(123), 123i128);
assert_can_encode_to_type(Value::u128(123), 123u8);
assert_can_encode_to_type(Value::u128(123), 123u16);
assert_can_encode_to_type(Value::u128(123), 123u32);
assert_can_encode_to_type(Value::u128(123), 123u64);
assert_can_encode_to_type(Value::u128(123), 123u128);
assert_can_encode_to_type(Value::bool(true), true);
assert_can_encode_to_type(Value::bool(false), false);
assert_can_encode_to_type(Value::string("Hello"), "Hello");
assert_can_encode_to_type(Value::string("Hello"), "Hello".to_string());
}
#[test]
fn chars_encoded_like_numbers() {
assert_can_encode_to_type(Value::char('j'), 'j' as u32);
assert_can_encode_to_type(Value::char('j'), b'j');
}
#[test]
fn can_encode_primitive_arrs_to_array() {
use crate::Primitive;
assert_can_encode_to_type(Value::primitive(Primitive::U256([12u8; 32])), [12u8; 32]);
assert_can_encode_to_type(Value::primitive(Primitive::I256([12u8; 32])), [12u8; 32]);
}
#[test]
fn can_encode_primitive_arrs_to_vecs() {
use crate::Primitive;
assert_can_encode_to_type(Value::primitive(Primitive::U256([12u8; 32])), vec![12u8; 32]);
assert_can_encode_to_type(Value::primitive(Primitive::I256([12u8; 32])), vec![12u8; 32]);
}
#[test]
fn can_encode_arrays() {
let value = Value::unnamed_composite(vec![
Value::u128(1),
Value::u128(2),
Value::u128(3),
Value::u128(4),
]);
assert_can_encode_to_type(value, [1u16, 2, 3, 4]);
}
#[test]
fn can_encode_variants() {
#[derive(Encode, scale_info::TypeInfo)]
enum Foo {
Named { hello: String, foo: bool },
Unnamed(u64, Vec<bool>),
}
let named_value = value!(Named { foo: true, hello: "world" });
assert_can_encode_to_type(named_value, Foo::Named { hello: "world".into(), foo: true });
let unnamed_value = value!(Unnamed(123u64, (true, false, true)));
assert_can_encode_to_type(unnamed_value, Foo::Unnamed(123, vec![true, false, true]));
}
#[test]
fn can_encode_vec_tuples() {
let vec_tuple = value!(((20u8, 30u16)));
assert_can_encode_to_type(vec_tuple, vec![(20u8, 30u16)]);
}
#[test]
fn can_encode_structs() {
#[derive(Encode, scale_info::TypeInfo)]
struct Foo {
hello: String,
foo: bool,
}
let named_value = value!({foo: true, hello: "world"});
assert_can_encode_to_type(named_value, Foo { hello: "world".into(), foo: true });
}
#[test]
fn can_encode_tuples_from_named_composite() {
let named_value = value!({hello: "world", foo: true});
assert_can_encode_to_type(named_value, ("world", true));
}
#[test]
fn can_encode_tuples_from_unnamed_composite() {
let unnamed_value = value!(("world", true));
assert_can_encode_to_type(unnamed_value, ("world", true));
}
#[test]
fn can_encode_unnamed_composite_to_named_struct() {
#[derive(Encode, scale_info::TypeInfo)]
struct Foo {
hello: String,
foo: bool,
}
let unnamed_value = value!(("world", true));
assert_can_encode_to_type(unnamed_value, Foo { hello: "world".to_string(), foo: true });
}
#[test]
fn can_encode_bitvecs() {
use scale_bits::bits;
assert_can_encode_to_type(
Value::bit_sequence(bits![0, 1, 1, 0, 0, 1]),
bits![0, 1, 1, 0, 0, 1],
);
assert_can_encode_to_type(value!((false)), bits![0]);
assert_can_encode_to_type(
value!((false, true, true, false, false, true)),
bits![0, 1, 1, 0, 0, 1],
);
assert_can_encode_to_type(value!((0u8, 1u8, 1u8, 0u8, 0u8, 1u8)), bits![0, 1, 1, 0, 0, 1]);
assert_can_encode_to_type(value!((0, 1, 1, 0, 0, 1)), bits![0, 1, 1, 0, 0, 1]);
}
#[test]
fn can_encode_to_compact_types() {
assert_can_encode_to_type(Value::u128(123), Compact(123u64));
assert_can_encode_to_type(Value::u128(123), Compact(123u64));
assert_can_encode_to_type(Value::u128(123), Compact(123u64));
assert_can_encode_to_type(Value::u128(123), Compact(123u64));
assert_can_encode_to_type(value!((123)), Compact(123u64));
assert_can_encode_to_type(value!(({foo: 123u64})), Compact(123u64));
}
#[test]
fn can_encode_skipping_struct_newtype_wrappers() {
#[derive(Encode, scale_info::TypeInfo)]
struct Foo {
inner: u32,
}
assert_can_encode_to_type(Value::u128(32), Foo { inner: 32 });
#[derive(Encode, scale_info::TypeInfo)]
struct Bar(Foo);
assert_can_encode_to_type(Value::u128(32), Bar(Foo { inner: 32 }));
assert_can_encode_to_type(value!((32u8)), Bar(Foo { inner: 32 }));
#[derive(Encode, scale_info::TypeInfo)]
struct SomeBytes(Vec<u8>);
assert_can_encode_to_type(
Value::from_bytes([1, 2, 3, 4, 5]),
SomeBytes(vec![1, 2, 3, 4, 5]),
);
assert_can_encode_to_type(Value::from_bytes([1]), SomeBytes(vec![1]));
}
#[test]
fn can_encode_skipping_value_newtype_wrappers() {
#[derive(Encode, scale_info::TypeInfo)]
struct Foo {
inner: u32,
}
assert_can_encode_to_type(
Value::unnamed_composite(vec![Value::u128(32)]),
Foo { inner: 32 },
);
assert_can_encode_to_type(
Value::unnamed_composite(vec![Value::unnamed_composite(vec![Value::u128(32)])]),
Foo { inner: 32 },
);
assert_can_encode_to_type(
Value::unnamed_composite(vec![Value::unnamed_composite(vec![
Value::unnamed_composite(vec![Value::u128(32)]),
])]),
Foo { inner: 32 },
);
}
#[test]
#[cfg(feature = "std")]
fn encoding_shouldnt_take_forever() {
panic_after(Duration::from_millis(100), || {
#[derive(scale_info::TypeInfo, codec::Encode)]
struct A(bool);
let mut buf = Vec::new();
let (ty_id, types) = make_type::<A>();
let value = Value::unnamed_composite(vec![Value::from_bytes(0u32.to_le_bytes())]);
value
.encode_as_type_to(ty_id, &types, &mut buf)
.expect_err("We tried encoding a Value of the wrong shape so this should fail");
})
}
}