Skip to content

Commit

Permalink
Implement #[belongs_to] for one to many relationship (#135)
Browse files Browse the repository at this point in the history
- Implement #[belongs_to] and add table relation example
- Hot fix (#136)
  • Loading branch information
yanganto committed Jul 27, 2021
1 parent f5bb497 commit 641fcdd
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 19 deletions.
40 changes: 39 additions & 1 deletion examples/rdb-contract/src/lib.rs
@@ -1,15 +1,23 @@
use serde_derive::{Deserialize, Serialize};

use sewup::rdb::errors::Error as LibError;
use sewup_derive::{ewasm_fn, ewasm_fn_sig, ewasm_main, ewasm_test};

mod errors;

mod modules;
use modules::{person, Person, PERSON};
use modules::{person, post, Person, Post, PERSON, POST};

#[derive(Serialize, Deserialize)]
pub struct Input {
id: usize,
}

#[ewasm_fn]
fn init_db_with_tables() -> anyhow::Result<sewup::primitives::EwasmAny> {
let mut db = sewup::rdb::Db::new()?;
db.create_table::<Person>();
db.create_table::<Post>();
db.commit()?;
Ok(().into())
}
Expand Down Expand Up @@ -77,6 +85,20 @@ fn get_childern() -> anyhow::Result<sewup::primitives::EwasmAny> {
Ok(sewup::primitives::EwasmAny::from(protocol))
}

#[ewasm_fn]
fn get_post_author(input: Input) -> anyhow::Result<sewup::primitives::EwasmAny> {
let table = sewup::rdb::Db::load(None)?.table::<Post>()?;
let post = table.get_record(input.id)?;

// ( Person <- 1 --- many -> Post )
// use relationship to get the post owner
let owner = post.person()?;

// This is an example show output not wrappered into protocol,
// just return instance itself
Ok(sewup::primitives::EwasmAny::from(owner))
}

#[ewasm_main(auto)]
fn main() -> anyhow::Result<sewup::primitives::EwasmAny> {
use sewup_derive::ewasm_input_from;
Expand All @@ -87,9 +109,14 @@ fn main() -> anyhow::Result<sewup::primitives::EwasmAny> {
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!(post::get) => ewasm_input_from!(contract move post::get),
ewasm_fn_sig!(post::create) => ewasm_input_from!(contract move post::create),
ewasm_fn_sig!(post::update) => ewasm_input_from!(contract move post::update),
ewasm_fn_sig!(post::delete) => ewasm_input_from!(contract move post::delete),
ewasm_fn_sig!(check_version_and_features) => {
check_version_and_features(0, vec![sewup::rdb::Feature::Default])
}
ewasm_fn_sig!(get_post_author) => ewasm_input_from!(contract move get_post_author),
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(),
Expand Down Expand Up @@ -118,10 +145,21 @@ mod tests {
expect_output.set_id(1);
ewasm_auto_assert_eq!(person::create(create_input), expect_output);

let post = Post {
words: 100,
person_id: 1,
};
let mut create_post_input = post::protocol(post);
let mut expect_post_output = create_post_input.clone();
expect_post_output.set_id(1);
ewasm_auto_assert_eq!(post::create(create_post_input), expect_post_output);

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

ewasm_auto_assert_eq!(get_post_author(Input { id: 1 }), person);

let child = Person {
trusted: false,
age: 9,
Expand Down
11 changes: 10 additions & 1 deletion examples/rdb-contract/src/modules.rs
Expand Up @@ -5,8 +5,17 @@ use sewup_derive::Table;
// to communicate with these handler, you will need protocol.
// The protocol is easy to build by the `{struct_name}::protocol`, `{struct_name}::Protocol`,
// please check out the test case in the end of this document
#[derive(Table, Default, Clone, Serialize, Deserialize)]
#[derive(Table, Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Person {
pub(crate) trusted: bool,
pub(crate) age: u8,
}

#[derive(Table, Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[belongs_to(Person)]
pub struct Post {
pub(crate) words: u8,

// Curretly, this field need to set up manually, this will be enhance later
pub(crate) person_id: usize,
}
70 changes: 53 additions & 17 deletions sewup-derive/src/lib.rs
Expand Up @@ -613,9 +613,26 @@ pub fn derive_value(item: TokenStream) -> TokenStream {
/// assert!(default_input != default_person_input)
/// ```
#[cfg(feature = "rdb")]
#[proc_macro_derive(Table)]
#[proc_macro_derive(Table, attributes(belongs_to, belongs_none_or))]
pub fn derive_table(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);
let attrs = &input.attrs;
let mut belongs_to: Option<String> = None;
for a in attrs.iter() {
let syn::Attribute { path, tokens, .. } = a;
let attr_name = path.segments.first().map(|s| s.ident.to_string());
if Some("belongs_to".to_string()) == attr_name {
belongs_to = Some(
tokens
.to_string()
.strip_prefix('(')
.expect("#[belongs_to(table_name)] is not correct")
.strip_suffix(')')
.expect("#[belongs_to(table_name)] is not correct")
.to_string(),
);
}
}
let struct_name = &input.ident;
let fields_with_type = match &input.data {
syn::Data::Struct(syn::DataStruct {
Expand Down Expand Up @@ -660,16 +677,15 @@ pub fn derive_table(item: TokenStream) -> TokenStream {

let protocol_name = Ident::new(&format!("{}Protocol", struct_name), Span::call_site());
let wrapper_name = Ident::new(&format!("{}Wrapper", struct_name), Span::call_site());
let captal_mod_name = Ident::new(
let captal_name = Ident::new(
&format!("{}", struct_name).to_ascii_uppercase(),
Span::call_site(),
);
let lower_mod_name = Ident::new(
let lower_name = Ident::new(
&format!("{}", struct_name).to_ascii_lowercase(),
Span::call_site(),
);

quote!(
let mut output = quote!(
impl sewup::rdb::traits::Record for #struct_name {}

#[derive(Clone, sewup::Serialize, sewup::Deserialize)]
Expand Down Expand Up @@ -738,7 +754,7 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
}
}
}
pub mod #captal_mod_name {
pub mod #captal_name {
use sewup_derive::ewasm_fn_sig;
pub(crate) const GET_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::get());
pub(crate) const CREATE_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::create());
Expand Down Expand Up @@ -766,14 +782,14 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
}
}
#[cfg(target_arch = "wasm32")]
pub mod #lower_mod_name {
pub mod #lower_name {
use super::*;
use sewup::primitives::IntoEwasmAny;
pub type Protocol = #protocol_name;
pub type Wrapper = #wrapper_name;
pub type _Instance = #struct_name;
pub type _InstanceType = #struct_name;
pub fn get(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
if proc.filter {
let mut raw_output: Vec<Wrapper> = Vec::new();
for r in table.all_records()?.drain(..){
Expand Down Expand Up @@ -818,37 +834,37 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
}
}
pub fn create(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
let mut output_proc = proc.clone();
output_proc.records[0].id = Some(table.add_record(proc.records[0].into())?);
table.commit()?;
Ok(output_proc.into_ewasm_any())
}
pub fn update(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
let id = proc.records[0].id.unwrap_or_default();
table.update_record(id, Some(proc.records[0].clone().into()))?;
table.commit()?;
Ok(proc.into_ewasm_any())
}
pub fn delete(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
let mut table = sewup::rdb::Db::load(None)?.table::<_Instance>()?;
let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
let id = proc.records[0].id.unwrap_or_default();
table.update_record(id, None)?;
table.commit()?;
Ok(proc.into_ewasm_any())
}
}
#[cfg(not(target_arch = "wasm32"))]
pub mod #lower_mod_name {
pub mod #lower_name {
use super::*;
pub type Protocol = #protocol_name;
pub type Wrapper = #wrapper_name;
pub type _Instance = #struct_name;
pub type _InstanceType = #struct_name;
pub type Query = Wrapper;

#[inline]
pub fn protocol(instance: _Instance) -> Protocol {
pub fn protocol(instance: _InstanceType) -> Protocol {
instance.into()
}
impl Protocol {
Expand All @@ -860,7 +876,7 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
true
}
}
pub fn query(instance: _Instance) -> Wrapper {
pub fn query(instance: _InstanceType) -> Wrapper {
instance.into()
}

Expand All @@ -874,7 +890,27 @@ pub fn derive_table(item: TokenStream) -> TokenStream {
}
}
}
).into()
).to_string();

if let Some(parent_table) = belongs_to {
let lower_parent_table = &format!("{}", &parent_table).to_ascii_lowercase();
let parent_table = Ident::new(&parent_table, Span::call_site());
let lower_parent_table_ident = Ident::new(&lower_parent_table, Span::call_site());
let field_name = &format!("{}_id", lower_parent_table);

output += &quote! {
impl #struct_name {
pub fn #lower_parent_table_ident (&self) -> sewup::Result<#parent_table> {
let id: usize = sewup::utils::get_field_by_name(self, #field_name);
let parent_table = sewup::rdb::Db::load(None)?.table::<#parent_table>()?;
parent_table.get_record(id)
}
}
}
.to_string();
}

output.parse().unwrap()
}

/// helps you setup the test mododule, and test cases in contract.
Expand Down

0 comments on commit 641fcdd

Please sign in to comment.