diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index 78f2e393014cee0850d9015b77554f80abaa5eac..5bd7358e4f648ecefa72396e96c0836d882eb8eb 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -530,7 +530,7 @@ morph_types! { } where L::Type: Ord, M: TryMorph<L::Type, Outcome = L::Type>; } -/// Extensible conversion trait. Generic over both source and destination types. +/// Infallible conversion trait. Generic over both source and destination types. pub trait Convert<A, B> { /// Make conversion. fn convert(a: A) -> B; @@ -542,7 +542,125 @@ impl<A, B: Default> Convert<A, B> for () { } } -/// Adapter which turns a `Get` implementation into a `Convert` implementation which always returns +/// Reversing infallible conversion trait. Generic over both source and destination types. +/// +/// This specifically reverses the conversion. +pub trait ConvertBack<A, B>: Convert<A, B> { + /// Make conversion back. + fn convert_back(b: B) -> A; +} + +/// Fallible conversion trait returning an [Option]. Generic over both source and destination types. +pub trait MaybeConvert<A, B> { + /// Attempt to make conversion. + fn maybe_convert(a: A) -> Option<B>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl<A: Clone, B> MaybeConvert<A, B> for Tuple { + fn maybe_convert(a: A) -> Option<B> { + for_tuples!( #( + match Tuple::maybe_convert(a.clone()) { + Some(b) => return Some(b), + None => {}, + } + )* ); + None + } +} + +/// Reversing fallible conversion trait returning an [Option]. Generic over both source and +/// destination types. +pub trait MaybeConvertBack<A, B>: MaybeConvert<A, B> { + /// Attempt to make conversion back. + fn maybe_convert_back(b: B) -> Option<A>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl<A: Clone, B: Clone> MaybeConvertBack<A, B> for Tuple { + fn maybe_convert_back(b: B) -> Option<A> { + for_tuples!( #( + match Tuple::maybe_convert_back(b.clone()) { + Some(a) => return Some(a), + None => {}, + } + )* ); + None + } +} + +/// Fallible conversion trait which returns the argument in the case of being unable to convert. +/// Generic over both source and destination types. +pub trait TryConvert<A, B> { + /// Attempt to make conversion. If returning [Result::Err], the inner must always be `a`. + fn try_convert(a: A) -> Result<B, A>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl<A, B> TryConvert<A, B> for Tuple { + fn try_convert(a: A) -> Result<B, A> { + for_tuples!( #( + let a = match Tuple::try_convert(a) { + Ok(b) => return Ok(b), + Err(a) => a, + }; + )* ); + Err(a) + } +} + +/// Reversing fallible conversion trait which returns the argument in the case of being unable to +/// convert back. Generic over both source and destination types. +pub trait TryConvertBack<A, B>: TryConvert<A, B> { + /// Attempt to make conversion back. If returning [Result::Err], the inner must always be `b`. + + fn try_convert_back(b: B) -> Result<A, B>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl<A, B> TryConvertBack<A, B> for Tuple { + fn try_convert_back(b: B) -> Result<A, B> { + for_tuples!( #( + let b = match Tuple::try_convert_back(b) { + Ok(a) => return Ok(a), + Err(b) => b, + }; + )* ); + Err(b) + } +} + +/// Definition for a bi-directional, fallible conversion between two types. +pub trait MaybeEquivalence<A, B> { + /// Attempt to convert reference of `A` into value of `B`, returning `None` if not possible. + fn convert(a: &A) -> Option<B>; + /// Attempt to convert reference of `B` into value of `A`, returning `None` if not possible. + fn convert_back(b: &B) -> Option<A>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl<A, B> MaybeEquivalence<A, B> for Tuple { + fn convert(a: &A) -> Option<B> { + for_tuples!( #( + match Tuple::convert(a) { + Some(b) => return Some(b), + None => {}, + } + )* ); + None + } + fn convert_back(b: &B) -> Option<A> { + for_tuples!( #( + match Tuple::convert_back(b) { + Some(a) => return Some(a), + None => {}, + } + )* ); + None + } +} + +/// Adapter which turns a [Get] implementation into a [Convert] implementation which always returns /// in the same value no matter the input. pub struct ConvertToValue<T>(sp_std::marker::PhantomData<T>); impl<X, Y, T: Get<Y>> Convert<X, Y> for ConvertToValue<T> { @@ -550,6 +668,34 @@ impl<X, Y, T: Get<Y>> Convert<X, Y> for ConvertToValue<T> { T::get() } } +impl<X, Y, T: Get<Y>> MaybeConvert<X, Y> for ConvertToValue<T> { + fn maybe_convert(_: X) -> Option<Y> { + Some(T::get()) + } +} +impl<X, Y, T: Get<Y>> MaybeConvertBack<X, Y> for ConvertToValue<T> { + fn maybe_convert_back(_: Y) -> Option<X> { + None + } +} +impl<X, Y, T: Get<Y>> TryConvert<X, Y> for ConvertToValue<T> { + fn try_convert(_: X) -> Result<Y, X> { + Ok(T::get()) + } +} +impl<X, Y, T: Get<Y>> TryConvertBack<X, Y> for ConvertToValue<T> { + fn try_convert_back(y: Y) -> Result<X, Y> { + Err(y) + } +} +impl<X, Y, T: Get<Y>> MaybeEquivalence<X, Y> for ConvertToValue<T> { + fn convert(_: &X) -> Option<Y> { + Some(T::get()) + } + fn convert_back(_: &Y) -> Option<X> { + None + } +} /// A structure that performs identity conversion. pub struct Identity; @@ -563,19 +709,100 @@ impl<T> ConvertBack<T, T> for Identity { a } } +impl<T> MaybeConvert<T, T> for Identity { + fn maybe_convert(a: T) -> Option<T> { + Some(a) + } +} +impl<T> MaybeConvertBack<T, T> for Identity { + fn maybe_convert_back(a: T) -> Option<T> { + Some(a) + } +} +impl<T> TryConvert<T, T> for Identity { + fn try_convert(a: T) -> Result<T, T> { + Ok(a) + } +} +impl<T> TryConvertBack<T, T> for Identity { + fn try_convert_back(a: T) -> Result<T, T> { + Ok(a) + } +} +impl<T: Clone> MaybeEquivalence<T, T> for Identity { + fn convert(a: &T) -> Option<T> { + Some(a.clone()) + } + fn convert_back(a: &T) -> Option<T> { + Some(a.clone()) + } +} /// A structure that performs standard conversion using the standard Rust conversion traits. pub struct ConvertInto; -impl<A, B: From<A>> Convert<A, B> for ConvertInto { +impl<A: Into<B>, B> Convert<A, B> for ConvertInto { fn convert(a: A) -> B { a.into() } } +impl<A: Into<B>, B> MaybeConvert<A, B> for ConvertInto { + fn maybe_convert(a: A) -> Option<B> { + Some(a.into()) + } +} +impl<A: Into<B>, B: Into<A>> MaybeConvertBack<A, B> for ConvertInto { + fn maybe_convert_back(b: B) -> Option<A> { + Some(b.into()) + } +} +impl<A: Into<B>, B> TryConvert<A, B> for ConvertInto { + fn try_convert(a: A) -> Result<B, A> { + Ok(a.into()) + } +} +impl<A: Into<B>, B: Into<A>> TryConvertBack<A, B> for ConvertInto { + fn try_convert_back(b: B) -> Result<A, B> { + Ok(b.into()) + } +} +impl<A: Clone + Into<B>, B: Clone + Into<A>> MaybeEquivalence<A, B> for ConvertInto { + fn convert(a: &A) -> Option<B> { + Some(a.clone().into()) + } + fn convert_back(b: &B) -> Option<A> { + Some(b.clone().into()) + } +} -/// Extensible conversion trait. Generic over both source and destination types. -pub trait ConvertBack<A, B>: Convert<A, B> { - /// Make conversion back. - fn convert_back(b: B) -> A; +/// A structure that performs standard conversion using the standard Rust conversion traits. +pub struct TryConvertInto; +impl<A: Clone + TryInto<B>, B> MaybeConvert<A, B> for TryConvertInto { + fn maybe_convert(a: A) -> Option<B> { + a.clone().try_into().ok() + } +} +impl<A: Clone + TryInto<B>, B: Clone + TryInto<A>> MaybeConvertBack<A, B> for TryConvertInto { + fn maybe_convert_back(b: B) -> Option<A> { + b.clone().try_into().ok() + } +} +impl<A: Clone + TryInto<B>, B> TryConvert<A, B> for TryConvertInto { + fn try_convert(a: A) -> Result<B, A> { + a.clone().try_into().map_err(|_| a) + } +} +impl<A: Clone + TryInto<B>, B: Clone + TryInto<A>> TryConvertBack<A, B> for TryConvertInto { + fn try_convert_back(b: B) -> Result<A, B> { + b.clone().try_into().map_err(|_| b) + } +} +impl<A: Clone + TryInto<B>, B: Clone + TryInto<A>> MaybeEquivalence<A, B> for TryConvertInto { + fn convert(a: &A) -> Option<B> { + a.clone().try_into().ok() + } + fn convert_back(b: &B) -> Option<A> { + b.clone().try_into().ok() + } } /// Convenience type to work around the highly unergonomic syntax needed