// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Traits for dealing with the idea of membership. use impl_trait_for_tuples::impl_for_tuples; use sp_arithmetic::traits::AtLeast16BitUnsigned; use sp_runtime::DispatchResult; use sp_std::{marker::PhantomData, prelude::*}; /// A trait for querying whether a type can be said to "contain" a value. pub trait Contains { /// Return `true` if this "contains" the given value `t`. fn contains(t: &T) -> bool; } #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] impl Contains for Tuple { fn contains(t: &T) -> bool { for_tuples!( #( if Tuple::contains(t) { return true } )* ); false } } /// A trait for querying whether a type can be said to "contain" a pair-value. pub trait ContainsPair { /// Return `true` if this "contains" the pair-value `(a, b)`. fn contains(a: &A, b: &B) -> bool; } #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] impl ContainsPair for Tuple { fn contains(a: &A, b: &B) -> bool { for_tuples!( #( if Tuple::contains(a, b) { return true } )* ); false } } /// Converter `struct` to use a `ContainsPair` implementation for a `Contains` bound. pub struct FromContainsPair(PhantomData); impl> Contains<(A, B)> for FromContainsPair { fn contains((ref a, ref b): &(A, B)) -> bool { CP::contains(a, b) } } /// A [`Contains`] implementation that contains every value. pub enum Everything {} impl Contains for Everything { fn contains(_: &T) -> bool { true } } impl ContainsPair for Everything { fn contains(_: &A, _: &B) -> bool { true } } /// A [`Contains`] implementation that contains no value. pub enum Nothing {} impl Contains for Nothing { fn contains(_: &T) -> bool { false } } impl ContainsPair for Nothing { fn contains(_: &A, _: &B) -> bool { false } } /// A [`Contains`] implementation that contains everything except the values in `Exclude`. pub struct EverythingBut(PhantomData); impl> Contains for EverythingBut { fn contains(t: &T) -> bool { !Exclude::contains(t) } } impl> ContainsPair for EverythingBut { fn contains(a: &A, b: &B) -> bool { !Exclude::contains(a, b) } } /// A [`Contains`] implementation that contains all members of `These` excepting any members in /// `Except`. pub struct TheseExcept(PhantomData<(These, Except)>); impl, Except: Contains> Contains for TheseExcept { fn contains(t: &T) -> bool { These::contains(t) && !Except::contains(t) } } impl, Except: ContainsPair> ContainsPair for TheseExcept { fn contains(a: &A, b: &B) -> bool { These::contains(a, b) && !Except::contains(a, b) } } /// A [`Contains`] implementation which contains all members of `These` which are also members of /// `Those`. pub struct InsideBoth(PhantomData<(These, Those)>); impl, Those: Contains> Contains for InsideBoth { fn contains(t: &T) -> bool { These::contains(t) && Those::contains(t) } } impl, Those: ContainsPair> ContainsPair for InsideBoth { fn contains(a: &A, b: &B) -> bool { These::contains(a, b) && Those::contains(a, b) } } /// An implementation of [`Contains`] which contains only equal members to `T`. pub struct Equals(PhantomData); impl> Contains for Equals { fn contains(t: &X) -> bool { t == &T::get() } } /// Create a type which implements the `Contains` trait for a particular type with syntax similar /// to `matches!`. #[macro_export] macro_rules! match_types { ( pub type $n:ident: impl Contains<$t:ty> = { $phead:pat_param $( | $ptail:pat )* }; $( $rest:tt )* ) => { pub struct $n; impl $crate::traits::Contains<$t> for $n { fn contains(l: &$t) -> bool { matches!(l, $phead $( | $ptail )* ) } } $crate::match_types!( $( $rest )* ); }; ( pub type $n:ident: impl ContainsPair<$a:ty, $b:ty> = { $phead:pat_param $( | $ptail:pat )* }; $( $rest:tt )* ) => { pub struct $n; impl $crate::traits::ContainsPair<$a, $b> for $n { fn contains(a: &$a, b: &$b) -> bool { matches!((a, b), $phead $( | $ptail )* ) } } $crate::match_types!( $( $rest )* ); }; () => {} } /// Create a type which implements the `Contains` trait for a particular type with syntax similar /// to `matches!`. #[macro_export] #[deprecated = "Use `match_types!` instead"] macro_rules! match_type { ($( $x:tt )*) => { $crate::match_types!( $( $x )* ); } } #[deprecated = "Use `Everything` instead"] pub type AllowAll = Everything; #[deprecated = "Use `Nothing` instead"] pub type DenyAll = Nothing; #[deprecated = "Use `Contains` instead"] pub trait Filter { fn filter(t: &T) -> bool; } #[allow(deprecated)] impl> Filter for C { fn filter(t: &T) -> bool { Self::contains(t) } } #[cfg(test)] mod tests { use super::*; match_types! { pub type OneOrTenToTwenty: impl Contains = { 1 | 10..=20 }; } #[test] fn match_types_works() { for i in 0..=255 { assert_eq!(OneOrTenToTwenty::contains(&i), i == 1 || i >= 10 && i <= 20); } } } /// A trait for a set which can enumerate its members in order. pub trait SortedMembers { /// Get a vector of all members in the set, ordered. fn sorted_members() -> Vec; /// Return `true` if this "contains" the given value `t`. fn contains(t: &T) -> bool { Self::sorted_members().binary_search(t).is_ok() } /// Get the number of items in the set. fn count() -> usize { Self::sorted_members().len() } /// Add an item that would satisfy `contains`. It does not make sure any other /// state is correctly maintained or generated. /// /// **Should be used for benchmarking only!!!** #[cfg(feature = "runtime-benchmarks")] fn add(_t: &T) { unimplemented!() } } /// Adapter struct for turning an `OrderedMembership` impl into a `Contains` impl. pub struct AsContains(PhantomData<(OM,)>); impl> Contains for AsContains { fn contains(t: &T) -> bool { OM::contains(t) } } /// Trivial utility for implementing `Contains`/`OrderedMembership` with a `Vec`. pub struct IsInVec(PhantomData); impl>> Contains for IsInVec { fn contains(t: &X) -> bool { T::get().contains(t) } } impl>> SortedMembers for IsInVec { fn sorted_members() -> Vec { let mut r = T::get(); r.sort(); r } } /// A trait for querying bound for the length of an implementation of `Contains` pub trait ContainsLengthBound { /// Minimum number of elements contained fn min_len() -> usize; /// Maximum number of elements contained fn max_len() -> usize; } /// Ranked membership data structure. pub trait RankedMembers { type AccountId; type Rank: AtLeast16BitUnsigned; /// The lowest rank possible in this membership organisation. fn min_rank() -> Self::Rank; /// Return the rank of the given ID, or `None` if they are not a member. fn rank_of(who: &Self::AccountId) -> Option; /// Add a member to the group at the `min_rank()`. fn induct(who: &Self::AccountId) -> DispatchResult; /// Promote a member to the next higher rank. fn promote(who: &Self::AccountId) -> DispatchResult; /// Demote a member to the next lower rank; demoting beyond the `min_rank` removes the /// member entirely. fn demote(who: &Self::AccountId) -> DispatchResult; } /// Trait for type that can handle the initialization of account IDs at genesis. pub trait InitializeMembers { /// Initialize the members to the given `members`. fn initialize_members(members: &[AccountId]); } impl InitializeMembers for () { fn initialize_members(_: &[T]) {} } /// Trait for type that can handle incremental changes to a set of account IDs. pub trait ChangeMembers { /// A number of members `incoming` just joined the set and replaced some `outgoing` ones. The /// new set is given by `new`, and need not be sorted. /// /// This resets any previous value of prime. fn change_members(incoming: &[AccountId], outgoing: &[AccountId], mut new: Vec) { new.sort(); Self::change_members_sorted(incoming, outgoing, &new[..]); } /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The /// new set is thus given by `sorted_new` and **must be sorted**. /// /// NOTE: This is the only function that needs to be implemented in `ChangeMembers`. /// /// This resets any previous value of prime. fn change_members_sorted( incoming: &[AccountId], outgoing: &[AccountId], sorted_new: &[AccountId], ); /// Set the new members; they **must already be sorted**. This will compute the diff and use it /// to call `change_members_sorted`. /// /// This resets any previous value of prime. fn set_members_sorted(new_members: &[AccountId], old_members: &[AccountId]) { let (incoming, outgoing) = Self::compute_members_diff_sorted(new_members, old_members); Self::change_members_sorted(&incoming[..], &outgoing[..], new_members); } /// Compute diff between new and old members; they **must already be sorted**. /// /// Returns incoming and outgoing members. fn compute_members_diff_sorted( new_members: &[AccountId], old_members: &[AccountId], ) -> (Vec, Vec) { let mut old_iter = old_members.iter(); let mut new_iter = new_members.iter(); let mut incoming = Vec::new(); let mut outgoing = Vec::new(); let mut old_i = old_iter.next(); let mut new_i = new_iter.next(); loop { match (old_i, new_i) { (None, None) => break, (Some(old), Some(new)) if old == new => { old_i = old_iter.next(); new_i = new_iter.next(); }, (Some(old), Some(new)) if old < new => { outgoing.push(old.clone()); old_i = old_iter.next(); }, (Some(old), None) => { outgoing.push(old.clone()); old_i = old_iter.next(); }, (_, Some(new)) => { incoming.push(new.clone()); new_i = new_iter.next(); }, } } (incoming, outgoing) } /// Set the prime member. fn set_prime(_prime: Option) {} /// Get the current prime. fn get_prime() -> Option { None } } impl ChangeMembers for () { fn change_members(_: &[T], _: &[T], _: Vec) {} fn change_members_sorted(_: &[T], _: &[T], _: &[T]) {} fn set_members_sorted(_: &[T], _: &[T]) {} fn set_prime(_: Option) {} }