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
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dan_layer/engine/tests/hello_world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"

[dependencies]
tari_template_abi = { path = "../../../template_abi" }
tari_template_macros = { path = "../macros" }
tari_template_macros = { path = "../../../template_macros" }
common = { path = "../common" }

[profile.release]
Expand Down
3 changes: 2 additions & 1 deletion dan_layer/engine/tests/hello_world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

use tari_template_macros::template;

template! {
#[template]
mod hello_world {
struct HelloWorld {}

impl HelloWorld {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ edition = "2021"
proc-macro = true

[dependencies]
tari_template_abi = { path = "../../../template_abi" }
tari_template_abi = { path = "../template_abi" }
syn = { version = "1.0.98", features = ["full"] }
proc-macro2 = "1.0.42"
quote = "1.0.20"
quote = "1.0.20"

[dev-dependencies]
indoc = "1.0.6"
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::Comma,
Error,
FnArg,
Ident,
ImplItem,
ImplItemMethod,
ItemImpl,
ItemMod,
ItemStruct,
Result,
ReturnType,
Expand All @@ -44,8 +46,32 @@ pub struct TemplateAst {

impl Parse for TemplateAst {
fn parse(input: ParseStream) -> Result<Self> {
let struct_section: ItemStruct = input.parse()?;
let impl_section = input.parse()?;
// parse the "mod" block
let module: ItemMod = input.parse()?;

// get the contents of the "mod" block
let items = match module.content {
Some((_, items)) => items,
None => return Err(Error::new(module.ident.span(), "empty module")),
};

// there should be two items: the "struct" and the "impl" blocks
if items.len() != 2 {
return Err(Error::new(module.ident.span(), "invalid number of module sections"));
}

// get the "struct" block
let struct_section = match &items[0] {
syn::Item::Struct(struct_item) => struct_item.clone(),
_ => return Err(Error::new(module.ident.span(), "the first section is not a 'struct'")),
};

// get the "impl" block
let impl_section = match &items[1] {
syn::Item::Impl(impl_item) => impl_item.clone(),
_ => return Err(Error::new(module.ident.span(), "the second section is not an 'impl'")),
};

let template_name = struct_section.ident.clone();

Ok(Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ mod template;

use proc_macro::TokenStream;

#[proc_macro]
pub fn template(input: TokenStream) -> TokenStream {
template::generate_template(proc_macro2::TokenStream::from(input))
#[proc_macro_attribute]
pub fn template(_attr: TokenStream, item: TokenStream) -> TokenStream {
template::generate_template(proc_macro2::TokenStream::from(item))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ fn generate_abi_type(rust_type: &str) -> Expr {
mod tests {
use std::str::FromStr;

use indoc::indoc;
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse2;
Expand All @@ -101,9 +102,20 @@ mod tests {

#[test]
fn test_hello_world() {
let input = TokenStream::from_str(
"struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }",
)
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() {}
}
}
"})
Comment on lines +105 to +118
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

.unwrap();

let ast = parse2::<TemplateAst>(input).unwrap();
Expand All @@ -112,16 +124,28 @@ mod tests {

assert_code_eq(output, quote! {
#[no_mangle]
pub extern "C" fn HelloWorld_abi() -> *mut u8 {
pub extern "C" fn Foo_abi() -> *mut u8 {
use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type};

let template = TemplateDef {
template_name: "HelloWorld".to_string(),
functions: vec![ FunctionDef {
name: "greet".to_string(),
arguments: vec![],
output: Type::String,
}],
template_name: "Foo".to_string(),
functions: vec![
FunctionDef {
name: "no_args_function".to_string(),
arguments: vec![],
output: Type::String,
},
FunctionDef {
name: "some_args_function".to_string(),
arguments: vec![Type::I8, Type::String],
output: Type::U32,
},
FunctionDef {
name: "no_return_function".to_string(),
arguments: vec![],
output: Type::Unit,
}
],
};

let buf = encode_with_len(&template);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn get_function_blocks(ast: &TemplateAst) -> Vec<Expr> {
mod tests {
use std::str::FromStr;

use indoc::indoc;
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse2;
Expand All @@ -91,9 +92,16 @@ mod tests {

#[test]
fn test_hello_world() {
let input = TokenStream::from_str(
"struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }",
)
let input = TokenStream::from_str(indoc! {"
mod hello_world {
struct HelloWorld {}
impl HelloWorld {
pub fn greet() -> String {
\"Hello World!\".to_string()
}
}
}
"})
.unwrap();

let ast = parse2::<TemplateAst>(input).unwrap();
Expand Down