From 9cc1ebf6dd9722f36a1765a41e669826f5e390a9 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 9 Nov 2023 15:07:25 +0000 Subject: [PATCH 1/8] hard-code Error type that IntoVisitor expects --- .gitignore | 1 + scale-decode/src/lib.rs | 14 +++++--------- scale-decode/src/visitor/mod.rs | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 4fffb2f..1d14bc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +.DS_Store \ No newline at end of file diff --git a/scale-decode/src/lib.rs b/scale-decode/src/lib.rs index 249b15b..549da48 100644 --- a/scale-decode/src/lib.rs +++ b/scale-decode/src/lib.rs @@ -166,7 +166,7 @@ use alloc::vec::Vec; /// This trait is implemented for any type `T` where `T` implements [`IntoVisitor`] and the errors returned /// from this [`Visitor`] can be converted into [`Error`]. It's essentially a convenience wrapper around /// [`visitor::decode_with_visitor`] that mirrors `scale-encode`'s `EncodeAsType`. -pub trait DecodeAsType: Sized { +pub trait DecodeAsType: Sized + IntoVisitor { /// Given some input bytes, a `type_id`, and type registry, attempt to decode said bytes into /// `Self`. Implementations should modify the `&mut` reference to the bytes such that any bytes /// not used in the course of decoding are still pointed to after decoding is complete. @@ -192,11 +192,7 @@ pub trait DecodeAsType: Sized { ) -> Result; } -impl DecodeAsType for T -where - T: IntoVisitor, - Error: From<::Error>, -{ +impl DecodeAsType for T { fn decode_as_type_maybe_compact( input: &mut &[u8], type_id: u32, @@ -267,11 +263,11 @@ pub trait FieldIter<'a>: Iterator> {} impl<'a, T> FieldIter<'a> for T where T: Iterator> {} /// This trait can be implemented on any type that has an associated [`Visitor`] responsible for decoding -/// SCALE encoded bytes to it. If you implement this on some type and the [`Visitor`] that you return has -/// an error type that converts into [`Error`], then you'll also get a [`DecodeAsType`] implementation for free. +/// SCALE encoded bytes to it. Anything that implements this trait gets a [`DecodeAsType`] implementation +/// for free. pub trait IntoVisitor { /// The visitor type used to decode SCALE encoded bytes to `Self`. - type Visitor: for<'scale, 'info> visitor::Visitor = Self>; + type Visitor: for<'scale, 'info> visitor::Visitor = Self, Error = Error>; /// A means of obtaining this visitor. fn into_visitor() -> Self::Visitor; } diff --git a/scale-decode/src/visitor/mod.rs b/scale-decode/src/visitor/mod.rs index 5a7b38f..4786000 100644 --- a/scale-decode/src/visitor/mod.rs +++ b/scale-decode/src/visitor/mod.rs @@ -326,6 +326,26 @@ pub enum DecodeAsTypeResult { Decoded(R), } +impl DecodeAsTypeResult { + /// If we have a [`DecodeAsTypeResult::Decoded`], the function provided will + /// map this decoded result to whatever it returns. + pub fn map_decoded T>(self, f: F) -> DecodeAsTypeResult { + match self { + DecodeAsTypeResult::Skipped(s) => DecodeAsTypeResult::Skipped(s), + DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(f(r)) + } + } + + /// If we have a [`DecodeAsTypeResult::Skipped`], the function provided will + /// map this skipped value to whatever it returns. + pub fn map_skipped T>(self, f: F) -> DecodeAsTypeResult { + match self { + DecodeAsTypeResult::Skipped(s) => DecodeAsTypeResult::Skipped(f(s)), + DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(r) + } + } +} + /// This is implemented for visitor related types which have a `decode_item` method, /// and allows you to generically talk about decoding unnamed items. pub trait DecodeItemIterator<'scale, 'info> { From 050a0aeaba1ca4bc0fa211cbd3e2d5920d242d3b Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 9 Nov 2023 15:52:52 +0000 Subject: [PATCH 2/8] simplify some bounds and add a dev note --- Cargo.toml | 1 + scale-decode/src/error/mod.rs | 7 ++++++ scale-decode/src/impls/mod.rs | 43 ++++++++--------------------------- scale-decode/src/lib.rs | 9 ++++++-- 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ae96e6..1aadc78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "scale-decode-derive", "testing/no_std", ] +resolver = "2" [workspace.package] version = "0.9.0" diff --git a/scale-decode/src/error/mod.rs b/scale-decode/src/error/mod.rs index f8672fc..78af5ea 100644 --- a/scale-decode/src/error/mod.rs +++ b/scale-decode/src/error/mod.rs @@ -103,6 +103,13 @@ impl From for Error { } } +impl From for Error { + fn from(err: codec::Error) -> Error { + let err: DecodeError = err.into(); + Error::new(err.into()) + } +} + /// The underlying nature of the error. #[derive(Debug, derive_more::From, derive_more::Display)] pub enum ErrorKind { diff --git a/scale-decode/src/impls/mod.rs b/scale-decode/src/impls/mod.rs index 73941f5..0c1bc90 100644 --- a/scale-decode/src/impls/mod.rs +++ b/scale-decode/src/impls/mod.rs @@ -262,7 +262,6 @@ macro_rules! impl_decode_seq_via_collect { impl <$generic> Visitor for BasicVisitor<$ty<$generic>> where $generic: IntoVisitor, - Error: From<<$generic::Visitor as Visitor>::Error>, $( $($where)* )? { type Value<'scale, 'info> = $ty<$generic>; @@ -306,11 +305,7 @@ macro_rules! array_method_impl { Ok(arr) }}; } -impl Visitor for BasicVisitor<[T; N]> -where - T: IntoVisitor, - Error: From<::Error>, -{ +impl Visitor for BasicVisitor<[T; N]> { type Value<'scale, 'info> = [T; N]; type Error = Error; @@ -331,22 +326,14 @@ where visit_single_field_composite_tuple_impls!(); } -impl IntoVisitor for [T; N] -where - T: IntoVisitor, - Error: From<::Error>, -{ +impl IntoVisitor for [T; N] { type Visitor = BasicVisitor<[T; N]>; fn into_visitor() -> Self::Visitor { BasicVisitor { _marker: core::marker::PhantomData } } } -impl Visitor for BasicVisitor> -where - T: IntoVisitor, - Error: From<::Error>, -{ +impl Visitor for BasicVisitor> { type Error = Error; type Value<'scale, 'info> = BTreeMap; @@ -375,11 +362,7 @@ where } impl_into_visitor!(BTreeMap); -impl Visitor for BasicVisitor> -where - T: IntoVisitor, - Error: From<::Error>, -{ +impl Visitor for BasicVisitor> { type Error = Error; type Value<'scale, 'info> = Option; @@ -409,13 +392,7 @@ where } impl_into_visitor!(Option); -impl Visitor for BasicVisitor> -where - T: IntoVisitor, - Error: From<::Error>, - E: IntoVisitor, - Error: From<::Error>, -{ +impl Visitor for BasicVisitor> { type Error = Error; type Value<'scale, 'info> = Result; @@ -595,7 +572,6 @@ macro_rules! impl_decode_tuple { impl < $($t),* > Visitor for BasicVisitor<($($t,)*)> where $( $t: IntoVisitor, - Error: From<<$t::Visitor as Visitor>::Error>, )* { type Value<'scale, 'info> = ($($t,)*); @@ -623,7 +599,7 @@ macro_rules! impl_decode_tuple { // We can turn this tuple into a visitor which knows how to decode it: impl < $($t),* > IntoVisitor for ($($t,)*) - where $( $t: IntoVisitor, Error: From<<$t::Visitor as Visitor>::Error>, )* + where $( $t: IntoVisitor, )* { type Visitor = BasicVisitor<($($t,)*)>; fn into_visitor() -> Self::Visitor { @@ -633,7 +609,7 @@ macro_rules! impl_decode_tuple { // We can decode given a list of fields (just delegate to the visitor impl: impl < $($t),* > DecodeAsFields for ($($t,)*) - where $( $t: IntoVisitor, Error: From<<$t::Visitor as Visitor>::Error>, )* + where $( $t: IntoVisitor, )* { fn decode_as_fields<'info>(input: &mut &[u8], fields: &mut dyn FieldIter<'info>, types: &'info scale_info::PortableRegistry) -> Result { let mut composite = crate::visitor::types::Composite::new(input, crate::EMPTY_SCALE_INFO_PATH, fields, types, false); @@ -678,7 +654,6 @@ fn decode_items_using<'a, 'scale, 'info, D: DecodeItemIterator<'scale, 'info>, T ) -> impl Iterator> + 'a where T: IntoVisitor, - Error: From<::Error>, D: DecodeItemIterator<'scale, 'info>, { let mut idx = 0; @@ -712,7 +687,7 @@ mod test { fn assert_encode_decode_to_with(a: &A, b: &B) where A: Encode, - B: DecodeAsType + PartialEq + core::fmt::Debug, + B: IntoVisitor + PartialEq + core::fmt::Debug, T: scale_info::TypeInfo + 'static, { let (type_id, types) = make_type::(); @@ -726,7 +701,7 @@ mod test { fn assert_encode_decode_to(a: &A, b: &B) where A: Encode + scale_info::TypeInfo + 'static, - B: DecodeAsType + PartialEq + core::fmt::Debug, + B: IntoVisitor + PartialEq + core::fmt::Debug, { assert_encode_decode_to_with::(a, b); } diff --git a/scale-decode/src/lib.rs b/scale-decode/src/lib.rs index 549da48..a7c0308 100644 --- a/scale-decode/src/lib.rs +++ b/scale-decode/src/lib.rs @@ -263,8 +263,13 @@ pub trait FieldIter<'a>: Iterator> {} impl<'a, T> FieldIter<'a> for T where T: Iterator> {} /// This trait can be implemented on any type that has an associated [`Visitor`] responsible for decoding -/// SCALE encoded bytes to it. Anything that implements this trait gets a [`DecodeAsType`] implementation -/// for free. +/// SCALE encoded bytes to it whose error type is [`Error`]. Anything that implements this trait gets a +/// [`DecodeAsType`] implementation for free. +// Dev note: This used to allow for any Error type that could be converted into `scale_decode::Error`. +// The problem with this is that the `DecodeAsType` trait became tricky to use in some contexts, because it +// didn't automatically imply so much. Realistically, being stricter here shouldn't matter too much; derive +// impls all use `scale_decode::Error` anyway, and manual impls can just manually convert into the error +// rather than rely on auto conversion, if they care about also being able to impl `DecodeAsType`. pub trait IntoVisitor { /// The visitor type used to decode SCALE encoded bytes to `Self`. type Visitor: for<'scale, 'info> visitor::Visitor = Self, Error = Error>; From cc4ad4d9a2a8fd816086336a2c1db91463169c03 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 9 Nov 2023 17:49:04 +0000 Subject: [PATCH 3/8] cargo fmt --- scale-decode/src/impls/mod.rs | 4 +--- scale-decode/src/visitor/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/scale-decode/src/impls/mod.rs b/scale-decode/src/impls/mod.rs index 0c1bc90..19e4228 100644 --- a/scale-decode/src/impls/mod.rs +++ b/scale-decode/src/impls/mod.rs @@ -350,9 +350,7 @@ impl Visitor for BasicVisitor> { continue; }; // Decode the value now that we have a valid name. - let Some(val) = value.decode_item(T::into_visitor()) else { - break - }; + let Some(val) = value.decode_item(T::into_visitor()) else { break }; // Save to the map. let val = val.map_err(|e| Error::from(e).at_field(key.to_owned()))?; map.insert(key.to_owned(), val); diff --git a/scale-decode/src/visitor/mod.rs b/scale-decode/src/visitor/mod.rs index 4786000..2378327 100644 --- a/scale-decode/src/visitor/mod.rs +++ b/scale-decode/src/visitor/mod.rs @@ -326,13 +326,13 @@ pub enum DecodeAsTypeResult { Decoded(R), } -impl DecodeAsTypeResult { +impl DecodeAsTypeResult { /// If we have a [`DecodeAsTypeResult::Decoded`], the function provided will /// map this decoded result to whatever it returns. pub fn map_decoded T>(self, f: F) -> DecodeAsTypeResult { match self { DecodeAsTypeResult::Skipped(s) => DecodeAsTypeResult::Skipped(s), - DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(f(r)) + DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(f(r)), } } @@ -341,7 +341,7 @@ impl DecodeAsTypeResult { pub fn map_skipped T>(self, f: F) -> DecodeAsTypeResult { match self { DecodeAsTypeResult::Skipped(s) => DecodeAsTypeResult::Skipped(f(s)), - DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(r) + DecodeAsTypeResult::Decoded(r) => DecodeAsTypeResult::Decoded(r), } } } From d6971da09d07f15cae3b3b5b7995b6d72244b187 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 10 Nov 2023 12:48:11 +0000 Subject: [PATCH 4/8] Add adapter to make it easy to write visitors whose errors convert nicely --- scale-decode/src/visitor/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scale-decode/src/visitor/mod.rs b/scale-decode/src/visitor/mod.rs index 2378327..d2ffeeb 100644 --- a/scale-decode/src/visitor/mod.rs +++ b/scale-decode/src/visitor/mod.rs @@ -378,6 +378,33 @@ impl Visitor for IgnoreVisitor { } } +/// Some [`Visitor`] implementations may want to return an error type other than [`crate::Error`], which means +/// that they would not be automatically compatible with [`crate::IntoVisitor`], which requires visitors that do return +/// [`crate::Error`] errors. +/// +/// As long as the error type of the visitor implementation can be converted into [`crate::Error`] via [`Into`], +/// the visitor implementation can be wrapped in this [`VisitorWithCrateError`] struct to make it work with +/// [`crate::IntoVisitor`]. +pub struct VisitorWithCrateError(pub V); + +impl Visitor for VisitorWithCrateError +where + V::Error: Into, +{ + type Value<'scale, 'info> = V::Value<'scale, 'info>; + type Error = crate::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: TypeId, + types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + let res = decode_with_visitor(input, type_id.0, types, self.0).map_err(Into::into); + DecodeAsTypeResult::Decoded(res) + } +} + #[cfg(test)] mod test { use crate::visitor::TypeId; From 9968819eb51fee568dc840cc1b7826d8b567a5f9 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 10 Nov 2023 13:00:32 +0000 Subject: [PATCH 5/8] remove now-unneeded Error::from's --- scale-decode/src/impls/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scale-decode/src/impls/mod.rs b/scale-decode/src/impls/mod.rs index 19e4228..4c92040 100644 --- a/scale-decode/src/impls/mod.rs +++ b/scale-decode/src/impls/mod.rs @@ -352,7 +352,7 @@ impl Visitor for BasicVisitor> { // Decode the value now that we have a valid name. let Some(val) = value.decode_item(T::into_visitor()) else { break }; // Save to the map. - let val = val.map_err(|e| Error::from(e).at_field(key.to_owned()))?; + let val = val.map_err(|e| e.at_field(key.to_owned()))?; map.insert(key.to_owned(), val); } Ok(map) @@ -374,7 +374,7 @@ impl Visitor for BasicVisitor> { .fields() .decode_item(T::into_visitor()) .transpose() - .map_err(|e| Error::from(e).at_variant("Some"))? + .map_err(|e| e.at_variant("Some"))? .expect("checked for 1 field already so should be ok"); Ok(Some(val)) } else if value.name() == "None" && value.fields().remaining() == 0 { @@ -404,7 +404,7 @@ impl Visitor for BasicVisitor> { .fields() .decode_item(T::into_visitor()) .transpose() - .map_err(|e| Error::from(e).at_variant("Ok"))? + .map_err(|e| e.at_variant("Ok"))? .expect("checked for 1 field already so should be ok"); Ok(Ok(val)) } else if value.name() == "Err" && value.fields().remaining() == 1 { @@ -412,7 +412,7 @@ impl Visitor for BasicVisitor> { .fields() .decode_item(E::into_visitor()) .transpose() - .map_err(|e| Error::from(e).at_variant("Err"))? + .map_err(|e| e.at_variant("Err"))? .expect("checked for 1 field already so should be ok"); Ok(Err(val)) } else { @@ -518,7 +518,7 @@ macro_rules! tuple_method_impl { let v = $value .decode_item($t::into_visitor()) .transpose() - .map_err(|e| Error::from(e).at_idx(idx))? + .map_err(|e| e.at_idx(idx))? .expect("length already checked via .remaining()"); idx += 1; v @@ -656,9 +656,7 @@ where { let mut idx = 0; core::iter::from_fn(move || { - let item = decoder - .decode_item(T::into_visitor()) - .map(|res| res.map_err(|e| Error::from(e).at_idx(idx))); + let item = decoder.decode_item(T::into_visitor()).map(|res| res.map_err(|e| e.at_idx(idx))); idx += 1; item }) From a763acb2ac3565d7266a1467d81e64df96bc7b42 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 10 Nov 2023 13:34:29 +0000 Subject: [PATCH 6/8] add a quick sanity check test for VisitorWithCrateError --- scale-decode/src/visitor/mod.rs | 63 ++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/scale-decode/src/visitor/mod.rs b/scale-decode/src/visitor/mod.rs index d2ffeeb..0aa81d4 100644 --- a/scale-decode/src/visitor/mod.rs +++ b/scale-decode/src/visitor/mod.rs @@ -385,6 +385,7 @@ impl Visitor for IgnoreVisitor { /// As long as the error type of the visitor implementation can be converted into [`crate::Error`] via [`Into`], /// the visitor implementation can be wrapped in this [`VisitorWithCrateError`] struct to make it work with /// [`crate::IntoVisitor`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct VisitorWithCrateError(pub V); impl Visitor for VisitorWithCrateError @@ -444,6 +445,7 @@ mod test { BitSequence(scale_bits::Bits), } + #[derive(Clone, Copy)] struct ValueVisitor; impl Visitor for ValueVisitor { type Value<'scale, 'info> = Value; @@ -642,22 +644,69 @@ mod test { /// This just tests that if we try to decode some values we've encoded using a visitor /// which just ignores everything by default, that we'll consume all of the bytes. - fn encode_decode_check_explicit_info( + fn encode_decode_check_explicit_info< + Ty: scale_info::TypeInfo + 'static, + T: Encode, + V: for<'s, 'i> Visitor = Value, Error = E>, + E: std::fmt::Debug, + >( val: T, expected: Value, + visitor: V, ) { let encoded = val.encode(); let (id, types) = make_type::(); let bytes = &mut &*encoded; - let val = decode_with_visitor(bytes, id, &types, ValueVisitor) - .expect("decoding should not error"); + let val = + decode_with_visitor(bytes, id, &types, visitor).expect("decoding should not error"); assert_eq!(bytes.len(), 0, "Decoding should consume all bytes"); assert_eq!(val, expected); } + fn encode_decode_check_with_visitor< + T: Encode + scale_info::TypeInfo + 'static, + V: for<'s, 'i> Visitor = Value, Error = E>, + E: std::fmt::Debug, + >( + val: T, + expected: Value, + visitor: V, + ) { + encode_decode_check_explicit_info::(val, expected, visitor); + } + fn encode_decode_check(val: T, expected: Value) { - encode_decode_check_explicit_info::(val, expected); + encode_decode_check_explicit_info::(val, expected, ValueVisitor); + } + + #[test] + fn decode_with_root_error_wrapper_works() { + use crate::visitor::VisitorWithCrateError; + let visitor = VisitorWithCrateError(ValueVisitor); + + encode_decode_check_with_visitor(123u8, Value::U8(123), visitor); + encode_decode_check_with_visitor(123u16, Value::U16(123), visitor); + encode_decode_check_with_visitor(123u32, Value::U32(123), visitor); + encode_decode_check_with_visitor(123u64, Value::U64(123), visitor); + encode_decode_check_with_visitor(123u128, Value::U128(123), visitor); + encode_decode_check_with_visitor( + "Hello there", + Value::Str("Hello there".to_owned()), + visitor, + ); + + #[derive(Encode, scale_info::TypeInfo)] + struct Unnamed(bool, String, Vec); + encode_decode_check_with_visitor( + Unnamed(true, "James".into(), vec![1, 2, 3]), + Value::Composite(vec![ + (String::new(), Value::Bool(true)), + (String::new(), Value::Str("James".to_string())), + (String::new(), Value::Sequence(vec![Value::U8(1), Value::U8(2), Value::U8(3)])), + ]), + visitor, + ); } #[test] @@ -674,7 +723,11 @@ mod test { encode_decode_check(codec::Compact(123u128), Value::U128(123)); encode_decode_check(true, Value::Bool(true)); encode_decode_check(false, Value::Bool(false)); - encode_decode_check_explicit_info::('c' as u32, Value::Char('c')); + encode_decode_check_explicit_info::( + 'c' as u32, + Value::Char('c'), + ValueVisitor, + ); encode_decode_check("Hello there", Value::Str("Hello there".to_owned())); encode_decode_check("Hello there".to_string(), Value::Str("Hello there".to_owned())); } From 4c68d22498abe8ddfa6a9c110280474f2c2cd66c Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 10 Nov 2023 13:37:30 +0000 Subject: [PATCH 7/8] fix no-std tests --- scale-decode/src/visitor/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scale-decode/src/visitor/mod.rs b/scale-decode/src/visitor/mod.rs index 0aa81d4..2bccd30 100644 --- a/scale-decode/src/visitor/mod.rs +++ b/scale-decode/src/visitor/mod.rs @@ -648,7 +648,7 @@ mod test { Ty: scale_info::TypeInfo + 'static, T: Encode, V: for<'s, 'i> Visitor = Value, Error = E>, - E: std::fmt::Debug, + E: core::fmt::Debug, >( val: T, expected: Value, @@ -667,7 +667,7 @@ mod test { fn encode_decode_check_with_visitor< T: Encode + scale_info::TypeInfo + 'static, V: for<'s, 'i> Visitor = Value, Error = E>, - E: std::fmt::Debug, + E: core::fmt::Debug, >( val: T, expected: Value, From d177e4f9e900570903aae76953d185e7805344d1 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Fri, 10 Nov 2023 13:40:15 +0000 Subject: [PATCH 8/8] IntoVisitor => DecodeAsType on a couple of tests to undo prev change --- scale-decode/src/impls/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scale-decode/src/impls/mod.rs b/scale-decode/src/impls/mod.rs index 4c92040..de61b44 100644 --- a/scale-decode/src/impls/mod.rs +++ b/scale-decode/src/impls/mod.rs @@ -683,7 +683,7 @@ mod test { fn assert_encode_decode_to_with(a: &A, b: &B) where A: Encode, - B: IntoVisitor + PartialEq + core::fmt::Debug, + B: DecodeAsType + PartialEq + core::fmt::Debug, T: scale_info::TypeInfo + 'static, { let (type_id, types) = make_type::(); @@ -697,7 +697,7 @@ mod test { fn assert_encode_decode_to(a: &A, b: &B) where A: Encode + scale_info::TypeInfo + 'static, - B: IntoVisitor + PartialEq + core::fmt::Debug, + B: DecodeAsType + PartialEq + core::fmt::Debug, { assert_encode_decode_to_with::(a, b); }