referrerpolicy=no-referrer-when-downgrade

frame_support/traits/
filter.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Traits and associated utilities for dealing with abstract constraint filters.
19
20pub use super::members::Contains;
21use core::marker::PhantomData;
22
23/// Trait to add a constraint onto the filter.
24pub trait FilterStack<T>: Contains<T> {
25	/// The type used to archive the stack.
26	type Stack;
27
28	/// Add a new `constraint` onto the filter.
29	fn push(constraint: impl Fn(&T) -> bool + 'static);
30
31	/// Removes the most recently pushed, and not-yet-popped, constraint from the filter.
32	fn pop();
33
34	/// Clear the filter, returning a value that may be used later to `restore` it.
35	fn take() -> Self::Stack;
36
37	/// Restore the filter from a previous `take` operation.
38	fn restore(taken: Self::Stack);
39}
40
41/// Guard type for pushing a constraint to a `FilterStack` and popping when dropped.
42pub struct FilterStackGuard<F: FilterStack<T>, T>(PhantomData<(F, T)>);
43
44/// Guard type for clearing all pushed constraints from a `FilterStack` and reinstating them when
45/// dropped.
46pub struct ClearFilterGuard<F: FilterStack<T>, T>(Option<F::Stack>, PhantomData<T>);
47
48impl<F: FilterStack<T>, T> FilterStackGuard<F, T> {
49	/// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when
50	/// this instance is dropped.
51	pub fn new(constraint: impl Fn(&T) -> bool + 'static) -> Self {
52		F::push(constraint);
53		Self(PhantomData)
54	}
55}
56
57impl<F: FilterStack<T>, T> Drop for FilterStackGuard<F, T> {
58	fn drop(&mut self) {
59		F::pop();
60	}
61}
62
63impl<F: FilterStack<T>, T> ClearFilterGuard<F, T> {
64	/// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when
65	/// this instance is dropped.
66	pub fn new() -> Self {
67		Self(Some(F::take()), PhantomData)
68	}
69}
70
71impl<F: FilterStack<T>, T> Drop for ClearFilterGuard<F, T> {
72	fn drop(&mut self) {
73		if let Some(taken) = self.0.take() {
74			F::restore(taken);
75		}
76	}
77}
78
79/// Simple trait for providing a filter over a reference to some type, given an instance of itself.
80pub trait InstanceFilter<T>: Sized + Send + Sync {
81	/// Determine if a given value should be allowed through the filter (returns `true`) or not.
82	fn filter(&self, _: &T) -> bool;
83
84	/// Determines whether `self` matches at least everything that `_o` does.
85	fn is_superset(&self, _o: &Self) -> bool {
86		false
87	}
88}
89
90impl<T> InstanceFilter<T> for () {
91	fn filter(&self, _: &T) -> bool {
92		true
93	}
94	fn is_superset(&self, _o: &Self) -> bool {
95		true
96	}
97}
98
99#[macro_export]
100macro_rules! impl_filter_stack {
101	($target:ty, $base:ty, $call:ty, $module:ident) => {
102		#[cfg(feature = "std")]
103		mod $module {
104			#[allow(unused_imports)]
105			use super::*;
106			use std::{boxed::Box, cell::RefCell, mem::{swap, take}, vec::Vec};
107			use $crate::traits::filter::{Contains, FilterStack};
108
109			thread_local! {
110				static FILTER: RefCell<Vec<Box<dyn Fn(&$call) -> bool + 'static>>> = RefCell::new(Vec::new());
111			}
112
113			impl Contains<$call> for $target {
114				fn contains(call: &$call) -> bool {
115					<$base>::contains(call) &&
116						FILTER.with(|filter| filter.borrow().iter().all(|f| f(call)))
117				}
118			}
119
120			impl FilterStack<$call> for $target {
121				type Stack = Vec<Box<dyn Fn(&$call) -> bool + 'static>>;
122				fn push(f: impl Fn(&$call) -> bool + 'static) {
123					FILTER.with(|filter| filter.borrow_mut().push(Box::new(f)));
124				}
125				fn pop() {
126					FILTER.with(|filter| filter.borrow_mut().pop());
127				}
128				fn take() -> Self::Stack {
129					FILTER.with(|filter| take(filter.borrow_mut().as_mut()))
130				}
131				fn restore(mut s: Self::Stack) {
132					FILTER.with(|filter| swap(filter.borrow_mut().as_mut(), &mut s));
133				}
134			}
135		}
136
137		#[cfg(not(feature = "std"))]
138		mod $module {
139			#[allow(unused_imports)]
140			use super::*;
141			use $crate::traits::{swap, take, RefCell, Vec, Box, Contains, FilterStack};
142
143			struct ThisFilter(RefCell<Vec<Box<dyn Fn(&$call) -> bool + 'static>>>);
144			// NOTE: Safe only in wasm (guarded above) because there's only one thread.
145			unsafe impl Send for ThisFilter {}
146			unsafe impl Sync for ThisFilter {}
147
148			static FILTER: ThisFilter = ThisFilter(RefCell::new(Vec::new()));
149
150			impl Contains<$call> for $target {
151				fn contains(call: &$call) -> bool {
152					<$base>::contains(call) && FILTER.0.borrow().iter().all(|f| f(call))
153				}
154			}
155
156			impl FilterStack<$call> for $target {
157				type Stack = Vec<Box<dyn Fn(&$call) -> bool + 'static>>;
158				fn push(f: impl Fn(&$call) -> bool + 'static) {
159					FILTER.0.borrow_mut().push(Box::new(f));
160				}
161				fn pop() {
162					FILTER.0.borrow_mut().pop();
163				}
164				fn take() -> Self::Stack {
165					take(FILTER.0.borrow_mut().as_mut())
166				}
167				fn restore(mut s: Self::Stack) {
168					swap(FILTER.0.borrow_mut().as_mut(), &mut s);
169				}
170			}
171		}
172	}
173}
174
175#[cfg(test)]
176pub mod test_impl_filter_stack {
177	use super::*;
178
179	pub struct IsCallable;
180	pub struct BaseFilter;
181	impl Contains<u32> for BaseFilter {
182		fn contains(x: &u32) -> bool {
183			x % 2 == 0
184		}
185	}
186	impl_filter_stack!(
187		crate::traits::filter::test_impl_filter_stack::IsCallable,
188		crate::traits::filter::test_impl_filter_stack::BaseFilter,
189		u32,
190		is_callable
191	);
192
193	#[test]
194	fn impl_filter_stack_should_work() {
195		assert!(IsCallable::contains(&36));
196		assert!(IsCallable::contains(&40));
197		assert!(IsCallable::contains(&42));
198		assert!(!IsCallable::contains(&43));
199
200		IsCallable::push(|x| *x < 42);
201		assert!(IsCallable::contains(&36));
202		assert!(IsCallable::contains(&40));
203		assert!(!IsCallable::contains(&42));
204
205		IsCallable::push(|x| *x % 3 == 0);
206		assert!(IsCallable::contains(&36));
207		assert!(!IsCallable::contains(&40));
208
209		IsCallable::pop();
210		assert!(IsCallable::contains(&36));
211		assert!(IsCallable::contains(&40));
212		assert!(!IsCallable::contains(&42));
213
214		let saved = IsCallable::take();
215		assert!(IsCallable::contains(&36));
216		assert!(IsCallable::contains(&40));
217		assert!(IsCallable::contains(&42));
218		assert!(!IsCallable::contains(&43));
219
220		IsCallable::restore(saved);
221		assert!(IsCallable::contains(&36));
222		assert!(IsCallable::contains(&40));
223		assert!(!IsCallable::contains(&42));
224
225		IsCallable::pop();
226		assert!(IsCallable::contains(&36));
227		assert!(IsCallable::contains(&40));
228		assert!(IsCallable::contains(&42));
229		assert!(!IsCallable::contains(&43));
230	}
231
232	#[test]
233	fn guards_should_work() {
234		assert!(IsCallable::contains(&36));
235		assert!(IsCallable::contains(&40));
236		assert!(IsCallable::contains(&42));
237		assert!(!IsCallable::contains(&43));
238		{
239			let _guard_1 = FilterStackGuard::<IsCallable, u32>::new(|x| *x < 42);
240			assert!(IsCallable::contains(&36));
241			assert!(IsCallable::contains(&40));
242			assert!(!IsCallable::contains(&42));
243			{
244				let _guard_2 = FilterStackGuard::<IsCallable, u32>::new(|x| *x % 3 == 0);
245				assert!(IsCallable::contains(&36));
246				assert!(!IsCallable::contains(&40));
247			}
248			assert!(IsCallable::contains(&36));
249			assert!(IsCallable::contains(&40));
250			assert!(!IsCallable::contains(&42));
251			{
252				let _guard_2 = ClearFilterGuard::<IsCallable, u32>::new();
253				assert!(IsCallable::contains(&36));
254				assert!(IsCallable::contains(&40));
255				assert!(IsCallable::contains(&42));
256				assert!(!IsCallable::contains(&43));
257			}
258			assert!(IsCallable::contains(&36));
259			assert!(IsCallable::contains(&40));
260			assert!(!IsCallable::contains(&42));
261		}
262		assert!(IsCallable::contains(&36));
263		assert!(IsCallable::contains(&40));
264		assert!(IsCallable::contains(&42));
265		assert!(!IsCallable::contains(&43));
266	}
267}