bounded_collections/
lib.rs

1// Copyright 2023 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Collection types that have an upper limit on how many elements that they can contain, and
10//! supporting traits that aid in defining the limit.
11
12#![cfg_attr(not(feature = "std"), no_std)]
13
14pub extern crate alloc;
15
16pub mod bounded_btree_map;
17pub mod bounded_btree_set;
18pub mod bounded_vec;
19pub(crate) mod codec_utils;
20pub mod const_int;
21pub mod weak_bounded_vec;
22
23mod test;
24
25pub use bounded_btree_map::BoundedBTreeMap;
26pub use bounded_btree_set::BoundedBTreeSet;
27pub use bounded_vec::{BoundedSlice, BoundedVec};
28pub use const_int::{ConstInt, ConstUint};
29pub use weak_bounded_vec::WeakBoundedVec;
30
31/// A trait for querying a single value from a type defined in the trait.
32///
33/// It is not required that the value is constant.
34pub trait TypedGet {
35	/// The type which is returned.
36	type Type;
37	/// Return the current value.
38	fn get() -> Self::Type;
39}
40
41/// A trait for querying a single value from a type.
42///
43/// It is not required that the value is constant.
44pub trait Get<T> {
45	/// Return the current value.
46	fn get() -> T;
47}
48
49impl<T: Default> Get<T> for () {
50	fn get() -> T {
51		T::default()
52	}
53}
54
55/// Converts [`Get<I>`] to [`Get<R>`] using [`Into`].
56///
57/// Acts as a type-safe bridge between `Get` implementations where `I: Into<R>`.
58///
59/// - `Inner`: The [`Get<I>`] implementation
60/// - `I`: Source type to convert from
61///
62/// # Example
63/// ```
64/// use bounded_collections::Get;
65/// use bounded_collections::GetInto;
66///
67/// struct MyGetter;
68/// impl Get<u16> for MyGetter { fn get() -> u16 { 42 } }
69/// let foo: u32 = GetInto::<MyGetter, u16>::get();
70/// assert_eq!(foo, 42u32); // <--- infered as u32
71/// ```
72pub struct GetInto<Inner, I>(core::marker::PhantomData<(Inner, I)>);
73
74impl<Inner, I, R> Get<R> for GetInto<Inner, I>
75where
76	Inner: Get<I>,
77	I: Into<R>,
78{
79	/// Returns the converted value by:
80	/// 1. Getting the inner value of type `I`
81	/// 2. Converting it to type `R` using [`Into`]
82	fn get() -> R {
83		Inner::get().into()
84	}
85}
86
87/// Implement Get by returning Default for any type that implements Default.
88pub struct GetDefault;
89impl<T: Default> Get<T> for GetDefault {
90	fn get() -> T {
91		T::default()
92	}
93}
94
95macro_rules! impl_const_get {
96	($name:ident, $t:ty) => {
97		/// Const getter for a basic type.
98		#[derive(Default, Clone)]
99		pub struct $name<const T: $t>;
100
101		#[cfg(feature = "std")]
102		impl<const T: $t> core::fmt::Debug for $name<T> {
103			fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
104				fmt.write_str(&format!("{}<{}>", stringify!($name), T))
105			}
106		}
107		#[cfg(not(feature = "std"))]
108		impl<const T: $t> core::fmt::Debug for $name<T> {
109			fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
110				fmt.write_str("<wasm:stripped>")
111			}
112		}
113		impl<const T: $t> Get<$t> for $name<T> {
114			fn get() -> $t {
115				T
116			}
117		}
118		impl<const T: $t> Get<Option<$t>> for $name<T> {
119			fn get() -> Option<$t> {
120				Some(T)
121			}
122		}
123		impl<const T: $t> TypedGet for $name<T> {
124			type Type = $t;
125			fn get() -> $t {
126				T
127			}
128		}
129	};
130}
131
132impl_const_get!(ConstBool, bool);
133impl_const_get!(ConstU8, u8);
134impl_const_get!(ConstU16, u16);
135impl_const_get!(ConstU32, u32);
136impl_const_get!(ConstU64, u64);
137impl_const_get!(ConstU128, u128);
138impl_const_get!(ConstI8, i8);
139impl_const_get!(ConstI16, i16);
140impl_const_get!(ConstI32, i32);
141impl_const_get!(ConstI64, i64);
142impl_const_get!(ConstI128, i128);
143
144/// Try and collect into a collection `C`.
145pub trait TryCollect<C> {
146	/// The error type that gets returned when a collection can't be made from `self`.
147	type Error;
148	/// Consume self and try to collect the results into `C`.
149	///
150	/// This is useful in preventing the undesirable `.collect().try_into()` call chain on
151	/// collections that need to be converted into a bounded type (e.g. `BoundedVec`).
152	fn try_collect(self) -> Result<C, Self::Error>;
153}
154
155/// Create new implementations of the [`Get`](crate::Get) trait.
156///
157/// The so-called parameter type can be created in four different ways:
158///
159/// - Using `const` to create a parameter type that provides a `const` getter. It is required that
160///   the `value` is const.
161///
162/// - Declare the parameter type without `const` to have more freedom when creating the value.
163///
164/// NOTE: A more substantial version of this macro is available in `frame_support` crate which
165/// allows mutable and persistant variants.
166///
167/// # Examples
168///
169/// ```
170/// # use bounded_collections::Get;
171/// # use bounded_collections::parameter_types;
172/// // This function cannot be used in a const context.
173/// fn non_const_expression() -> u64 { 99 }
174///
175/// const FIXED_VALUE: u64 = 10;
176/// parameter_types! {
177///    pub const Argument: u64 = 42 + FIXED_VALUE;
178///    /// Visibility of the type is optional
179///    OtherArgument: u64 = non_const_expression();
180/// }
181///
182/// trait Config {
183///    type Parameter: Get<u64>;
184///    type OtherParameter: Get<u64>;
185/// }
186///
187/// struct Runtime;
188/// impl Config for Runtime {
189///    type Parameter = Argument;
190///    type OtherParameter = OtherArgument;
191/// }
192/// ```
193///
194/// # Invalid example:
195///
196/// ```compile_fail
197/// # use bounded_collections::Get;
198/// # use bounded_collections::parameter_types;
199/// // This function cannot be used in a const context.
200/// fn non_const_expression() -> u64 { 99 }
201///
202/// parameter_types! {
203///    pub const Argument: u64 = non_const_expression();
204/// }
205/// ```
206#[macro_export]
207macro_rules! parameter_types {
208	(
209		$( #[ $attr:meta ] )*
210		$vis:vis const $name:ident: $type:ty = $value:expr;
211		$( $rest:tt )*
212	) => (
213		$( #[ $attr ] )*
214		$vis struct $name;
215		$crate::parameter_types!(@IMPL_CONST $name , $type , $value);
216		$crate::parameter_types!( $( $rest )* );
217	);
218	(
219		$( #[ $attr:meta ] )*
220		$vis:vis $name:ident: $type:ty = $value:expr;
221		$( $rest:tt )*
222	) => (
223		$( #[ $attr ] )*
224		$vis struct $name;
225		$crate::parameter_types!(@IMPL $name, $type, $value);
226		$crate::parameter_types!( $( $rest )* );
227	);
228	() => ();
229	(@IMPL_CONST $name:ident, $type:ty, $value:expr) => {
230		impl $name {
231			/// Returns the value of this parameter type.
232			pub const fn get() -> $type {
233				$value
234			}
235		}
236
237		impl<I: From<$type>> $crate::Get<I> for $name {
238			fn get() -> I {
239				I::from(Self::get())
240			}
241		}
242
243		impl $crate::TypedGet for $name {
244			type Type = $type;
245			fn get() -> $type {
246				Self::get()
247			}
248		}
249	};
250	(@IMPL $name:ident, $type:ty, $value:expr) => {
251		impl $name {
252			/// Returns the value of this parameter type.
253			pub fn get() -> $type {
254				$value
255			}
256		}
257
258		impl<I: From<$type>> $crate::Get<I> for $name {
259			fn get() -> I {
260				I::from(Self::get())
261			}
262		}
263
264		impl $crate::TypedGet for $name {
265			type Type = $type;
266			fn get() -> $type {
267				Self::get()
268			}
269		}
270	};
271}
272
273/// Build a bounded vec from the given literals.
274///
275/// The type of the outcome must be known.
276///
277/// Will not handle any errors and just panic if the given literals cannot fit in the corresponding
278/// bounded vec type. Thus, this is only suitable for testing and non-consensus code.
279#[macro_export]
280#[cfg(feature = "std")]
281macro_rules! bounded_vec {
282	($ ($values:expr),* $(,)?) => {
283		{
284			$crate::alloc::vec![$($values),*].try_into().unwrap()
285		}
286	};
287	( $value:expr ; $repetition:expr ) => {
288		{
289			$crate::alloc::vec![$value ; $repetition].try_into().unwrap()
290		}
291	}
292}
293
294/// Build a bounded btree-map from the given literals.
295///
296/// The type of the outcome must be known.
297///
298/// Will not handle any errors and just panic if the given literals cannot fit in the corresponding
299/// bounded vec type. Thus, this is only suitable for testing and non-consensus code.
300#[macro_export]
301#[cfg(feature = "std")]
302macro_rules! bounded_btree_map {
303	($ ( $key:expr => $value:expr ),* $(,)?) => {
304		{
305			$crate::TryCollect::<$crate::BoundedBTreeMap<_, _, _>>::try_collect(
306				$crate::alloc::vec![$(($key, $value)),*].into_iter()
307			).unwrap()
308		}
309	};
310}