near_sdk_contract_tools/
hook.rs

1//! # Hooks
2//!
3//! Hooks are a way to wrap (inject code before and after) component functions.
4//!
5//! Most of the time, hooks are used to implement cross-cutting concerns, such as
6//! logging, accounting, or integration with other components.
7//!
8//! ## Example
9//!
10//! ```
11//! use near_sdk::{env, log, near, PanicOnDefault};
12//! use near_sdk_contract_tools::{hook::Hook, standard::nep141::*, Nep141};
13//!
14//! pub struct MyTransferHook;
15//!
16//! impl Hook<MyContract, Nep141Transfer<'_>> for MyTransferHook {
17//!     fn hook<R>(contract: &mut MyContract, transfer: &Nep141Transfer<'_>, f: impl FnOnce(&mut MyContract) -> R) -> R {
18//!         // Log, check preconditions, save state, etc.
19//!         log!("NEP-141 transfer from {} to {} of {} tokens", transfer.sender_id, transfer.receiver_id, transfer.amount);
20//!
21//!         let storage_usage_before = env::storage_usage();
22//!
23//!         let r = f(contract); // execute wrapped function
24//!
25//!         let storage_usage_after = env::storage_usage();
26//!         log!("Storage delta: {}", storage_usage_after - storage_usage_before);
27//!
28//!         r
29//!     }
30//! }
31//!
32//! #[derive(Nep141, PanicOnDefault)]
33//! #[nep141(transfer_hook = "MyTransferHook")]
34//! #[near(contract_state)]
35//! struct MyContract {}
36//! ```
37
38/// Generic hook trait for injecting code before and after component functions.
39pub trait Hook<C, A = ()> {
40    /// Execute a function with hooks.
41    fn hook<R>(contract: &mut C, _args: &A, f: impl FnOnce(&mut C) -> R) -> R {
42        f(contract)
43    }
44}
45
46impl<C, A> Hook<C, A> for () {}
47
48impl<C, A, T, U> Hook<C, A> for (T, U)
49where
50    T: Hook<C, A>,
51    U: Hook<C, A>,
52{
53    fn hook<R>(contract: &mut C, args: &A, f: impl FnOnce(&mut C) -> R) -> R {
54        T::hook(contract, args, |contract| U::hook(contract, args, f))
55    }
56}