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}