diff --git a/core/src/lib.rs b/core/src/lib.rs index b0315eb5..213d9dc1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -47,7 +47,7 @@ pub use unwrap_or_revert::UnwrapOrRevert; pub use utils::serialize; pub use casper_types; -pub use casper_types::bytesrepr::{Bytes, FromBytes, ToBytes}; +pub use casper_types::bytesrepr::{Bytes, Error as BytesReprError, FromBytes, ToBytes}; pub use casper_types::CLValue; pub use casper_types::{runtime_args, RuntimeArgs}; pub use casper_types::{CLType, CLTyped, PublicKey, SecretKey, U128, U256, U512}; diff --git a/core/src/variable.rs b/core/src/variable.rs index 58db4eba..6e79d92a 100644 --- a/core/src/variable.rs +++ b/core/src/variable.rs @@ -25,12 +25,7 @@ impl Variable { } } -impl Variable { - pub fn get_or_default(&self) -> T { - let env = self.env(); - env.get_value(&env.current_key()).unwrap_or_default() - } - +impl Variable { pub fn get(&self) -> Option { let env = self.env(); env.get_value(&env.current_key()) @@ -38,8 +33,13 @@ impl Variable { pub fn get_or_revert_with>(&self, error: E) -> T { let env = self.env(); - env.get_value(&env.current_key()) - .unwrap_or_revert_with(&env, error) + self.get().unwrap_or_revert_with(&env, error) + } +} + +impl Variable { + pub fn get_or_default(&self) -> T { + self.get().unwrap_or_default() } } diff --git a/examples2/src/counter.rs b/examples2/src/counter.rs index e2e878b0..ab711a3a 100644 --- a/examples2/src/counter.rs +++ b/examples2/src/counter.rs @@ -1,100 +1,78 @@ use odra::prelude::*; -use odra::{runtime_args, FromBytes, RuntimeArgs}; -use odra::{CallDef, ContractEnv, HostEnv, Mapping, Variable}; +use odra::{Address, CallDef, ContractEnv, HostEnv, Mapping, Module, OdraType, Variable}; -pub struct Counter { - env: Rc, - count0: Variable, - count1: Variable, - count2: Variable, - count3: Variable, - count4: Variable, - count5: Variable, - count6: Variable, - count7: Variable, - count8: Variable, - count9: Variable, - counts: Mapping +#[derive(OdraType)] +struct MyCounter { + value: u32, + creator: Address } -impl odra::contract_def::HasEvents for Counter { - fn events() -> Vec { - vec![] - } +#[derive(OdraType)] +enum MyEnum { + VariantA, + VariantB, + Unknown +} + +#[odra::module] +pub struct Counter { + count0: Variable, + count1: Variable, + count2: Variable, + count3: Variable, + count4: Variable, + count5: Variable, + count6: Variable, + count7: Variable, + count8: Variable, + count9: Variable, + counts: Mapping } impl Counter { pub fn get_count(&self, index: u8) -> u32 { match index { - 0 => self.count0.get_or_default(), - 1 => self.count1.get_or_default(), - 2 => self.count2.get_or_default(), - 3 => self.count3.get_or_default(), - 4 => self.count4.get_or_default(), - 5 => self.count5.get_or_default(), - 6 => self.count6.get_or_default(), - 7 => self.count7.get_or_default(), - 8 => self.count8.get_or_default(), - 9 => self.count9.get_or_default(), + 0 => self.count0.get().map(|c| c.value).unwrap_or_default(), + 1 => self.count1.get().map(|c| c.value).unwrap_or_default(), + 2 => self.count2.get().map(|c| c.value).unwrap_or_default(), + 3 => self.count3.get().map(|c| c.value).unwrap_or_default(), + 4 => self.count4.get().map(|c| c.value).unwrap_or_default(), + 5 => self.count5.get().map(|c| c.value).unwrap_or_default(), + 6 => self.count6.get().map(|c| c.value).unwrap_or_default(), + 7 => self.count7.get().map(|c| c.value).unwrap_or_default(), + 8 => self.count8.get().map(|c| c.value).unwrap_or_default(), + 9 => self.count9.get().map(|c| c.value).unwrap_or_default(), _ => unreachable!() } } pub fn increment(&mut self, index: u8) { match index { - 0 => increment(&mut self.count0), - 1 => increment(&mut self.count1), - 2 => increment(&mut self.count2), - 3 => increment(&mut self.count3), - 4 => increment(&mut self.count4), - 5 => increment(&mut self.count5), - 6 => increment(&mut self.count6), - 7 => increment(&mut self.count7), - 8 => increment(&mut self.count8), - 9 => increment(&mut self.count9), + 0 => increment(&self.env(), &mut self.count0), + 1 => increment(&self.env(), &mut self.count1), + 2 => increment(&self.env(), &mut self.count2), + 3 => increment(&self.env(), &mut self.count3), + 4 => increment(&self.env(), &mut self.count4), + 5 => increment(&self.env(), &mut self.count5), + 6 => increment(&self.env(), &mut self.count6), + 7 => increment(&self.env(), &mut self.count7), + 8 => increment(&self.env(), &mut self.count8), + 9 => increment(&self.env(), &mut self.count9), _ => unreachable!() }; } } -fn increment(count: &mut Variable) { - let a = count.get_or_default(); - count.set(a + 1); -} - -mod __counter_pack_module { - use super::*; - impl odra::module::Module for Counter { - fn new(env: Rc) -> Self { - let count0 = Variable::new(Rc::clone(&env), 0); - let count1 = Variable::new(Rc::clone(&env), 1); - let count2 = Variable::new(Rc::clone(&env), 2); - let count3 = Variable::new(Rc::clone(&env), 3); - let count4 = Variable::new(Rc::clone(&env), 4); - let count5 = Variable::new(Rc::clone(&env), 5); - let count6 = Variable::new(Rc::clone(&env), 6); - let count7 = Variable::new(Rc::clone(&env), 7); - let count8 = Variable::new(Rc::clone(&env), 8); - let count9 = Variable::new(Rc::clone(&env), 9); - let counts = Mapping::new(Rc::clone(&env), 10); - Self { - env, - count0, - count1, - count2, - count3, - count4, - count5, - count6, - count7, - count8, - count9, - counts - } - } - - fn env(&self) -> Rc { - self.env.clone() - } +fn increment(env: &ContractEnv, count: &mut Variable) { + if let Some(counter) = count.get() { + count.set(MyCounter { + value: counter.value + 1, + creator: counter.creator + }); + } else { + count.set(MyCounter { + value: 1, + creator: env.caller() + }); } } diff --git a/examples2/src/erc20.rs b/examples2/src/erc20.rs index a16a5995..d78bd5b1 100644 --- a/examples2/src/erc20.rs +++ b/examples2/src/erc20.rs @@ -1,7 +1,7 @@ use casper_event_standard::Event; use odra::{casper_event_standard, Bytes, Module, OdraError, PublicKey}; use odra::{prelude::*, CallDef, ModuleWrapper}; -use odra::{Address, ContractEnv, Mapping, Variable, U256, U512}; +use odra::{Address, Mapping, Variable, U256, U512}; #[derive(Event, Eq, PartialEq, Debug)] pub struct OnTransfer { @@ -25,18 +25,12 @@ pub struct OnApprove { pub value: U256 } -#[repr(u16)] +#[derive(OdraError)] pub enum Erc20Error { InsufficientBalance = 1, InsufficientAllowance = 2 } -impl From for OdraError { - fn from(error: Erc20Error) -> Self { - OdraError::user(error as u16) - } -} - #[odra::module(events = [OnTransfer, OnCrossTransfer, OnApprove])] pub struct Erc20 { total_supply: Variable, diff --git a/examples2/src/reentrancy_guard.rs b/examples2/src/reentrancy_guard.rs index 30c4981c..abe9c59b 100644 --- a/examples2/src/reentrancy_guard.rs +++ b/examples2/src/reentrancy_guard.rs @@ -1,8 +1,7 @@ -use odra::{prelude::*, ContractEnv, Variable}; +use odra::{prelude::*, Module, Variable}; #[odra::module] pub struct ReentrancyMock { - env: Rc, counter: Variable } @@ -21,8 +20,8 @@ impl ReentrancyMock { if n > 0 { self.count(); let other_erc20 = ReentrancyMockContractRef { - address: self.env.self_address(), - env: self.env.clone() + address: self.env().self_address(), + env: self.env().clone() } .count_ref_recursive(n - 1); } diff --git a/justfile b/justfile index 62716272..8506abc3 100644 --- a/justfile +++ b/justfile @@ -73,20 +73,20 @@ clean: cd modules && rm -f Cargo.lock build-erc20: - cd examples2 && ODRA_MODULE=Erc20 cargo build --release --target wasm32-unknown-unknown --bin contract - wasm-strip examples2/target/wasm32-unknown-unknown/release/contract.wasm + cd examples2 && ODRA_MODULE=Erc20 cargo build --release --target wasm32-unknown-unknown --bin build_contract + wasm-strip examples2/target/wasm32-unknown-unknown/release/build_contract.wasm rm -rf examples2/wasm/Erc20.wasm mkdir -p examples2/wasm - mv examples2/target/wasm32-unknown-unknown/release/contract.wasm examples2/wasm/Erc20.wasm + mv examples2/target/wasm32-unknown-unknown/release/build_contract.wasm examples2/wasm/Erc20.wasm test-erc20: build-erc20 cd examples2 && cargo test --lib erc20 -- --nocapture cd examples2 && ODRA_BACKEND=casper cargo test --lib erc20 -- --nocapture build-counter-pack: - cd examples2 && ODRA_MODULE=CounterPack cargo build --release --target wasm32-unknown-unknown --bin contract - wasm-strip examples2/target/wasm32-unknown-unknown/release/contract.wasm - cp examples2/target/wasm32-unknown-unknown/release/contract.wasm examples2/wasm/CounterPack.wasm + cd examples2 && ODRA_MODULE=CounterPack cargo build --release --target wasm32-unknown-unknown --bin build_contract + wasm-strip examples2/target/wasm32-unknown-unknown/release/build_contract.wasm + cp examples2/target/wasm32-unknown-unknown/release/build_contract.wasm examples2/wasm/CounterPack.wasm test-counter-pack: build-counter-pack cd examples2 && ODRA_BACKEND=casper cargo test --lib counter_pack -- --nocapture diff --git a/modules/src/erc20.rs b/modules/src/erc20.rs index 6719796f..8abb120c 100644 --- a/modules/src/erc20.rs +++ b/modules/src/erc20.rs @@ -1,7 +1,7 @@ -use odra::prelude::*; -use odra::{Address, ContractEnv, Mapping, U256, Variable}; use crate::erc20::errors::Error::{DecimalsNotSet, NameNotSet, SymbolNotSet}; use crate::erc20::events::*; +use odra::prelude::*; +use odra::{Address, ContractEnv, Mapping, Variable, U256}; #[odra::module(events = [Approval, Transfer])] pub struct Erc20 { @@ -33,8 +33,7 @@ impl Erc20 { self.balances.set(&caller, initial_supply); if !initial_supply.is_zero() { - self.env.emit_event( - Transfer { + self.env.emit_event(Transfer { from: None, to: Some(caller), amount: initial_supply @@ -76,9 +75,7 @@ impl Erc20 { } pub fn decimals(&self) -> u8 { - self.decimals - .get() - .unwrap_or_revert_with(DecimalsNotSet) + self.decimals.get().unwrap_or_revert_with(DecimalsNotSet) } pub fn total_supply(&self) -> U256 { @@ -156,8 +153,8 @@ impl Erc20 { } pub mod events { - use odra::{Address, U256}; use casper_event_standard::Event; + use odra::{Address, U256}; #[derive(Event, Eq, PartialEq, Debug)] pub struct Transfer { @@ -178,12 +175,12 @@ pub mod errors { use odra::OdraError; pub enum Error { - InsufficientBalance = 30_000, - InsufficientAllowance = 30_001, - NameNotSet = 30_002, - SymbolNotSet = 30_003, - DecimalsNotSet = 30_004, - } + InsufficientBalance = 30_000, + InsufficientAllowance = 30_001, + NameNotSet = 30_002, + SymbolNotSet = 30_003, + DecimalsNotSet = 30_004 + } impl From for OdraError { fn from(error: Error) -> Self { diff --git a/odra-macros/src/ast/clone.rs b/odra-macros/src/ast/clone.rs new file mode 100644 index 00000000..9770c368 --- /dev/null +++ b/odra-macros/src/ast/clone.rs @@ -0,0 +1,113 @@ +use crate::ast::fn_utils::SelfFnItem; +use crate::ast::utils::{ImplItem, Named}; +use crate::ir::TypeIR; +use crate::utils; +use crate::utils::misc::AsBlock; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{punctuated::Punctuated, token::Comma}; + +#[derive(syn_derive::ToTokens)] +pub struct CloneItem { + attr: syn::Attribute, + impl_item: ImplItem, + #[syn(braced)] + braces: syn::token::Brace, + #[syn(in = braces)] + inline_attr: syn::Attribute, + #[syn(in = braces)] + fn_item: SelfFnItem +} + +impl TryFrom<&'_ TypeIR> for CloneItem { + type Error = syn::Error; + + fn try_from(ir: &TypeIR) -> Result { + let ident_clone = utils::ident::clone(); + let ident = ir.name()?; + let fields = ir.fields()?; + let block = match ir.is_enum() { + true => { + let variants = fields + .iter() + .map(|field| map_enum_ident(&ident, field)) + .collect::>(); + let ty_self = utils::ty::_self(); + quote!(match #ty_self { #variants }).as_block() + } + false => { + let variants = fields + .iter() + .map(|field| map_struct_ident(&ident, field)) + .collect::>(); + let ty_self = utils::ty::_Self(); + quote!(#ty_self { #variants }).as_block() + } + }; + + Ok(Self { + attr: utils::attr::automatically_derived(), + impl_item: ImplItem::clone(ir)?, + braces: Default::default(), + inline_attr: utils::attr::inline(), + fn_item: SelfFnItem::new(&ident_clone, ret_ty(), block) + }) + } +} + +fn map_struct_ident(_ident: &syn::Ident, field: &syn::Ident) -> TokenStream { + let ty_self = utils::ty::self_ref(); + quote!(#field: ::core::clone::Clone::clone(#ty_self.#field)) +} + +fn map_enum_ident(ident: &syn::Ident, variant: &syn::Ident) -> TokenStream { + quote!(#ident::#variant => #ident::#variant) +} + +fn ret_ty() -> syn::ReturnType { + utils::misc::ret_ty(&utils::ty::_Self()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils; + + #[test] + fn test_struct() { + let ir = test_utils::mock_struct(); + let item = CloneItem::try_from(&ir).unwrap(); + let expected = quote! { + #[automatically_derived] + impl ::core::clone::Clone for MyType { + #[inline] + fn clone(&self) -> Self { + Self { + a: ::core::clone::Clone::clone(&self.a), + b: ::core::clone::Clone::clone(&self.b), + } + } + } + }; + test_utils::assert_eq(item, expected); + } + + #[test] + fn test_enum() { + let ir = test_utils::mock_enum(); + let item = CloneItem::try_from(&ir).unwrap(); + let expected = quote! { + #[automatically_derived] + impl ::core::clone::Clone for MyType { + #[inline] + fn clone(&self) -> Self { + match self { + MyType::A => MyType::A, + MyType::B => MyType::B, + } + } + } + }; + test_utils::assert_eq(item, expected); + } +} diff --git a/odra-macros/src/ast/deployer_utils.rs b/odra-macros/src/ast/deployer_utils.rs index cf416150..df3a16f1 100644 --- a/odra-macros/src/ast/deployer_utils.rs +++ b/odra-macros/src/ast/deployer_utils.rs @@ -1,4 +1,5 @@ use super::{fn_utils, ref_utils}; +use crate::utils::misc::AsBlock; use crate::{ ir::{FnIR, ModuleIR}, utils @@ -105,10 +106,7 @@ impl TryFrom<&'_ ModuleIR> for NewContractExpr { let args = module .constructor() .map(|f| fn_utils::runtime_args_block(&f, ref_utils::insert_arg_stmt)) - .unwrap_or({ - let args = utils::expr::new_runtime_args(); - parse_quote!({#args}) - }); + .unwrap_or(utils::expr::new_runtime_args().as_block()); let new_contract_expr = parse_quote!( #env_ident.new_contract( diff --git a/odra-macros/src/ast/error_item.rs b/odra-macros/src/ast/error_item.rs new file mode 100644 index 00000000..7861037b --- /dev/null +++ b/odra-macros/src/ast/error_item.rs @@ -0,0 +1,58 @@ +use crate::ast::fn_utils::SingleArgFnItem; +use crate::ast::utils::{ImplItem, Named}; +use crate::ir::TypeIR; +use crate::utils; +use crate::utils::misc::AsBlock; +use syn::parse_quote; + +#[derive(syn_derive::ToTokens)] +pub struct OdraErrorItem { + attr: syn::Attribute, + impl_item: ImplItem, + #[syn(braced)] + braces: syn::token::Brace, + #[syn(in = braces)] + fn_item: SingleArgFnItem +} + +impl TryFrom<&'_ TypeIR> for OdraErrorItem { + type Error = syn::Error; + + fn try_from(ty: &TypeIR) -> Result { + let ident = ty.name()?; + let ident_error = utils::ident::error(); + let ty_odra_error = utils::ty::odra_error(); + Ok(Self { + attr: utils::attr::automatically_derived(), + impl_item: ImplItem::from(ty, &ty_odra_error)?, + braces: Default::default(), + fn_item: SingleArgFnItem::new( + &utils::ident::from(), + parse_quote!(#ident_error: #ident), + utils::misc::ret_ty(&utils::ty::_Self()), + utils::expr::user_error(&ident_error).as_block() + ) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils; + + #[test] + fn test_odra_error_item() { + let ty = test_utils::mock_enum(); + let item = OdraErrorItem::try_from(&ty).unwrap(); + let expected = quote::quote! { + #[automatically_derived] + impl ::core::convert::From for odra::OdraError { + fn from(error: MyType) -> Self { + odra::OdraError::user(error as u16) + } + } + }; + test_utils::assert_eq(item, expected); + } +} diff --git a/odra-macros/src/ast/events_item.rs b/odra-macros/src/ast/events_item.rs index b847167b..00f2278e 100644 --- a/odra-macros/src/ast/events_item.rs +++ b/odra-macros/src/ast/events_item.rs @@ -1,5 +1,9 @@ use syn::parse_quote; +use crate::ast::fn_utils::FnItem; +use crate::ast::utils::Named; +use crate::ir::TypeIR; +use crate::utils::misc::AsBlock; use crate::{ir::StructIR, utils}; #[derive(syn_derive::ToTokens)] @@ -29,26 +33,59 @@ impl TryFrom<&'_ StructIR> for HasEventsImplItem { } } +impl TryFrom<&'_ TypeIR> for HasEventsImplItem { + type Error = syn::Error; + + fn try_from(ir: &'_ TypeIR) -> Result { + Ok(Self { + impl_token: Default::default(), + has_ident_ty: utils::ty::has_events(), + for_token: Default::default(), + module_ident: ir.name()?, + brace_token: Default::default(), + events_fn: EventsFnItem::empty() + }) + } +} + #[derive(syn_derive::ToTokens)] pub struct EventsFnItem { - sig: syn::Signature, - block: syn::Block + fn_item: FnItem +} + +impl EventsFnItem { + pub fn empty() -> Self { + let ident_events = utils::ident::events(); + let empty_vec = utils::expr::empty_vec(); + Self { + fn_item: FnItem::new(&ident_events, vec![], Self::ret_ty(), empty_vec.as_block()) + } + } + + fn ret_ty() -> syn::ReturnType { + let ev_ty = utils::ty::event(); + let vec = utils::ty::vec_of(&ev_ty); + utils::misc::ret_ty(&vec) + } } impl TryFrom<&'_ StructIR> for EventsFnItem { type Error = syn::Error; fn try_from(struct_ir: &'_ StructIR) -> Result { - let ev_ty = utils::ty::event(); + let ident_events = utils::ident::events(); let struct_events_stmt = struct_events_stmt(struct_ir); let chain_events_expr = chain_events_expr(struct_ir)?; - Ok(Self { - sig: parse_quote!(fn events() -> Vec<#ev_ty>), - block: parse_quote!({ - #struct_events_stmt - #chain_events_expr - }) + fn_item: FnItem::new( + &ident_events, + vec![], + Self::ret_ty(), + parse_quote!({ + #struct_events_stmt + #chain_events_expr + }) + ) }) } } @@ -61,7 +98,8 @@ fn struct_events_stmt(ir: &StructIR) -> syn::Stmt { .iter() .map(utils::expr::into_event) .collect::>(); - parse_quote!(let #events_ident = vec![#struct_events];) + let vec = utils::expr::vec(struct_events); + parse_quote!(let #events_ident = #vec;) } fn chain_events_expr(ir: &StructIR) -> Result { @@ -95,8 +133,8 @@ mod test { let module = test_utils::mock_module_definition(); let expected = quote!( impl odra::contract_def::HasEvents for CounterPack { - fn events() -> Vec { - let events = vec![ + fn events() -> odra::prelude::vec::Vec { + let events = odra::prelude::vec![ ::into_event(), ::into_event() ]; diff --git a/odra-macros/src/ast/fn_utils.rs b/odra-macros/src/ast/fn_utils.rs index 94c4f901..8d84107a 100644 --- a/odra-macros/src/ast/fn_utils.rs +++ b/odra-macros/src/ast/fn_utils.rs @@ -2,6 +2,8 @@ use crate::{ ir::{FnArgIR, FnIR}, utils }; +use quote::TokenStreamExt; +use syn::{parse_quote, FnArg}; pub fn runtime_args_block syn::Stmt>( fun: &FnIR, @@ -27,3 +29,61 @@ pub fn insert_args_stmts syn::Stmt>( .map(insert_arg_fn) .collect::>() } + +#[derive(syn_derive::ToTokens)] +pub struct FnItem { + fn_token: syn::Token![fn], + fn_name: syn::Ident, + #[syn(parenthesized)] + paren_token: syn::token::Paren, + #[syn(in = paren_token)] + #[to_tokens(| tokens, f | tokens.append_all(f))] + args: Vec, + ret_ty: syn::ReturnType, + block: syn::Block +} + +impl FnItem { + pub fn new( + name: &syn::Ident, + args: Vec, + ret_ty: syn::ReturnType, + block: syn::Block + ) -> Self { + Self { + fn_token: Default::default(), + fn_name: name.clone(), + paren_token: Default::default(), + args, + ret_ty, + block + } + } +} + +#[derive(syn_derive::ToTokens)] +pub struct SingleArgFnItem { + fn_item: FnItem +} + +impl SingleArgFnItem { + pub fn new(name: &syn::Ident, arg: FnArg, ret_ty: syn::ReturnType, block: syn::Block) -> Self { + Self { + fn_item: FnItem::new(name, vec![arg], ret_ty, block) + } + } +} + +#[derive(syn_derive::ToTokens)] +pub struct SelfFnItem { + fn_item: SingleArgFnItem +} + +impl SelfFnItem { + pub fn new(name: &syn::Ident, ret_ty: syn::ReturnType, block: syn::Block) -> Self { + let self_ty = utils::ty::self_ref(); + Self { + fn_item: SingleArgFnItem::new(name, parse_quote!(#self_ty), ret_ty, block) + } + } +} diff --git a/odra-macros/src/ast/ident_item.rs b/odra-macros/src/ast/ident_item.rs index ed075323..f9675cc5 100644 --- a/odra-macros/src/ast/ident_item.rs +++ b/odra-macros/src/ast/ident_item.rs @@ -65,8 +65,8 @@ mod test { let module = test_utils::mock_module_definition(); let expected = quote!( impl odra::contract_def::HasIdent for CounterPack { - fn ident() -> String { - String::from("CounterPack") + fn ident() -> odra::prelude::string::String { + odra::prelude::string::String::from("CounterPack") } } ); diff --git a/odra-macros/src/ast/mod.rs b/odra-macros/src/ast/mod.rs index df2e2868..feb624da 100644 --- a/odra-macros/src/ast/mod.rs +++ b/odra-macros/src/ast/mod.rs @@ -1,7 +1,9 @@ mod blueprint; +mod clone; mod deployer_item; mod deployer_utils; mod entrypoints_item; +mod error_item; mod events_item; mod exec_parts; mod fn_utils; @@ -9,20 +11,24 @@ mod host_ref_item; mod ident_item; mod module_def; mod module_item; +mod odra_type_item; mod parts_utils; mod ref_item; mod ref_utils; mod test_parts; +mod utils; mod wasm_parts; mod wasm_parts_utils; pub(crate) use blueprint::BlueprintItem; pub(crate) use entrypoints_item::HasEntrypointsImplItem; +pub(crate) use error_item::OdraErrorItem; pub(crate) use events_item::HasEventsImplItem; pub(crate) use exec_parts::{ExecPartsItem, ExecPartsReexportItem}; pub(crate) use ident_item::HasIdentImplItem; pub(crate) use module_def::ModuleDefItem; pub(crate) use module_item::ModuleModItem; +pub(crate) use odra_type_item::OdraTypeItem; pub(crate) use ref_item::RefItem; pub(crate) use test_parts::{TestPartsItem, TestPartsReexportItem}; pub(crate) use wasm_parts::WasmPartsModuleItem; diff --git a/odra-macros/src/ast/odra_type_item.rs b/odra-macros/src/ast/odra_type_item.rs new file mode 100644 index 00000000..ad7792c7 --- /dev/null +++ b/odra-macros/src/ast/odra_type_item.rs @@ -0,0 +1,407 @@ +use crate::ast::clone::CloneItem; +use crate::ast::fn_utils::{FnItem, SelfFnItem, SingleArgFnItem}; +use crate::ast::utils::{ImplItem, Named}; +use crate::ast::HasEventsImplItem; +use crate::ir::TypeIR; +use crate::utils; +use crate::utils::misc::AsBlock; +use derive_try_from::TryFromRef; +use syn::parse_quote; + +macro_rules! impl_from_ir { + ($ty:path) => { + impl TryFrom<&'_ TypeIR> for $ty { + type Error = syn::Error; + + fn try_from(ir: &TypeIR) -> Result { + match ir { + x if x.is_enum() => Self::from_enum(ir), + x if x.is_struct() => Self::from_struct(ir), + _ => Err(syn::Error::new_spanned( + ir.self_code(), + "Only support enum or struct" + )) + } + } + } + }; +} + +#[derive(syn_derive::ToTokens, TryFromRef)] +#[source(TypeIR)] +pub struct OdraTypeItem { + from_bytes_impl: FromBytesItem, + to_bytes_impl: ToBytesItem, + cl_type_impl: CLTypedItem, + has_events_impl: HasEventsImplItem, + clone_item: CloneItem +} + +#[derive(syn_derive::ToTokens)] +struct FromBytesItem { + impl_item: ImplItem, + #[syn(braced)] + brace_token: syn::token::Brace, + #[syn(in = brace_token)] + fn_item: FromBytesFnItem +} + +impl TryFrom<&'_ TypeIR> for FromBytesItem { + type Error = syn::Error; + + fn try_from(ir: &TypeIR) -> Result { + Ok(Self { + impl_item: ImplItem::from_bytes(ir)?, + brace_token: Default::default(), + fn_item: ir.try_into()? + }) + } +} + +#[derive(syn_derive::ToTokens)] +struct ToBytesItem { + impl_item: ImplItem, + #[syn(braced)] + brace_token: syn::token::Brace, + #[syn(in = brace_token)] + fn_item: ToBytesFnItem, + #[syn(in = brace_token)] + serialized_length_item: SerializedLengthFnItem +} + +impl TryFrom<&'_ TypeIR> for ToBytesItem { + type Error = syn::Error; + + fn try_from(ir: &TypeIR) -> Result { + Ok(Self { + impl_item: ImplItem::to_bytes(ir)?, + brace_token: Default::default(), + fn_item: ir.try_into()?, + serialized_length_item: ir.try_into()? + }) + } +} + +#[derive(syn_derive::ToTokens)] +struct CLTypedItem { + impl_item: ImplItem, + #[syn(braced)] + brace_token: syn::token::Brace, + #[syn(in = brace_token)] + fn_item: FnItem +} + +impl TryFrom<&'_ TypeIR> for CLTypedItem { + type Error = syn::Error; + + fn try_from(ir: &TypeIR) -> Result { + let ret_ty_cl_type_any = match ir.is_enum() { + true => utils::ty::cl_type_u32(), + false => utils::ty::cl_type_any() + } + .as_block(); + let ty_cl_type = utils::ty::cl_type(); + Ok(Self { + impl_item: ImplItem::cl_typed(ir)?, + brace_token: Default::default(), + fn_item: FnItem::new( + &utils::ident::cl_type(), + vec![], + utils::misc::ret_ty(&ty_cl_type), + ret_ty_cl_type_any + ) + }) + } +} + +#[derive(syn_derive::ToTokens)] +struct FromBytesFnItem { + fn_item: SingleArgFnItem +} + +impl_from_ir!(FromBytesFnItem); + +impl FromBytesFnItem { + fn from_enum(ir: &TypeIR) -> Result { + let ident = ir.name()?; + let ident_bytes = utils::ident::bytes(); + let ident_from_bytes = utils::ident::from_bytes(); + let ident_result = utils::ident::result(); + let ty_u32 = utils::ty::u32(); + let from_bytes_expr = utils::expr::failable_from_bytes(&ident_bytes); + + let read_stmt: syn::Stmt = + parse_quote!(let (#ident_result, #ident_bytes): (#ty_u32, _) = #from_bytes_expr;); + let deser = ir.map_fields( + |i| quote::quote!(x if x == #ident::#i as #ty_u32 => Ok((#ident::#i, #ident_bytes))) + )?; + let arg = Self::arg(); + let ret_ty = Self::ret_ty(); + let block = parse_quote!({ + #read_stmt + match #ident_result { + #(#deser,)* + _ => Err(odra::BytesReprError::Formatting), + } + }); + Ok(Self { + fn_item: SingleArgFnItem::new(&ident_from_bytes, arg, ret_ty, block) + }) + } + + fn from_struct(ir: &TypeIR) -> Result { + let ident_bytes = utils::ident::bytes(); + let ident_from_bytes = utils::ident::from_bytes(); + + let from_bytes_expr = utils::expr::failable_from_bytes(&ident_bytes); + let fields = ir + .fields()? + .into_iter() + .collect::>(); + let deser = ir.map_fields(|i| quote::quote!(let (#i, #ident_bytes) = #from_bytes_expr;))?; + let arg = Self::arg(); + let ret_ty = Self::ret_ty(); + let block = parse_quote!({ + #(#deser)* + Ok((Self { #fields }, #ident_bytes)) + }); + + Ok(Self { + fn_item: SingleArgFnItem::new(&ident_from_bytes, arg, ret_ty, block) + }) + } + + fn arg() -> syn::FnArg { + let ident_bytes = utils::ident::bytes(); + let ty_bytes_slice = utils::ty::bytes_slice(); + parse_quote!(#ident_bytes: #ty_bytes_slice) + } + + fn ret_ty() -> syn::ReturnType { + let ty_bytes_slice = utils::ty::bytes_slice(); + let ty_self = utils::ty::_Self(); + let ty_ok = parse_quote!((#ty_self, #ty_bytes_slice)); + let ty_ret = utils::ty::bytes_result(&ty_ok); + utils::misc::ret_ty(&ty_ret) + } +} + +#[derive(syn_derive::ToTokens)] +struct ToBytesFnItem { + fn_item: SelfFnItem +} + +impl_from_ir!(ToBytesFnItem); + +impl ToBytesFnItem { + fn from_struct(ir: &TypeIR) -> Result { + let ty_bytes_vec = utils::ty::bytes_vec(); + let ty_ret = utils::ty::bytes_result(&ty_bytes_vec); + let ty_self = utils::ty::_self(); + + let ident_result = utils::ident::result(); + let serialized_length_expr = utils::expr::serialized_length(&ty_self); + + let init_vec_stmt = + utils::stmt::new_mut_vec_with_capacity(&ident_result, &serialized_length_expr); + + let serialize = ir.map_fields(|i| { + let member = utils::member::_self(i); + let expr_to_bytes = utils::expr::failable_to_bytes(&member); + quote::quote!(#ident_result.extend(#expr_to_bytes);) + })?; + + let name = utils::ident::to_bytes(); + let ret_ty = utils::misc::ret_ty(&ty_ret); + let block = parse_quote!({ + #init_vec_stmt + #(#serialize)* + Ok(#ident_result) + }); + Ok(Self { + fn_item: SelfFnItem::new(&name, ret_ty, block) + }) + } + + fn from_enum(_ir: &TypeIR) -> Result { + let ty_bytes_vec = utils::ty::bytes_vec(); + let ty_ret = utils::ty::bytes_result(&ty_bytes_vec); + let ty_u32 = utils::ty::u32(); + let clone_expr = utils::expr::clone(&utils::ty::_self()); + let as_u32 = quote::quote!((#clone_expr as #ty_u32)); + + let name = utils::ident::to_bytes(); + let ret_ty = utils::misc::ret_ty(&ty_ret); + let block = utils::expr::to_bytes(&as_u32).as_block(); + + Ok(Self { + fn_item: SelfFnItem::new(&name, ret_ty, block) + }) + } +} + +#[derive(syn_derive::ToTokens)] +struct SerializedLengthFnItem { + fn_item: SelfFnItem +} + +impl_from_ir!(SerializedLengthFnItem); + +impl SerializedLengthFnItem { + fn from_struct(ir: &TypeIR) -> Result { + let ty_usize = utils::ty::usize(); + let ident_result = utils::ident::result(); + + let stmts = ir.map_fields(|i| { + let member = utils::member::_self(i); + let expr = utils::expr::serialized_length(&member); + let stmt: syn::Stmt = parse_quote!(#ident_result += #expr;); + stmt + })?; + + let name = utils::ident::serialized_length(); + let ret_ty = utils::misc::ret_ty(&ty_usize); + let block = parse_quote!({ + let mut #ident_result = 0; + #(#stmts)* + #ident_result + }); + Ok(Self { + fn_item: SelfFnItem::new(&name, ret_ty, block) + }) + } + + fn from_enum(_ir: &TypeIR) -> Result { + let ty_usize = utils::ty::usize(); + let ty_u32 = utils::ty::u32(); + let clone_expr = utils::expr::clone(&utils::ty::_self()); + let as_u32 = quote::quote!((#clone_expr as #ty_u32)); + + let name = utils::ident::serialized_length(); + let ret_ty = utils::misc::ret_ty(&ty_usize); + let block = utils::expr::serialized_length(&as_u32).as_block(); + Ok(Self { + fn_item: SelfFnItem::new(&name, ret_ty, block) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils; + use quote::quote; + + #[test] + fn test_struct() { + let ir = test_utils::mock_struct(); + let item = OdraTypeItem::try_from(&ir).unwrap(); + let expected = quote!( + impl odra::FromBytes for MyType { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), odra::BytesReprError> { + let (a, bytes) = odra::FromBytes::from_bytes(bytes)?; + let (b, bytes) = odra::FromBytes::from_bytes(bytes)?; + + Ok((Self { + a, + b + }, bytes)) + } + } + + impl odra::ToBytes for MyType { + fn to_bytes(&self) -> Result, odra::BytesReprError> { + let mut result = odra::prelude::vec::Vec::with_capacity(self.serialized_length()); + result.extend(odra::ToBytes::to_bytes(&self.a)?); + result.extend(odra::ToBytes::to_bytes(&self.b)?); + Ok(result) + } + + fn serialized_length(&self) -> usize { + let mut result = 0; + result += self.a.serialized_length(); + result += self.b.serialized_length(); + result + } + } + + impl odra::casper_types::CLTyped for MyType { + fn cl_type() -> odra::casper_types::CLType { + odra::casper_types::CLType::Any + } + } + + impl odra::contract_def::HasEvents for MyType { + fn events() -> odra::prelude::vec::Vec { + odra::prelude::vec::Vec::new() + } + } + + #[automatically_derived] + impl ::core::clone::Clone for MyType { + #[inline] + fn clone(&self) -> Self { + Self { + a: ::core::clone::Clone::clone(&self.a), + b: ::core::clone::Clone::clone(&self.b), + } + } + } + ); + + test_utils::assert_eq(item, expected); + } + + #[test] + fn test_enum() { + let ir = test_utils::mock_enum(); + let item = OdraTypeItem::try_from(&ir).unwrap(); + let expected = quote!( + impl odra::FromBytes for MyType { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), odra::BytesReprError> { + let (result, bytes): (u32, _) = odra::FromBytes::from_bytes(bytes)?; + match result { + x if x == MyType::A as u32 => Ok((MyType::A, bytes)), + x if x == MyType::B as u32 => Ok((MyType::B, bytes)), + _ => Err(odra::BytesReprError::Formatting), + } + } + } + + impl odra::ToBytes for MyType { + fn to_bytes(&self) -> Result, odra::BytesReprError> { + (self.clone() as u32).to_bytes() + } + + fn serialized_length(&self) -> usize { + (self.clone() as u32).serialized_length() + } + } + + impl odra::casper_types::CLTyped for MyType { + fn cl_type() -> odra::casper_types::CLType { + odra::casper_types::CLType::U32 + } + } + + impl odra::contract_def::HasEvents for MyType { + fn events() -> odra::prelude::vec::Vec { + odra::prelude::vec::Vec::new() + } + } + + #[automatically_derived] + impl ::core::clone::Clone for MyType { + #[inline] + fn clone(&self) -> Self { + match self { + MyType::A => MyType::A, + MyType::B => MyType::B, + } + } + } + ); + + test_utils::assert_eq(item, expected); + } +} diff --git a/odra-macros/src/ast/utils.rs b/odra-macros/src/ast/utils.rs new file mode 100644 index 00000000..f604c5c1 --- /dev/null +++ b/odra-macros/src/ast/utils.rs @@ -0,0 +1,64 @@ +use crate::ir::{StructIR, TypeIR}; +use crate::utils; +use crate::utils::misc::AsType; + +#[derive(syn_derive::ToTokens)] +pub struct ImplItem { + impl_token: syn::Token![impl], + ty: syn::Type, + for_token: syn::Token![for], + for_ty: syn::Type +} + +impl ImplItem { + fn new(named: &T, ty: syn::Type) -> Result { + Ok(Self { + impl_token: Default::default(), + ty, + for_token: Default::default(), + for_ty: named.name()?.as_type() + }) + } + + pub fn from_bytes(ir: &TypeIR) -> Result { + Self::new(ir, utils::ty::from_bytes()) + } + + pub fn to_bytes(ir: &TypeIR) -> Result { + Self::new(ir, utils::ty::to_bytes()) + } + + pub fn cl_typed(ir: &TypeIR) -> Result { + Self::new(ir, utils::ty::cl_typed()) + } + + pub fn clone(named: &T) -> Result { + Self::new(named, utils::ty::clone()) + } + + pub fn from(named: &T, for_ty: &syn::Type) -> Result { + let ty_from = utils::ty::from(&named.name()?); + Ok(Self { + impl_token: Default::default(), + ty: ty_from, + for_token: Default::default(), + for_ty: for_ty.clone() + }) + } +} + +pub trait Named { + fn name(&self) -> Result; +} + +impl Named for TypeIR { + fn name(&self) -> Result { + Ok(self.self_code().ident.clone()) + } +} + +impl Named for StructIR { + fn name(&self) -> Result { + Ok(self.module_ident()) + } +} diff --git a/odra-macros/src/ir/mod.rs b/odra-macros/src/ir/mod.rs index b2d6b6da..efc58490 100644 --- a/odra-macros/src/ir/mod.rs +++ b/odra-macros/src/ir/mod.rs @@ -4,7 +4,7 @@ use crate::utils; use config::ConfigItem; use proc_macro2::Ident; use quote::{format_ident, ToTokens}; -use syn::{parse_quote, spanned::Spanned}; +use syn::{parse_quote, spanned::Spanned, Data}; use self::attr::OdraAttribute; @@ -418,3 +418,42 @@ impl FnArgIR { } } } + +pub struct TypeIR { + code: syn::DeriveInput +} + +impl TryFrom<&proc_macro2::TokenStream> for TypeIR { + type Error = syn::Error; + + fn try_from(stream: &proc_macro2::TokenStream) -> Result { + Ok(Self { + code: syn::parse2::(stream.clone())? + }) + } +} + +impl TypeIR { + pub fn self_code(&self) -> &syn::DeriveInput { + &self.code + } + + pub fn fields(&self) -> Result, syn::Error> { + utils::syn::derive_item_variants(&self.code) + } + + pub fn map_fields(&self, func: F) -> Result, syn::Error> + where + F: FnMut(&syn::Ident) -> R + { + Ok(self.fields()?.iter().map(func).collect::>()) + } + + pub fn is_enum(&self) -> bool { + matches!(self.code.data, Data::Enum(_)) + } + + pub fn is_struct(&self) -> bool { + matches!(self.code.data, Data::Struct(_)) + } +} diff --git a/odra-macros/src/lib.rs b/odra-macros/src/lib.rs index 19504197..15c03611 100644 --- a/odra-macros/src/lib.rs +++ b/odra-macros/src/lib.rs @@ -2,11 +2,10 @@ use ast::*; use derive_try_from::TryFromRef; -use ir::{ModuleIR, StructIR}; +use ir::{ModuleIR, StructIR, TypeIR}; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; -use syn::spanned::Spanned; mod ast; mod ir; @@ -14,6 +13,14 @@ mod ir; mod test_utils; mod utils; +macro_rules! span_error { + ($span:ident, $msg:expr) => { + syn::Error::new(syn::spanned::Spanned::span(&$span), $msg) + .to_compile_error() + .into() + }; +} + #[proc_macro_attribute] pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream { let attr: TokenStream2 = attr.into(); @@ -24,9 +31,25 @@ pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream { if let Ok(ir) = StructIR::try_from((&attr, &item)) { return ModuleStruct::try_from(&ir).into_code(); } - syn::Error::new(item.span(), "Struct or impl block expected") - .to_compile_error() - .into() + span_error!(item, "Struct or impl block expected") +} + +#[proc_macro_derive(OdraType)] +pub fn derive_odra_type(item: TokenStream) -> TokenStream { + let item = item.into(); + if let Ok(ir) = TypeIR::try_from(&item) { + return OdraTypeItem::try_from(&ir).into_code(); + } + span_error!(item, "Struct or Enum expected") +} + +#[proc_macro_derive(OdraError)] +pub fn derive_odra_error(item: TokenStream) -> TokenStream { + let item = item.into(); + if let Ok(ir) = TypeIR::try_from(&item) { + return OdraErrorItem::try_from(&ir).into_code(); + } + span_error!(item, "Struct or Enum expected") } #[derive(syn_derive::ToTokens, TryFromRef)] diff --git a/odra-macros/src/test_utils.rs b/odra-macros/src/test_utils.rs index 6a673136..1467cf2f 100644 --- a/odra-macros/src/test_utils.rs +++ b/odra-macros/src/test_utils.rs @@ -1,6 +1,6 @@ use quote::{quote, ToTokens}; -use crate::ir::{ModuleIR, StructIR}; +use crate::ir::{ModuleIR, StructIR, TypeIR}; pub fn mock_module() -> ModuleIR { let module = quote! { @@ -52,6 +52,26 @@ pub fn mock_module_definition() -> StructIR { StructIR::try_from((&attr, &module)).unwrap() } +pub fn mock_struct() -> TypeIR { + let ty = quote!( + struct MyType { + a: String, + b: u32 + } + ); + TypeIR::try_from(&ty).unwrap() +} + +pub fn mock_enum() -> TypeIR { + let ty = quote!( + enum MyType { + A, + B + } + ); + TypeIR::try_from(&ty).unwrap() +} + #[track_caller] pub fn assert_eq(a: A, b: B) { fn parse(e: T) -> String { diff --git a/odra-macros/src/utils/attr.rs b/odra-macros/src/utils/attr.rs index e07da0bc..88bf13a4 100644 --- a/odra-macros/src/utils/attr.rs +++ b/odra-macros/src/utils/attr.rs @@ -19,3 +19,7 @@ pub fn no_mangle() -> syn::Attribute { pub fn inline() -> syn::Attribute { parse_quote!(#[inline]) } + +pub fn automatically_derived() -> syn::Attribute { + parse_quote!(#[automatically_derived]) +} diff --git a/odra-macros/src/utils/expr.rs b/odra-macros/src/utils/expr.rs index 27cb5d6b..cb467078 100644 --- a/odra-macros/src/utils/expr.rs +++ b/odra-macros/src/utils/expr.rs @@ -1,3 +1,4 @@ +use quote::ToTokens; use syn::parse_quote; pub fn new_runtime_args() -> syn::Expr { @@ -85,7 +86,48 @@ pub fn new_blueprint(ident: &syn::Ident) -> syn::Expr { } pub fn string_from(string: String) -> syn::Expr { - parse_quote!(String::from(#string)) + let ty = super::ty::string(); + parse_quote!(#ty::from(#string)) +} + +pub fn failable_from_bytes(arg_ident: &syn::Ident) -> syn::Expr { + let ty = super::ty::from_bytes(); + let fn_ident = super::ident::from_bytes(); + parse_quote!(#ty::#fn_ident(#arg_ident)?) +} + +pub fn serialized_length(caller: &T) -> syn::Expr { + let fn_ident = super::ident::serialized_length(); + parse_quote!(#caller.#fn_ident()) +} + +pub fn failable_to_bytes(caller: &T) -> syn::Expr { + let fn_ident = super::ident::to_bytes(); + let ty = super::ty::to_bytes(); + parse_quote!(#ty::#fn_ident(&#caller)?) +} + +pub fn to_bytes(caller: &T) -> syn::Expr { + let fn_ident = super::ident::to_bytes(); + parse_quote!(#caller.#fn_ident()) +} + +pub fn empty_vec() -> syn::Expr { + let ty = super::ty::vec(); + parse_quote!(#ty::new()) +} + +pub fn vec(content: T) -> syn::Expr { + parse_quote!(odra::prelude::vec![#content]) +} + +pub fn clone(caller: &T) -> syn::Expr { + parse_quote!(#caller.clone()) +} + +pub fn user_error(error: &syn::Ident) -> syn::Expr { + let ty = super::ty::odra_error(); + parse_quote!(#ty::user(#error as u16)) } pub trait IntoExpr { diff --git a/odra-macros/src/utils/ident.rs b/odra-macros/src/utils/ident.rs index 75d7c99d..afa0deb6 100644 --- a/odra-macros/src/utils/ident.rs +++ b/odra-macros/src/utils/ident.rs @@ -82,3 +82,35 @@ pub fn entrypoints() -> syn::Ident { pub fn ident() -> syn::Ident { format_ident!("ident") } + +pub fn bytes() -> syn::Ident { + format_ident!("bytes") +} + +pub fn from_bytes() -> syn::Ident { + format_ident!("from_bytes") +} + +pub fn to_bytes() -> syn::Ident { + format_ident!("to_bytes") +} + +pub fn serialized_length() -> syn::Ident { + format_ident!("serialized_length") +} + +pub fn cl_type() -> syn::Ident { + format_ident!("cl_type") +} + +pub fn clone() -> syn::Ident { + format_ident!("clone") +} + +pub fn from() -> syn::Ident { + format_ident!("from") +} + +pub fn error() -> syn::Ident { + format_ident!("error") +} diff --git a/odra-macros/src/utils/member.rs b/odra-macros/src/utils/member.rs index 6189cc0c..2bbbb1c5 100644 --- a/odra-macros/src/utils/member.rs +++ b/odra-macros/src/utils/member.rs @@ -17,3 +17,7 @@ pub fn underscored_env() -> syn::ExprField { fn member(ident: syn::Ident) -> syn::ExprField { syn::parse_quote!(self.#ident) } + +pub fn _self(ident: &syn::Ident) -> syn::Expr { + syn::parse_quote!(self.#ident) +} diff --git a/odra-macros/src/utils/misc.rs b/odra-macros/src/utils/misc.rs index be69f2f8..8cd49071 100644 --- a/odra-macros/src/utils/misc.rs +++ b/odra-macros/src/utils/misc.rs @@ -1,3 +1,47 @@ +use syn::parse_quote; + +pub fn ret_ty(ty: &syn::Type) -> syn::ReturnType { + parse_quote!(-> #ty) +} + +pub trait AsBlock { + fn as_block(&self) -> syn::Block; +} + +impl AsBlock for T { + fn as_block(&self) -> syn::Block { + parse_quote!({ #self }) + } +} + +pub trait AsExpr { + fn as_expr(&self) -> ::syn::Expr; +} + +impl AsExpr for T { + fn as_expr(&self) -> ::syn::Expr { + ::syn::parse_quote!(#self) + } +} +pub trait AsStmt { + fn as_stmt(&self) -> ::syn::Stmt; +} + +impl AsStmt for T { + fn as_stmt(&self) -> ::syn::Stmt { + ::syn::parse_quote!(#self) + } +} +pub trait AsType { + fn as_type(&self) -> ::syn::Type; +} + +impl AsType for T { + fn as_type(&self) -> ::syn::Type { + ::syn::parse_quote!(#self) + } +} + pub fn field(ident: &syn::Ident, ty: &syn::Type) -> syn::Field { syn::Field { attrs: vec![], diff --git a/odra-macros/src/utils/stmt.rs b/odra-macros/src/utils/stmt.rs index 5fd7ca72..8ef59b27 100644 --- a/odra-macros/src/utils/stmt.rs +++ b/odra-macros/src/utils/stmt.rs @@ -1,3 +1,4 @@ +use crate::utils::misc::AsExpr; use syn::parse_quote; pub fn runtime_return(result_ident: &syn::Ident) -> syn::Stmt { @@ -50,3 +51,9 @@ pub fn new_execution_env(ident: &syn::Ident, env_rc_ident: &syn::Ident) -> syn:: pub fn new_rc(var_ident: &syn::Ident, env_ident: &syn::Ident) -> syn::Stmt { parse_quote!(let #var_ident = Rc::new(#env_ident);) } + +pub fn new_mut_vec_with_capacity(ident: &syn::Ident, capacity_expr: &E) -> syn::Stmt { + let ty = super::ty::vec(); + let expr = capacity_expr.as_expr(); + parse_quote!(let mut #ident = #ty::with_capacity(#expr);) +} diff --git a/odra-macros/src/utils/syn.rs b/odra-macros/src/utils/syn.rs index 09dc76f3..6373308e 100644 --- a/odra-macros/src/utils/syn.rs +++ b/odra-macros/src/utils/syn.rs @@ -101,6 +101,36 @@ pub fn struct_fields(item: &syn::ItemStruct) -> Result Result, syn::Error> { + match &item.data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => fields + .iter() + .map(|f| { + f.ident + .clone() + .ok_or(syn::Error::new(f.span(), "Unnamed field")) + }) + .collect::, _>>(), + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let is_valid = variants + .iter() + .all(|v| matches!(v.fields, syn::Fields::Unit)); + if is_valid { + Ok(variants.iter().map(|v| v.ident.clone()).collect::>()) + } else { + Err(syn::Error::new_spanned( + variants, + "Expected a unit enum variant." + )) + } + } + _ => Err(syn::Error::new_spanned( + item, + "Struct with named fields expected" + )) + } +} + pub fn visibility_pub() -> syn::Visibility { parse_quote!(pub) } diff --git a/odra-macros/src/utils/ty.rs b/odra-macros/src/utils/ty.rs index 1fc537a4..b9db3bf2 100644 --- a/odra-macros/src/utils/ty.rs +++ b/odra-macros/src/utils/ty.rs @@ -1,3 +1,4 @@ +use quote::ToTokens; use syn::parse_quote; pub fn address() -> syn::Type { @@ -16,6 +17,14 @@ pub fn from_bytes() -> syn::Type { parse_quote!(odra::FromBytes) } +pub fn to_bytes() -> syn::Type { + parse_quote!(odra::ToBytes) +} + +pub fn bytes_err() -> syn::Type { + parse_quote!(odra::BytesReprError) +} + pub fn event_instance() -> syn::Type { parse_quote!(odra::casper_event_standard::EventInstance) } @@ -96,6 +105,18 @@ pub fn cl_typed() -> syn::Type { parse_quote!(odra::casper_types::CLTyped) } +pub fn cl_type() -> syn::Type { + parse_quote!(odra::casper_types::CLType) +} + +pub fn cl_type_any() -> syn::Type { + parse_quote!(odra::casper_types::CLType::Any) +} + +pub fn cl_type_u32() -> syn::Type { + parse_quote!(odra::casper_types::CLType::U32) +} + pub fn runtime_args() -> syn::Type { parse_quote!(odra::RuntimeArgs) } @@ -145,5 +166,58 @@ pub fn entry_point_def_arg() -> syn::Type { } pub fn string() -> syn::Type { - parse_quote!(String) + parse_quote!(odra::prelude::string::String) +} + +pub fn result(ty: &syn::Type, err_ty: &syn::Type) -> syn::Type { + parse_quote!(Result<#ty, #err_ty>) +} + +pub fn bytes_result(ty: &syn::Type) -> syn::Type { + result(ty, &bytes_err()) +} + +pub fn self_ref() -> syn::Type { + parse_quote!(&self) +} + +pub fn bytes_slice() -> syn::Type { + parse_quote!(&[u8]) +} + +#[allow(non_snake_case)] +pub fn _Self() -> syn::Type { + parse_quote!(Self) +} + +pub fn _self() -> syn::Type { + parse_quote!(self) +} + +pub fn vec() -> syn::Type { + parse_quote!(odra::prelude::vec::Vec) +} + +pub fn vec_of(ty: &syn::Type) -> syn::Type { + parse_quote!(odra::prelude::vec::Vec<#ty>) +} + +pub fn bytes_vec() -> syn::Type { + parse_quote!(odra::prelude::vec::Vec) +} + +pub fn usize() -> syn::Type { + parse_quote!(usize) +} + +pub fn u32() -> syn::Type { + parse_quote!(u32) +} + +pub fn clone() -> syn::Type { + parse_quote!(::core::clone::Clone) +} + +pub fn from(ty: &T) -> syn::Type { + parse_quote!(::core::convert::From<#ty>) }