Quellcode durchsuchen

:sparkles: Simplify system arguments (#35)

* :sparkles: Simplify system arguments

* :sparkles: Add arguments helper macro
Gabriele Picco vor 1 Jahr
Ursprung
Commit
71c10f54fb

+ 1 - 0
.github/workflows/publish-bolt-crates.yml

@@ -191,6 +191,7 @@ jobs:
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-lang/attribute/system/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-lang/attribute/system-input/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-lang/attribute/extra-accounts/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
+          cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-lang/attribute/arguments/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-lang/attribute/bolt-program/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-helpers/attribute/system-template/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG
           cargo publish $DRY_RUN_FLAG --manifest-path=crates/bolt-helpers/attribute/world-apply/Cargo.toml --token $CRATES_TOKEN $NO_VERIFY_FLAG

+ 10 - 0
Cargo.lock

@@ -792,6 +792,15 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
 
+[[package]]
+name = "bolt-attribute-bolt-arguments"
+version = "0.1.1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "bolt-attribute-bolt-component"
 version = "0.1.1"
@@ -907,6 +916,7 @@ version = "0.1.1"
 dependencies = [
  "ahash 0.8.6",
  "anchor-lang",
+ "bolt-attribute-bolt-arguments",
  "bolt-attribute-bolt-component",
  "bolt-attribute-bolt-component-deserialize",
  "bolt-attribute-bolt-component-id",

+ 1 - 0
Cargo.toml

@@ -20,6 +20,7 @@ bolt-attribute-bolt-component = { path = "crates/bolt-lang/attribute/component",
 bolt-attribute-bolt-system = { path = "crates/bolt-lang/attribute/system", version = "=0.1.1"}
 bolt-attribute-bolt-system-input = { path = "crates/bolt-lang/attribute/system-input", version = "=0.1.1" }
 bolt-attribute-bolt-extra-accounts = { path = "crates/bolt-lang/attribute/extra-accounts", version = "=0.1.1" }
+bolt-attribute-bolt-arguments = { path = "crates/bolt-lang/attribute/arguments", version = "=0.1.1" }
 bolt-attribute-bolt-component-deserialize = { path = "crates/bolt-lang/attribute/component-deserialize", version = "=0.1.1" }
 bolt-attribute-bolt-component-id = { path = "crates/bolt-lang/attribute/component-id", version = "=0.1.1" }
 bolt-helpers-system-template = { path = "crates/bolt-helpers/attribute/system-template", version = "=0.1.1" }

+ 36 - 1
cli/src/rust_template.rs

@@ -39,7 +39,7 @@ pub(crate) fn create_system(name: &str) -> Result<()> {
             PathBuf::from("Cargo.toml".to_string()),
             workspace_manifest().to_string(),
         ),
-        (program_path.join("Cargo.toml"), cargo_toml(name)),
+        (program_path.join("Cargo.toml"), cargo_toml_with_serde(name)),
         (program_path.join("Xargo.toml"), xargo_toml().to_string()),
     ] as Files;
 
@@ -567,6 +567,41 @@ anchor-lang = {3}
     )
 }
 
+/// TODO: Remove serde dependency
+fn cargo_toml_with_serde(name: &str) -> String {
+    format!(
+        r#"[package]
+name = "{0}"
+version = "{2}"
+description = "Created with Bolt"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "{1}"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+idl-build = ["anchor-lang/idl-build"]
+
+[dependencies]
+bolt-lang = "{2}"
+anchor-lang = {3}
+serde = {{ version = "1.0", features = ["derive"] }}
+"#,
+        name,
+        name.to_snake_case(),
+        VERSION,
+        // Todo use stable version once new IDL standard is released
+        //anchor_cli::VERSION,
+        ANCHOR_CLI_VERSION
+    )
+}
+
 fn xargo_toml() -> &'static str {
     r#"[target.bpfel-unknown-unknown.dependencies.std]
 features = []

+ 1 - 0
crates/bolt-lang/Cargo.toml

@@ -19,6 +19,7 @@ bolt-attribute-bolt-system-input = { workspace = true }
 bolt-attribute-bolt-component-deserialize = { workspace = true }
 bolt-attribute-bolt-component-id = { workspace = true }
 bolt-attribute-bolt-extra-accounts = { workspace = true }
+bolt-attribute-bolt-arguments = { workspace = true }
 
 # Bolt Programs
 world = { workspace = true }

+ 17 - 0
crates/bolt-lang/attribute/arguments/Cargo.toml

@@ -0,0 +1,17 @@
+[package]
+name = "bolt-attribute-bolt-arguments"
+description = "bolt-attribute-bolt-arguments"
+version = { workspace = true }
+authors = { workspace = true }
+repository = { workspace = true }
+homepage = { workspace = true }
+license = { workspace = true }
+edition = { workspace = true }
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { workspace = true }
+quote = { workspace = true }
+proc-macro2 = { workspace = true }

+ 14 - 0
crates/bolt-lang/attribute/arguments/src/lib.rs

@@ -0,0 +1,14 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput};
+
+#[proc_macro_attribute]
+pub fn arguments(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    let mut input = parse_macro_input!(item as DeriveInput);
+    let new_attr: Attribute = parse_quote! { #[derive(bolt_lang::serde::Deserialize)] };
+    input.attrs.push(new_attr);
+    let expanded = quote! {
+        #input
+    };
+    TokenStream::from(expanded)
+}

+ 0 - 2
crates/bolt-lang/attribute/component/src/lib.rs

@@ -61,8 +61,6 @@ pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
     input.attrs.push(additional_derives);
 
     let new_fn = define_new_fn(&input);
-    // print new_fn
-    println!("{:?}", new_fn);
 
     add_bolt_metadata(&mut input);
 

+ 42 - 3
crates/bolt-lang/attribute/system/src/lib.rs

@@ -1,6 +1,6 @@
 use proc_macro::TokenStream;
 use proc_macro2::Ident;
-use quote::quote;
+use quote::{quote, ToTokens};
 use syn::{
     parse_macro_input, parse_quote, visit_mut::VisitMut, Expr, FnArg, GenericArgument, ItemFn,
     ItemMod, ItemStruct, PathArguments, ReturnType, Stmt, Type, TypePath,
@@ -104,7 +104,7 @@ impl VisitMut for SystemTransform {
                             item_fn.sig.ident.span(),
                         );
                     }
-                    if !Self::check_is_vec_u8(type_path) {
+                    if !Self::check_is_result_vec_u8(type_path) {
                         Self::modify_fn_return_type(item_fn, self.return_values);
                         // Modify the return statement inside the function body
                         let block = &mut item_fn.block;
@@ -116,6 +116,8 @@ impl VisitMut for SystemTransform {
                     }
                 }
             }
+            // If second argument is not Vec<u8>, modify it to be so and use parse_args
+            Self::modify_args(item_fn);
         }
     }
 
@@ -159,7 +161,7 @@ impl VisitMut for SystemTransform {
 
 impl SystemTransform {
     // Helper function to check if a type is `Vec<u8>` or `(Vec<u8>, Vec<u8>, ...)`
-    fn check_is_vec_u8(ty: &TypePath) -> bool {
+    fn check_is_result_vec_u8(ty: &TypePath) -> bool {
         if let Some(segment) = ty.path.segments.last() {
             if segment.ident == "Result" {
                 if let PathArguments::AngleBracketed(args) = &segment.arguments {
@@ -243,6 +245,43 @@ impl SystemTransform {
         }
         None
     }
+
+    fn modify_args(item_fn: &mut ItemFn) {
+        if item_fn.sig.inputs.len() >= 2 {
+            let second_arg = &mut item_fn.sig.inputs[1];
+            let is_vec_u8 = if let FnArg::Typed(syn::PatType { ty, .. }) = second_arg {
+                match &**ty {
+                    Type::Path(type_path) => {
+                        if let Some(segment) = type_path.path.segments.first() {
+                            segment.ident == "Vec" && Self::is_u8_vec(segment)
+                        } else {
+                            false
+                        }
+                    }
+                    _ => false,
+                }
+            } else {
+                false
+            };
+            if !is_vec_u8 {
+                if let FnArg::Typed(pat_type) = second_arg {
+                    let original_type = pat_type.ty.to_token_stream();
+                    let arg_original_name = pat_type.pat.to_token_stream();
+                    if let syn::Pat::Ident(ref mut pat_ident) = *pat_type.pat {
+                        let new_ident_name = format!("_{}", pat_ident.ident);
+                        pat_ident.ident =
+                            Ident::new(&new_ident_name, proc_macro2::Span::call_site());
+                    }
+                    let arg_name = pat_type.pat.to_token_stream();
+                    pat_type.ty = Box::new(syn::parse_quote! { Vec<u8> });
+                    let parse_stmt: Stmt = parse_quote! {
+                        let #arg_original_name = parse_args::<#original_type>(&#arg_name);
+                    };
+                    item_fn.block.stmts.insert(0, parse_stmt);
+                }
+            }
+        }
+    }
 }
 
 /// Visits the AST to extract the number of input components

+ 3 - 0
crates/bolt-lang/src/lib.rs

@@ -4,6 +4,7 @@ pub use anchor_lang::{
     AccountDeserialize, AccountSerialize, AnchorDeserialize, AnchorSerialize, Bumps, Result,
 };
 
+pub use bolt_attribute_bolt_arguments::arguments;
 pub use bolt_attribute_bolt_component::component;
 pub use bolt_attribute_bolt_component_deserialize::component_deserialize;
 pub use bolt_attribute_bolt_component_id::component_id;
@@ -18,6 +19,8 @@ pub use world;
 pub use world::program::World;
 pub use world::Entity;
 
+pub use serde;
+
 use std::str;
 use std::str::FromStr;
 

+ 1 - 1
examples/system-simple-movement/Cargo.toml

@@ -22,6 +22,6 @@ idl-build = ["anchor-lang/idl-build"]
 
 [dependencies]
 anchor-lang = { workspace = true }
+serde = { workspace = true }
 bolt-lang = { path = "../../crates/bolt-lang" }
 bolt-types = { version = "0.1.1", path = "../../crates/types" }
-serde = { version = "*", features = ["derive"] }

+ 3 - 5
examples/system-simple-movement/src/lib.rs

@@ -5,9 +5,7 @@ declare_id!("FSa6qoJXFBR3a7ThQkTAMrC15p6NkchPEjBdd4n6dXxA");
 #[system]
 pub mod system_simple_movement {
 
-    pub fn execute(ctx: Context<Components>, args_p: Vec<u8>) -> Result<Components> {
-        let args = parse_args::<Args>(&args_p);
-
+    pub fn execute(ctx: Context<Components>, args: Args) -> Result<Components> {
         // Compute the new position based on the direction
         let (dx, dy) = match args.direction {
             Direction::Left => (-1, 0),
@@ -28,12 +26,12 @@ pub mod system_simple_movement {
     }
 
     // Define the structs to deserialize the arguments
-    #[derive(serde::Serialize, serde::Deserialize)]
+    #[arguments]
     struct Args {
         direction: Direction,
     }
 
-    #[derive(serde::Serialize, serde::Deserialize)]
+    #[arguments]
     pub enum Direction {
         Left,
         Right,