Skip to content

Commit 3b6a195

Browse files
add contract state nesting
Signed-off-by: Valentyn Faychuk <valy@faychuk.com>
1 parent 1c7708c commit 3b6a195

3 files changed

Lines changed: 113 additions & 26 deletions

File tree

contract_samples/rust/examples/counter_macros.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ use alloc::string::ToString;
77
use amadeus_sdk::*;
88

99
#[contract_state]
10-
struct SimpleCounter {
11-
count: i128,
10+
struct Metadata {
1211
owner: Vec<u8>,
12+
created_at: i128,
13+
}
14+
15+
#[contract_state]
16+
struct Counter {
17+
count: i128,
18+
#[nested]
19+
metadata: Metadata,
1320
}
1421

1522
#[contract]
16-
impl SimpleCounter {
23+
impl Counter {
1724
pub fn init(&mut self) {
18-
*self.owner = account_current();
25+
*self.metadata.owner = account_current();
26+
*self.metadata.created_at = entry_slot() as i128;
1927
*self.count = 0;
20-
log("Counter initialized");
2128
}
2229

2330
pub fn increment(&mut self, amount: Vec<u8>) {
@@ -30,7 +37,7 @@ impl SimpleCounter {
3037

3138
pub fn reset(&mut self) {
3239
let caller = account_caller();
33-
amadeus_sdk::assert!(*self.owner == caller, "unauthorized");
40+
amadeus_sdk::assert!(*self.metadata.owner == caller, "unauthorized");
3441
*self.count = 0;
3542
}
3643
}

contract_samples/rust/sdk-macros/src/lib.rs

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,64 @@ pub fn contract_state(_attr: TokenStream, item: TokenStream) -> TokenStream {
1313
return TokenStream::from(quote! { #input });
1414
};
1515

16+
let is_nested = |f: &syn::Field| -> bool {
17+
f.attrs.iter().any(|attr| attr.path().is_ident("nested"))
18+
};
19+
1620
let transformed_fields = fields.named.iter().map(|f| {
1721
let field_name = &f.ident;
1822
let field_vis = &f.vis;
19-
let field_attrs = &f.attrs;
2023
let field_ty = &f.ty;
21-
quote! {
22-
#(#field_attrs)*
23-
#field_vis #field_name: LazyCell<#field_ty>
24+
let filtered_attrs: Vec<_> = f.attrs.iter()
25+
.filter(|attr| !attr.path().is_ident("nested"))
26+
.collect();
27+
28+
if is_nested(f) {
29+
quote! {
30+
#(#filtered_attrs)*
31+
#field_vis #field_name: #field_ty
32+
}
33+
} else {
34+
quote! {
35+
#(#filtered_attrs)*
36+
#field_vis #field_name: LazyCell<#field_ty>
37+
}
2438
}
2539
});
2640

2741
let init_calls = fields.named.iter().map(|f| {
2842
let field = f.ident.as_ref().unwrap();
2943
let key = field.to_string();
30-
quote! { self.#field = LazyCell::new(#key.as_bytes().to_vec()); }
44+
45+
if is_nested(f) {
46+
quote! {
47+
let mut key = prefix.clone();
48+
key.extend_from_slice(#key.as_bytes());
49+
key.push(b':');
50+
self.#field.__init_lazy_fields(key);
51+
}
52+
} else {
53+
quote! {
54+
let mut key = prefix.clone();
55+
key.extend_from_slice(#key.as_bytes());
56+
self.#field = LazyCell::new(key);
57+
}
58+
}
3159
});
3260

3361
let flush_calls = fields.named.iter().map(|f| {
3462
let field = f.ident.as_ref().unwrap();
35-
quote! { self.#field.flush(); }
63+
64+
if is_nested(f) {
65+
quote! { self.#field.__flush_lazy_fields(); }
66+
} else {
67+
quote! { self.#field.flush(); }
68+
}
3669
});
3770

3871
let default_fields = fields.named.iter().map(|f| {
3972
let field_name = &f.ident;
40-
quote! { #field_name: LazyCell::default() }
73+
quote! { #field_name: Default::default() }
4174
});
4275

4376
TokenStream::from(quote! {
@@ -52,9 +85,14 @@ pub fn contract_state(_attr: TokenStream, item: TokenStream) -> TokenStream {
5285
}
5386
}
5487

55-
impl #name {
56-
pub fn __init_lazy_fields(&mut self) { #(#init_calls)* }
57-
pub fn __flush_lazy_fields(&self) { #(#flush_calls)* }
88+
impl ContractState for #name {
89+
fn __init_lazy_fields(&mut self, prefix: alloc::vec::Vec<u8>) {
90+
#(#init_calls)*
91+
}
92+
93+
fn __flush_lazy_fields(&self) {
94+
#(#flush_calls)*
95+
}
5896
}
5997
})
6098
}
@@ -123,7 +161,7 @@ fn handle_impl_block(impl_block: ItemImpl) -> TokenStream {
123161
quote! {
124162
#(#deserializations)*
125163
let mut state = #self_ty::default();
126-
state.__init_lazy_fields();
164+
state.__init_lazy_fields(alloc::vec::Vec::new());
127165
let result = state.#call;
128166
state.__flush_lazy_fields();
129167
ret(result);
@@ -132,7 +170,7 @@ fn handle_impl_block(impl_block: ItemImpl) -> TokenStream {
132170
quote! {
133171
#(#deserializations)*
134172
let mut state = #self_ty::default();
135-
state.__init_lazy_fields();
173+
state.__init_lazy_fields(alloc::vec::Vec::new());
136174
state.#call;
137175
state.__flush_lazy_fields();
138176
}

contract_samples/rust/sdk/src/lib.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ pub use storage::*;
1010
pub use encoding::*;
1111
pub use amadeus_sdk_macros::{contract, contract_state};
1212

13+
pub trait ContractState {
14+
fn __init_lazy_fields(&mut self, prefix: Vec<u8>);
15+
fn __flush_lazy_fields(&self);
16+
}
17+
1318
use core::panic::PanicInfo;
1419

1520
use alloc::{borrow::Cow, vec::Vec, string::String, string::ToString};
@@ -141,6 +146,7 @@ where
141146
}
142147
}
143148

149+
144150
impl<T> Default for LazyCell<T> {
145151
fn default() -> Self {
146152
Self::new(Vec::new())
@@ -156,14 +162,6 @@ impl<T> LazyCell<T> {
156162
}
157163
}
158164

159-
pub fn flush(&self) where T: Payload + Clone {
160-
if self.dirty.get() {
161-
if let Some(val) = self.value.borrow().as_ref() {
162-
kv_put(&self.key, val.clone());
163-
}
164-
}
165-
}
166-
167165
pub fn get(&self) -> T where T: FromKvBytes + Default + Clone {
168166
if self.value.borrow().is_none() {
169167
let loaded = kv_get::<T>(&self.key).unwrap_or_default();
@@ -188,3 +186,47 @@ impl<T> LazyCell<T> {
188186
self.set(current + amount);
189187
}
190188
}
189+
190+
impl<T: Payload + Clone> LazyCell<T> {
191+
pub fn flush(&self) {
192+
if self.dirty.get() {
193+
if let Some(val) = self.value.borrow().as_ref() {
194+
kv_put(&self.key, val.clone());
195+
}
196+
}
197+
}
198+
}
199+
200+
impl<T: ContractState + Default> LazyCell<T> {
201+
202+
pub fn with_mut<F, R>(&mut self, f: F) -> R
203+
where
204+
F: FnOnce(&mut T) -> R
205+
{
206+
if self.value.borrow().is_none() {
207+
let mut loaded = T::default();
208+
loaded.__init_lazy_fields(self.key.clone());
209+
*self.value.borrow_mut() = Some(loaded);
210+
}
211+
self.dirty.set(true);
212+
unsafe {
213+
let ptr = self.value.as_ptr();
214+
f((*ptr).as_mut().unwrap())
215+
}
216+
}
217+
218+
pub fn with<F, R>(&self, f: F) -> R
219+
where
220+
F: FnOnce(&T) -> R
221+
{
222+
if self.value.borrow().is_none() {
223+
let mut loaded = T::default();
224+
loaded.__init_lazy_fields(self.key.clone());
225+
*self.value.borrow_mut() = Some(loaded);
226+
}
227+
unsafe {
228+
let ptr = self.value.as_ptr();
229+
f((*ptr).as_ref().unwrap())
230+
}
231+
}
232+
}

0 commit comments

Comments
 (0)