odra_core/
external.rs

1use core::{
2    cell::OnceCell,
3    ops::{Deref, DerefMut}
4};
5
6use crate::{contract_env::ContractRef, prelude::*};
7use crate::{
8    module::{ModuleComponent, ModulePrimitive},
9    ContractEnv
10};
11use alloc::rc::Rc;
12
13/// A module component that is a reference to an external contract.
14///
15/// # Example
16///
17/// ```ignore
18/// use core::ops::DerefMut;
19/// use odra::{Address, External, prelude::*};
20///
21/// #[odra::module]
22/// pub struct Contract {
23///     ext: odra::External<SetterGetterContractRef>
24/// }
25///
26/// #[odra::module]
27/// impl Contract {
28///     pub fn init(&mut self, address: Address) {
29///         self.ext.set(address);
30///     }
31///
32///     /// If a contract implements the set() method, you can't use
33///     /// the deref coercion mechanism, so you call the `External::set()` method.
34///     /// In this case you need to dereference the contract explicitly:
35///     pub fn set(&mut self, value: bool) {
36///         self.ext.deref_mut().set(value);
37///         // or
38///         // DerefMut::deref_mut(&mut self.ext).set(value);
39///     }
40///
41///     /// For any other method, you can use the deref coercion mechanism, and call
42///     /// the method directly on the external instance:
43///     pub fn get(&self) -> bool {
44///         self.ext.get()
45///     }
46/// }
47///
48/// #[odra::external_contract]
49/// pub trait SetterGetter {
50///     fn set(&mut self, value: bool);
51///     fn get(&self) -> bool;
52/// }
53/// ```
54pub struct External<T: ContractRef> {
55    env: Rc<ContractEnv>,
56    value: Var<Address>,
57    contract_ref: OnceCell<T>
58}
59
60impl<T: ContractRef> ModuleComponent for External<T> {
61    /// Creates a new instance of `External` with the given environment and index.
62    fn instance(env: Rc<ContractEnv>, index: u8) -> Self {
63        Self {
64            env: env.clone(),
65            value: Var::instance(env, index),
66            contract_ref: OnceCell::new()
67        }
68    }
69}
70
71impl<T: ContractRef> ModulePrimitive for External<T> {}
72
73impl<T: ContractRef> External<T> {
74    /// Sets the address of the external contract.
75    pub fn set(&mut self, address: Address) {
76        self.value.set(address);
77    }
78
79    fn contract_ref_mut(&mut self) -> &mut T {
80        if self.contract_ref.get().is_none() {
81            let _ = self.contract_ref();
82        }
83        self.contract_ref.get_mut().unwrap()
84    }
85
86    fn contract_ref(&self) -> &T {
87        self.contract_ref.get_or_init(|| {
88            let address = self
89                .value
90                .get_or_revert_with(ExecutionError::MissingAddress);
91            T::new(self.env.clone(), address)
92        })
93    }
94}
95
96impl<T: ContractRef> Deref for External<T> {
97    type Target = T;
98
99    fn deref(&self) -> &Self::Target {
100        self.contract_ref()
101    }
102}
103
104impl<T: ContractRef> DerefMut for External<T> {
105    fn deref_mut(&mut self) -> &mut Self::Target {
106        self.contract_ref_mut()
107    }
108}