Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(dan): template macro as an attribute macro #4361

Merged
merged 4 commits into from
Jul 31, 2022

Conversation

mrnaveira
Copy link
Contributor

Description

  • Refactored the existing declarative macro (template!) into an attribute one (#[template])
  • Moved the macro project to the root of the dan_layer crate
  • Improved unit testing of the generated ABI section of the wasm, to assert different function signatures.
  • Used the indoc! macro in tests to make the code more readable

Motivation and Context

The existing template definition macro was created as a declarative macro, but code completion on some editors breaks when wrapped such types of macros.

The main goal of this PR is to transform the template macro into an attribute macro, to be used like:

use tari_template_macros::template;

#[template]
mod hello_world {
    struct HelloWorld {}

    impl HelloWorld { 
        pub fn greet() -> String {
            "Hello World!".to_string()
        }
    }
}

How Has This Been Tested?

  • Existing tests pass

sdbondi
sdbondi previously approved these changes Jul 29, 2022
Copy link
Member

@sdbondi sdbondi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🎉

Comment on lines +105 to +118
let input = TokenStream::from_str(indoc! {"
mod foo {
struct Foo {}
impl Foo {
pub fn no_args_function() -> String {
\"Hello World!\".to_string()
}
pub fn some_args_function(a: i8, b: String) -> u32 {
1_u32
}
pub fn no_return_function() {}
}
}
"})
Copy link
Member

@sdbondi sdbondi Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use native rust multiline strings

Suggested change
let input = TokenStream::from_str(indoc! {"
mod foo {
struct Foo {}
impl Foo {
pub fn no_args_function() -> String {
\"Hello World!\".to_string()
}
pub fn some_args_function(a: i8, b: String) -> u32 {
1_u32
}
pub fn no_return_function() {}
}
}
"})
let input = TokenStream::from_str(
r#"
mod hello_world {
struct HelloWorld {}
impl HelloWorld {
pub fn greet() -> String {
"Hello World!".to_string()
}
}
}
"#,
)
.unwrap();

Copy link
Contributor Author

@mrnaveira mrnaveira Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser was giving me a hard time with native multilines due to some span issues. Using indoc we can avoid those kind of issues and it's a dev-dependency used only in tests, so my vote goes to indoc.

Copy link
Member

@sdbondi sdbondi Jul 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation - sounds good to me

@aviator-app aviator-app bot merged commit d012823 into tari-project:development Jul 31, 2022
stringhandler pushed a commit that referenced this pull request Aug 3, 2022
Description
---
* Function parameters are correctly encoded and passed to the user function
* Component parameters (i.e. `self`) are transparently handled to the user code:
    1. The ABI specifies a `u32` type component id
    2. The state is retrieved from the engine with that component id (not the real call yet, as it's not implemented in the engine)
    3. The state is decoded and passed to the function on the `self` parameter
    4. If the attribute is mutable (`&mut self`) then we call the engine to update the component state
* Unit return types (`()`) are now supported in functions (see the `State.set` function as an example)
* Expanded the `ast.rs` module with convenient functions to detect `self` and constructor functions

Motivation and Context
---
Following the previous work on the template macro (#4358, #4361), this PR aims to solve some of the previous limitations:
* The function arguments must be processed and encoded
* Struct fields and `self` must be handled
* Calls to the tari engine import should be done instead of mocked

With those implemented, the `state` test example is now written as:
```
use tari_template_macros::template;

#[template]
mod state_template {
    pub struct State {
        pub value: u32,
    }

    impl State {
        pub fn new() -> Self {
            Self { value: 0 }
        }

        pub fn set(&mut self, value: u32) {
            self.value = value;
        }

        pub fn get(&self) -> u32 {
            self.value
        }
    }
}
```
Please keep in mind that this is the simplest example that manages the contract state, but it currently supports function logic as complex as the user wants, as long as it is valid Rust code.

Also, for now I didn't find necessary to mark constructor functions in any special way. Right now, a function is automatically considered a constructor (and the component instantiated) if it returns `Self`.

Lastly, as the state managing itself is not yet implemented on the engine, state is not conserved between calls. But this PR encapsulates component related logic in a single module (`component.rs`) so it should be relatively simple to implement in the future.

How Has This Been Tested?
---
The unit test for the `state` example, now rewritten using the template macro, pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants