Skip to content

Commit

Permalink
Implement EwasmAny
Browse files Browse the repository at this point in the history
  • Loading branch information
yanganto committed Jul 23, 2021
1 parent 7aa712e commit ecebe8c
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 77 deletions.
92 changes: 38 additions & 54 deletions examples/rdb-contract/src/lib.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use serde_derive::{Deserialize, Serialize};

use sewup::primitives::Contract;
use sewup::primitives::{Contract, EwasmAny};
use sewup::rdb::{errors::Error as LibError, Db, Feature};
use sewup_derive::{
ewasm_fn, ewasm_fn_sig, ewasm_input_from, ewasm_main, ewasm_output_from, ewasm_test,
Expand All @@ -14,15 +14,15 @@ mod modules;
use modules::{person, Person, PERSON};

#[ewasm_fn]
fn init_db_with_tables() -> Result<()> {
fn init_db_with_tables() -> Result<EwasmAny> {
let mut db = Db::new()?;
db.create_table::<Person>();
db.commit()?;
Ok(())
Ok(().into())
}

#[ewasm_fn]
fn check_version_and_features(version: u8, features: Vec<Feature>) -> Result<()> {
fn check_version_and_features(version: u8, features: Vec<Feature>) -> Result<EwasmAny> {
let db = Db::load(None)?;
if db.version() != version {
return Err(RDBError::UnexpectVersion(db.version()).into());
Expand All @@ -32,11 +32,11 @@ fn check_version_and_features(version: u8, features: Vec<Feature>) -> Result<()>
return Err(RDBError::IncompatibleFeatures(current_features).into());
};

Ok(())
Ok(().into())
}

#[ewasm_fn]
fn check_tables() -> Result<()> {
fn check_tables() -> Result<EwasmAny> {
let mut db = Db::load(None)?;
let info = db.table_info::<Person>().unwrap();
if info.record_raw_size != 1 {
Expand All @@ -48,65 +48,64 @@ fn check_tables() -> Result<()> {
if info.range.end != 2 {
return Err(RDBError::SimpleError("Person range end not correct".into()).into());
}
Ok(())
Ok(().into())
}

#[ewasm_fn]
fn drop_table() -> Result<()> {
fn drop_table() -> Result<EwasmAny> {
let mut db = Db::load(None)?;
db.drop_table::<Person>();
db.commit()?;
Ok(())
Ok(().into())
}

#[ewasm_fn]
fn check_tables_again() -> Result<()> {
fn check_tables_again() -> Result<EwasmAny> {
let mut db = Db::load(None)?;
if db.table_info::<Person>().is_some() {
return Err(RDBError::SimpleError("Person table should be deleted".into()).into());
}
Ok(())
Ok(().into())
}

#[ewasm_fn]
fn get_childern() -> Result<()> {
fn get_childern() -> Result<EwasmAny> {
use sewup::primitives::IntoEwasmAny;

let table = sewup::rdb::Db::load(None)?.table::<Person>()?;
let people = table.filter_records(&|p: &Person| p.age < 12)?;

// you can do much complicate filter logic here as you like

let output: person::Protocol = people.into();
sewup::utils::ewasm_return(ewasm_output_from!(output));
Ok(())
let protocol: person::Protocol = people.into();
Ok(EwasmAny::from(protocol))
}

#[ewasm_main]
fn main() -> Result<()> {
#[ewasm_main(auto)]
fn main() -> Result<EwasmAny> {
let mut contract = Contract::new()?;

match contract.get_function_selector()? {
ewasm_fn_sig!(person::get) => ewasm_input_from!(contract move person::get)?,
ewasm_fn_sig!(person::create) => ewasm_input_from!(contract move person::create)?,
ewasm_fn_sig!(person::update) => ewasm_input_from!(contract move person::update)?,
ewasm_fn_sig!(person::delete) => ewasm_input_from!(contract move person::delete)?,
ewasm_fn_sig!(person::get) => ewasm_input_from!(contract move person::get),
ewasm_fn_sig!(person::create) => ewasm_input_from!(contract move person::create),
ewasm_fn_sig!(person::update) => ewasm_input_from!(contract move person::update),
ewasm_fn_sig!(person::delete) => ewasm_input_from!(contract move person::delete),
ewasm_fn_sig!(check_version_and_features) => {
check_version_and_features(0, vec![Feature::Default])?
check_version_and_features(0, vec![Feature::Default])
}
ewasm_fn_sig!(get_childern) => get_childern()?,
ewasm_fn_sig!(init_db_with_tables) => init_db_with_tables()?,
ewasm_fn_sig!(check_tables) => check_tables()?,
ewasm_fn_sig!(drop_table) => drop_table()?,
ewasm_fn_sig!(check_tables_again) => check_tables_again()?,
ewasm_fn_sig!(get_childern) => get_childern(),
ewasm_fn_sig!(init_db_with_tables) => init_db_with_tables(),
ewasm_fn_sig!(check_tables) => check_tables(),
ewasm_fn_sig!(drop_table) => drop_table(),
ewasm_fn_sig!(check_tables_again) => check_tables_again(),
_ => return Err(RDBError::UnknownHandle.into()),
}

Ok(())
}

#[ewasm_test]
mod tests {
use super::*;
use sewup_derive::{ewasm_assert_eq, ewasm_assert_ok, ewasm_err_output};
use sewup_derive::{ewasm_assert_eq, ewasm_assert_ok, ewasm_auto_assert_eq, ewasm_err_output};

#[ewasm_test]
fn test_execute_crud_handler() {
Expand All @@ -120,14 +119,11 @@ mod tests {
let mut create_input = person::protocol(person.clone());
let mut expect_output = create_input.clone();
expect_output.set_id(1);
ewasm_assert_eq!(
person::create(create_input),
ewasm_output_from!(expect_output)
);
ewasm_auto_assert_eq!(person::create(create_input), expect_output);

let mut get_input: person::Protocol = Person::default().into();
get_input.set_id(1);
ewasm_assert_eq!(person::get(get_input), ewasm_output_from!(expect_output));
ewasm_auto_assert_eq!(person::get(get_input), expect_output);

let child = Person {
trusted: false,
Expand All @@ -137,26 +133,20 @@ mod tests {
create_input = person::protocol(child.clone());
expect_output = create_input.clone();
expect_output.set_id(2);
ewasm_assert_eq!(
person::create(create_input),
ewasm_output_from!(expect_output)
);
ewasm_auto_assert_eq!(person::create(create_input), expect_output);

get_input.set_id(2);
ewasm_assert_eq!(person::get(get_input), ewasm_output_from!(expect_output));
ewasm_auto_assert_eq!(person::get(get_input), expect_output);

let older_person = Person {
trusted: true,
age: 20,
};
let mut update_input = person::protocol(older_person.clone());
update_input.set_id(1);
ewasm_assert_eq!(
person::update(update_input),
ewasm_output_from!(update_input)
);
ewasm_auto_assert_eq!(person::update(update_input), update_input);
get_input.set_id(1);
ewasm_assert_eq!(person::get(get_input), ewasm_output_from!(update_input));
ewasm_auto_assert_eq!(person::get(get_input), update_input);

// Here is the advance query with filter and selector
// In the example, the query only want to get the age of trusted person
Expand All @@ -170,14 +160,11 @@ mod tests {
// and other fields will be None
expect_output = vec![older_person].into();
expect_output.records[0].trusted = None;
ewasm_assert_eq!(
person::get(person_query_protocol),
ewasm_output_from!(expect_output)
);
ewasm_auto_assert_eq!(person::get(person_query_protocol), expect_output);

// Get the childern by the customized handler
expect_output = vec![child].into();
ewasm_assert_eq!(get_childern(), ewasm_output_from!(expect_output));
ewasm_auto_assert_eq!(get_childern(), expect_output);

// Please Notice that protocol from the default instance may not be empty,
// this dependents on the default implementation of the struct.
Expand All @@ -190,10 +177,7 @@ mod tests {
assert!(delete_input.is_empty());

delete_input.set_id(1);
ewasm_assert_eq!(
person::delete(delete_input),
ewasm_output_from!(delete_input)
);
ewasm_auto_assert_eq!(person::delete(delete_input), delete_input);

ewasm_assert_eq!(
person::get(get_input),
Expand Down
97 changes: 78 additions & 19 deletions sewup-derive/src/lib.rs
Expand Up @@ -58,7 +58,62 @@ pub fn ewasm_main(attr: TokenStream, item: TokenStream) -> TokenStream {
)
}

let output_type = match input.sig.clone().output {
syn::ReturnType::Type(_, boxed) => match Box::into_inner(boxed) {
syn::Type::Path(syn::TypePath { path: p, .. }) => match p.segments.first() {
Some(syn::PathSegment {
arguments:
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args: a,
..
}),
..
}) => match a.first() {
Some(a) => match a {
syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
path: p,
..
})) => {
if let Some(syn::PathSegment { ident: i, .. }) = p.segments.first() {
Some(i.to_string())
} else {
None
}
}
_ => None,
},
_ => None,
},
_ => None,
},
_ => None,
},
_ => None,
};

match attr.to_string().to_lowercase().as_str() {
"auto" if Some("EwasmAny".to_string()) == output_type => quote! {
#[cfg(target_arch = "wasm32")]
use sewup::bincode;
#[cfg(target_arch = "wasm32")]
use sewup::ewasm_api::finish_data;
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
pub fn main() { compile_error!("The function wrapped with ewasm_main need to be compiled with wasm32 target"); }
#[cfg(target_arch = "wasm32")]
#[no_mangle]
pub fn main() {
#input
match #name() {
Ok(r) => {
finish_data(&r.bin);
},
Err(e) => {
let error_msg = e.to_string();
finish_data(&error_msg.as_bytes());
}
}
}
},
// Return the inner structure from unwrap result
// This is for a scenario that you take care the result but not using Rust client
"auto" => quote! {
Expand All @@ -76,7 +131,6 @@ pub fn ewasm_main(attr: TokenStream, item: TokenStream) -> TokenStream {
Ok(r) => {
let bin = bincode::serialize(&r).expect("The resuslt of `ewasm_main` should be serializable");
finish_data(&bin);

},
Err(e) => {
let error_msg = e.to_string();
Expand Down Expand Up @@ -614,6 +668,14 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
pub records: Vec<#wrapper_name>
}

impl sewup::primitives::IntoEwasmAny for #protocol_name {
fn into_ewasm_any(self) -> sewup::primitives::EwasmAny {
sewup::primitives::EwasmAny {
bin: sewup::bincode::serialize(&self).expect("The input should be serializable")
}
}
}

impl #protocol_name {
pub fn set_select_fields(&mut self, fields: Vec<String>) {
if fields.is_empty() {
Expand Down Expand Up @@ -695,10 +757,11 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
#[cfg(target_arch = "wasm32")]
pub mod #lower_mod_name {
use super::*;
use sewup::primitives::IntoEwasmAny;
pub type Protocol = #protocol_name;
pub type Wrapper = #wrapper_name;
pub type _Instance = #struct_name;
pub fn get(proc: Protocol) -> sewup::Result<()> {
pub fn get(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
if proc.filter {
let mut raw_output: Vec<Wrapper> = Vec::new();
Expand Down Expand Up @@ -734,39 +797,35 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
)*
}
}
let output: Protocol = raw_output.into();
sewup::utils::ewasm_return(sewup_derive::ewasm_output_from!(output));
let p: #protocol_name = raw_output.into();
Ok(p.into_ewasm_any())
} else {
let raw_output = table.get_record(proc.records[0].id.unwrap_or_default())?;
let mut output: Protocol = raw_output.into();
output.records[0].id = proc.records[0].id;
sewup::utils::ewasm_return(sewup_derive::ewasm_output_from!(output));
let mut output_proc: Protocol = raw_output.into();
output_proc.records[0].id = proc.records[0].id;
Ok(output_proc.into_ewasm_any())
}
Ok(())
}
pub fn create(proc: Protocol) -> sewup::Result<()> {
pub fn create(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let mut output = proc.clone();
output.records[0].id = Some(table.add_record(proc.records[0].into())?);
let mut output_proc = proc.clone();
output_proc.records[0].id = Some(table.add_record(proc.records[0].into())?);
table.commit()?;
sewup::utils::ewasm_return(sewup_derive::ewasm_output_from!(output));
Ok(())
Ok(output_proc.into_ewasm_any())
}
pub fn update(proc: Protocol) -> sewup::Result<()> {
pub fn update(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let id = proc.records[0].id.unwrap_or_default();
table.update_record(id, Some(proc.records[0].clone().into()))?;
table.commit()?;
sewup::utils::ewasm_return(sewup_derive::ewasm_output_from!(proc));
Ok(())
Ok(proc.into_ewasm_any())
}
pub fn delete(proc: Protocol) -> sewup::Result<()> {
pub fn delete(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let id = proc.records[0].id.unwrap_or_default();
table.update_record(id, None)?;
table.commit()?;
sewup::utils::ewasm_return(sewup_derive::ewasm_output_from!(proc));
Ok(())
Ok(proc.into_ewasm_any())
}
}
#[cfg(not(target_arch = "wasm32"))]
Expand Down

0 comments on commit ecebe8c

Please sign in to comment.