cougr_core/standards/
execution_guard.rs1use soroban_sdk::{contracttype, Env, Symbol};
2
3use super::error::StandardsError;
4
5const EXECUTION_LOCK_PREFIX: &str = "std_exec";
6
7#[derive(Clone, Debug)]
9pub struct ExecutionGuard {
10 id: Symbol,
11}
12
13#[contracttype]
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct ExecutionGuardEnteredEvent {
16 pub guard_id: Symbol,
17}
18
19#[contracttype]
20#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct ExecutionGuardExitedEvent {
22 pub guard_id: Symbol,
23}
24
25impl ExecutionGuard {
26 pub fn new(id: Symbol) -> Self {
27 Self { id }
28 }
29
30 pub fn is_locked(&self, env: &Env) -> bool {
31 env.storage()
32 .persistent()
33 .get(&self.lock_key(env))
34 .unwrap_or(false)
35 }
36
37 pub fn require_unlocked(&self, env: &Env) -> Result<(), StandardsError> {
38 if self.is_locked(env) {
39 return Err(StandardsError::ExecutionLocked);
40 }
41 Ok(())
42 }
43
44 pub fn enter(&self, env: &Env) -> Result<ExecutionGuardEnteredEvent, StandardsError> {
45 self.require_unlocked(env)?;
46 env.storage().persistent().set(&self.lock_key(env), &true);
47 Ok(ExecutionGuardEnteredEvent {
48 guard_id: self.id.clone(),
49 })
50 }
51
52 pub fn exit(&self, env: &Env) -> Result<ExecutionGuardExitedEvent, StandardsError> {
53 if !self.is_locked(env) {
54 return Err(StandardsError::ExecutionNotLocked);
55 }
56 env.storage().persistent().set(&self.lock_key(env), &false);
57 Ok(ExecutionGuardExitedEvent {
58 guard_id: self.id.clone(),
59 })
60 }
61
62 pub fn execute<T>(&self, env: &Env, f: impl FnOnce() -> T) -> Result<T, StandardsError> {
63 self.enter(env)?;
64 let result = f();
65 self.exit(env)?;
66 Ok(result)
67 }
68
69 fn lock_key(&self, env: &Env) -> (Symbol, Symbol) {
70 (Symbol::new(env, EXECUTION_LOCK_PREFIX), self.id.clone())
71 }
72}