Skip to main content

subsoil/externalities/
extensions.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Externalities extensions storage.
8//!
9//! Externalities support to register a wide variety custom extensions. The [`Extensions`] provides
10//! some convenience functionality to store and retrieve these extensions.
11//!
12//! It is required that each extension implements the [`Extension`] trait.
13
14use super::Error;
15use alloc::{
16	boxed::Box,
17	collections::btree_map::{BTreeMap, Entry},
18};
19use core::{
20	any::{Any, TypeId},
21	iter::FromIterator,
22	ops::DerefMut,
23};
24
25/// Informs [`Extension`] about what type of transaction is started, committed or rolled back.
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum TransactionType {
28	/// A transaction started by the host.
29	Host,
30	/// A transaction started by the runtime.
31	Runtime,
32}
33
34impl TransactionType {
35	/// Is `self` set to [`Self::Host`].
36	pub fn is_host(self) -> bool {
37		matches!(self, Self::Host)
38	}
39
40	/// Is `self` set to [`Self::Runtime`].
41	pub fn is_runtime(self) -> bool {
42		matches!(self, Self::Runtime)
43	}
44}
45
46/// Marker trait for types that should be registered as [`Externalities`](crate::externalities::Externalities)
47/// extension.
48///
49/// As extensions are stored as `Box<Any>`, this trait should give more confidence that the correct
50/// type is registered and requested.
51pub trait Extension: Send + 'static {
52	/// Return the extension as `&mut dyn Any`.
53	///
54	/// This is a trick to make the trait type castable into an [`Any`].
55	fn as_mut_any(&mut self) -> &mut dyn Any;
56
57	/// Get the [`TypeId`] of this `Extension`.
58	fn type_id(&self) -> TypeId;
59
60	/// Start a transaction of type `ty`.
61	fn start_transaction(&mut self, ty: TransactionType) {
62		let _ty = ty;
63	}
64
65	/// Commit a transaction of type `ty`.
66	fn commit_transaction(&mut self, ty: TransactionType) {
67		let _ty = ty;
68	}
69
70	/// Rollback a transaction of type `ty`.
71	fn rollback_transaction(&mut self, ty: TransactionType) {
72		let _ty = ty;
73	}
74}
75
76impl Extension for Box<dyn Extension> {
77	fn as_mut_any(&mut self) -> &mut dyn Any {
78		(**self).as_mut_any()
79	}
80
81	fn type_id(&self) -> TypeId {
82		(**self).type_id()
83	}
84
85	fn start_transaction(&mut self, ty: TransactionType) {
86		(**self).start_transaction(ty);
87	}
88
89	fn commit_transaction(&mut self, ty: TransactionType) {
90		(**self).commit_transaction(ty);
91	}
92
93	fn rollback_transaction(&mut self, ty: TransactionType) {
94		(**self).rollback_transaction(ty);
95	}
96}
97
98/// Macro for declaring an extension that usable with [`Extensions`].
99///
100/// The extension will be an unit wrapper struct that implements [`Extension`], `Deref` and
101/// `DerefMut`. The wrapped type is given by the user.
102///
103/// # Example
104/// ```
105/// # use subsoil::decl_extension;
106/// decl_extension! {
107///     /// Some test extension
108///     struct TestExt(String);
109/// }
110/// ```
111///
112/// The [`Extension`] trait provides hooks that are called when starting, committing or rolling back
113/// a transaction. These can be implemented with the macro as well:
114/// ```
115/// # use subsoil::{decl_extension, externalities::TransactionType};
116/// decl_extension! {
117///     /// Some test extension
118///     struct TestExtWithCallback(String);
119///
120///     impl TestExtWithCallback {
121///         fn start_transaction(&mut self, ty: TransactionType) {
122///             // do something cool
123///         }
124///
125///         // The other methods `commit_transaction` and `rollback_transaction` can also
126///         // be implemented in the same way.
127///     }
128/// }
129/// ```
130#[macro_export]
131macro_rules! decl_extension {
132	(
133		$( #[ $attr:meta ] )*
134		$vis:vis struct $ext_name:ident ($inner:ty);
135		$(
136			impl $ext_name_impl:ident {
137				$(
138					$impls:tt
139				)*
140			}
141		)*
142	) => {
143		$( #[ $attr ] )*
144		$vis struct $ext_name (pub $inner);
145
146		impl $crate::externalities::Extension for $ext_name {
147			fn as_mut_any(&mut self) -> &mut dyn core::any::Any {
148				self
149			}
150
151			fn type_id(&self) -> core::any::TypeId {
152				core::any::Any::type_id(self)
153			}
154
155			$(
156				$(
157					$impls
158				)*
159			)*
160		}
161
162		impl $ext_name {
163			/// Returns the `TypeId` of this extension.
164			#[allow(dead_code)]
165			pub fn type_id() -> core::any::TypeId {
166				core::any::TypeId::of::<Self>()
167			}
168		}
169
170		impl core::ops::Deref for $ext_name {
171			type Target = $inner;
172
173			fn deref(&self) -> &Self::Target {
174				&self.0
175			}
176		}
177
178		impl core::ops::DerefMut for $ext_name {
179			fn deref_mut(&mut self) -> &mut Self::Target {
180				&mut self.0
181			}
182		}
183
184		impl From<$inner> for $ext_name {
185			fn from(inner: $inner) -> Self {
186				Self(inner)
187			}
188 		}
189	};
190	(
191		$( #[ $attr:meta ] )*
192		$vis:vis struct $ext_name:ident;
193	) => {
194		$( #[ $attr ] )*
195		$vis struct $ext_name;
196
197		impl $crate::externalities::Extension for $ext_name {
198			fn as_mut_any(&mut self) -> &mut dyn core::any::Any {
199				self
200			}
201
202			fn type_id(&self) -> core::any::TypeId {
203				core::any::Any::type_id(self)
204			}
205		}
206
207		impl $ext_name {
208			/// Returns the `TypeId` of this extension.
209			#[allow(dead_code)]
210			pub fn type_id() -> core::any::TypeId {
211				core::any::TypeId::of::<Self>()
212			}
213		}
214	}
215}
216
217/// Something that provides access to the [`Extensions`] store.
218///
219/// This is a super trait of the [`Externalities`](crate::externalities::Externalities).
220pub trait ExtensionStore {
221	/// Tries to find a registered extension by the given `type_id` and returns it as a `&mut dyn
222	/// Any`.
223	///
224	/// It is advised to use [`ExternalitiesExt::extension`](super::ExternalitiesExt::extension)
225	/// instead of this function to get type system support and automatic type downcasting.
226	fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any>;
227
228	/// Register extension `extension` with specified `type_id`.
229	///
230	/// It should return error if extension is already registered.
231	fn register_extension_with_type_id(
232		&mut self,
233		type_id: TypeId,
234		extension: Box<dyn Extension>,
235	) -> Result<(), Error>;
236
237	/// Deregister extension with specified 'type_id' and drop it.
238	///
239	/// It should return error if extension is not registered.
240	fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), Error>;
241}
242
243/// Stores extensions that should be made available through the externalities.
244#[derive(Default)]
245pub struct Extensions {
246	extensions: BTreeMap<TypeId, Box<dyn Extension>>,
247}
248
249impl core::fmt::Debug for Extensions {
250	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251		write!(f, "Extensions: ({})", self.extensions.len())
252	}
253}
254
255impl Extensions {
256	/// Create new instance of `Self`.
257	pub fn new() -> Self {
258		Self::default()
259	}
260
261	/// Register the given extension.
262	pub fn register<E: Extension>(&mut self, ext: E) {
263		let type_id = ext.type_id();
264		self.extensions.insert(type_id, Box::new(ext));
265	}
266
267	/// Returns `true` if an extension for the given `type_id` is already registered.
268	pub fn is_registered(&self, type_id: TypeId) -> bool {
269		self.extensions.contains_key(&type_id)
270	}
271
272	/// Register extension `extension` using the given `type_id`.
273	pub fn register_with_type_id(
274		&mut self,
275		type_id: TypeId,
276		extension: Box<dyn Extension>,
277	) -> Result<(), Error> {
278		match self.extensions.entry(type_id) {
279			Entry::Vacant(vacant) => {
280				vacant.insert(extension);
281				Ok(())
282			},
283			Entry::Occupied(_) => Err(Error::ExtensionAlreadyRegistered),
284		}
285	}
286
287	/// Return a mutable reference to the requested extension.
288	pub fn get_mut(&mut self, ext_type_id: TypeId) -> Option<&mut dyn Any> {
289		self.extensions
290			.get_mut(&ext_type_id)
291			.map(DerefMut::deref_mut)
292			.map(Extension::as_mut_any)
293	}
294
295	/// Deregister extension for the given `type_id`.
296	///
297	/// Returns `true` when the extension was registered.
298	pub fn deregister(&mut self, type_id: TypeId) -> bool {
299		self.extensions.remove(&type_id).is_some()
300	}
301
302	/// Returns a mutable iterator over all extensions.
303	pub fn iter_mut(&mut self) -> impl Iterator<Item = (&TypeId, &mut Box<dyn Extension>)> {
304		self.extensions.iter_mut()
305	}
306
307	/// Merge `other` into `self`.
308	///
309	/// If both contain the same extension, the extension instance of `other` will overwrite the
310	/// instance found in `self`.
311	pub fn merge(&mut self, other: Self) {
312		self.extensions.extend(other.extensions);
313	}
314
315	/// Start a transaction of type `ty`.
316	pub fn start_transaction(&mut self, ty: TransactionType) {
317		self.extensions.values_mut().for_each(|e| e.start_transaction(ty));
318	}
319
320	/// Commit a transaction of type `ty`.
321	pub fn commit_transaction(&mut self, ty: TransactionType) {
322		self.extensions.values_mut().for_each(|e| e.commit_transaction(ty));
323	}
324
325	/// Rollback a transaction of type `ty`.
326	pub fn rollback_transaction(&mut self, ty: TransactionType) {
327		self.extensions.values_mut().for_each(|e| e.rollback_transaction(ty));
328	}
329
330	/// Returns an iterator that returns all stored extensions.
331	pub fn into_extensions(self) -> impl Iterator<Item = Box<dyn Extension>> {
332		self.extensions.into_values()
333	}
334}
335
336impl Extend<Extensions> for Extensions {
337	fn extend<T: IntoIterator<Item = Extensions>>(&mut self, iter: T) {
338		iter.into_iter()
339			.for_each(|ext| self.extensions.extend(ext.extensions.into_iter()));
340	}
341}
342
343impl<A: Extension> From<A> for Extensions {
344	fn from(ext: A) -> Self {
345		Self {
346			extensions: FromIterator::from_iter(
347				[(Extension::type_id(&ext), Box::new(ext) as Box<dyn Extension>)].into_iter(),
348			),
349		}
350	}
351}
352
353impl<A: Extension, B: Extension> From<(A, B)> for Extensions {
354	fn from((ext, ext2): (A, B)) -> Self {
355		Self {
356			extensions: FromIterator::from_iter(
357				[
358					(Extension::type_id(&ext), Box::new(ext) as Box<dyn Extension>),
359					(Extension::type_id(&ext2), Box::new(ext2) as Box<dyn Extension>),
360				]
361				.into_iter(),
362			),
363		}
364	}
365}
366
367impl<A: Extension, B: Extension, C: Extension> From<(A, B, C)> for Extensions {
368	fn from((ext, ext2, ext3): (A, B, C)) -> Self {
369		Self {
370			extensions: FromIterator::from_iter(
371				[
372					(Extension::type_id(&ext), Box::new(ext) as Box<dyn Extension>),
373					(Extension::type_id(&ext2), Box::new(ext2) as Box<dyn Extension>),
374					(Extension::type_id(&ext3), Box::new(ext3) as Box<dyn Extension>),
375				]
376				.into_iter(),
377			),
378		}
379	}
380}
381
382#[cfg(test)]
383mod tests {
384	use super::*;
385
386	decl_extension! {
387		struct DummyExt(u32);
388	}
389	decl_extension! {
390		struct DummyExt2(u32);
391	}
392
393	#[test]
394	fn register_and_retrieve_extension() {
395		let mut exts = Extensions::new();
396		exts.register(DummyExt(1));
397		exts.register(DummyExt2(2));
398
399		let ext = exts.get_mut(TypeId::of::<DummyExt>()).expect("Extension is registered");
400		let ext_ty = ext.downcast_mut::<DummyExt>().expect("Downcasting works");
401
402		assert_eq!(ext_ty.0, 1);
403	}
404
405	#[test]
406	fn register_box_extension() {
407		let mut exts = Extensions::new();
408		let box1: Box<dyn Extension> = Box::new(DummyExt(1));
409		let box2: Box<dyn Extension> = Box::new(DummyExt2(2));
410		exts.register(box1);
411		exts.register(box2);
412
413		{
414			let ext = exts.get_mut(TypeId::of::<DummyExt>()).expect("Extension 1 is registered");
415			let ext_ty = ext.downcast_mut::<DummyExt>().expect("Downcasting works for Extension 1");
416			assert_eq!(ext_ty.0, 1);
417		}
418		{
419			let ext2 = exts.get_mut(TypeId::of::<DummyExt2>()).expect("Extension 2 is registered");
420			let ext_ty2 =
421				ext2.downcast_mut::<DummyExt2>().expect("Downcasting works for Extension 2");
422			assert_eq!(ext_ty2.0, 2);
423		}
424	}
425
426	#[test]
427	fn from_boxed_extensions() {
428		let exts = Extensions::from((DummyExt(1), DummyExt2(2)));
429
430		assert!(exts.is_registered(DummyExt::type_id()));
431		assert!(exts.is_registered(DummyExt2::type_id()));
432	}
433}