From df33cd48a1469fe790f0e7ac736928d8f16bdd78 Mon Sep 17 00:00:00 2001 From: ngundotra Date: Mon, 31 Oct 2022 13:15:58 -0400 Subject: [PATCH 1/6] idl instruction: add create account macro attr --- .../single_file/create_idl_instructions.json | 54 ++++ .../single_file/create_idl_instructions.rs | 5 + shank-idl/tests/instructions.rs | 17 + shank-macro-impl/src/converters.rs | 2 +- .../src/custom_type/custom_type_config.rs | 2 +- shank-macro-impl/src/error/program_error.rs | 7 +- shank-macro-impl/src/error/this_error.rs | 4 +- .../src/instruction/account_attrs.rs | 53 ++-- .../src/instruction/account_attrs_test.rs | 8 +- .../src/instruction/idl_instruction_attrs.rs | 298 ++++++++++++++++++ .../src/instruction/instruction.rs | 35 +- shank-macro-impl/src/instruction/mod.rs | 2 + shank-macro-impl/src/macros/program_id.rs | 6 +- shank-macro-impl/src/parsers/attrs.rs | 8 +- .../src/types/parsed_reference.rs | 9 +- shank-macro-impl/src/types/resolve_rust_ty.rs | 10 +- 16 files changed, 440 insertions(+), 80 deletions(-) create mode 100644 shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json create mode 100644 shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs create mode 100644 shank-macro-impl/src/instruction/idl_instruction_attrs.rs diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json new file mode 100644 index 0000000..798c41f --- /dev/null +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -0,0 +1,54 @@ +{ + "version": "", + "name": "", + "instructions": [ + { + "name": "Create", + "accounts": [ + { + "name": "from", + "isMut": true, + "isSigner": true, + "desc": "Payer of the transaction" + }, + { + "name": "to", + "isMut": true, + "isSigner": false, + "desc": "The deterministically defined 'state' account being created via `create_account_with_seed`" + }, + { + "name": "base", + "isMut": false, + "isSigner": false, + "desc": "The program-derived-address signing off on the account creation. Seeds = &[] + bump seed." + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "The system program" + }, + { + "name": "program", + "isMut": false, + "isSigner": false, + "desc": "The program whose state is being constructed" + } + ], + "args": [ + { + "name": "dataLen", + "type": "u64" + } + ], + "discriminant": { + "type": "u8", + "value": 0 + } + } + ], + "metadata": { + "origin": "shank" + } +} \ No newline at end of file diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs new file mode 100644 index 0000000..68eaa14 --- /dev/null +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs @@ -0,0 +1,5 @@ +#[derive(ShankInstruction)] +pub enum Instruction { + #[idl_instruction(Create)] + Create, +} diff --git a/shank-idl/tests/instructions.rs b/shank-idl/tests/instructions.rs index fd49f86..4e30c21 100644 --- a/shank-idl/tests/instructions.rs +++ b/shank-idl/tests/instructions.rs @@ -77,6 +77,23 @@ fn instruction_from_single_file_with_multiple_args() { assert_eq!(idl, expected_idl); } +#[test] +fn instruction_from_single_file_with_idl_instructions() { + let file = fixtures_dir() + .join("single_file") + .join("create_idl_instructions.rs"); + let idl = parse_file(&file, &ParseIdlConfig::optional_program_address()) + .expect("Parsing should not fail") + .expect("File contains IDL"); + + let expected_idl: Idl = serde_json::from_str(include_str!( + "./fixtures/instructions/single_file/create_idl_instructions.json" + )) + .unwrap(); + + assert_eq!(idl, expected_idl); +} + #[test] fn instruction_from_single_file_with_optional_account() { let file = fixtures_dir() diff --git a/shank-macro-impl/src/converters.rs b/shank-macro-impl/src/converters.rs index 809823d..70c7199 100644 --- a/shank-macro-impl/src/converters.rs +++ b/shank-macro-impl/src/converters.rs @@ -4,5 +4,5 @@ use syn::Error as ParseError; pub fn parse_error_into>(parse_err: T) -> Error { let parse_err: ParseError = parse_err.into(); Error::new(parse_err) - .context(format!("[ParseError] Run `cargo build` or `cargo check` in the program crate root for more details.")) + .context("[ParseError] Run `cargo build` or `cargo check` in the program crate root for more details.".to_string()) } diff --git a/shank-macro-impl/src/custom_type/custom_type_config.rs b/shank-macro-impl/src/custom_type/custom_type_config.rs index 12ff6bd..ed1f117 100644 --- a/shank-macro-impl/src/custom_type/custom_type_config.rs +++ b/shank-macro-impl/src/custom_type/custom_type_config.rs @@ -44,6 +44,6 @@ impl DetectCustomTypeConfig { saw_include = self.include_derives.contains(&derive); } } - return saw_include; + saw_include } } diff --git a/shank-macro-impl/src/error/program_error.rs b/shank-macro-impl/src/error/program_error.rs index 50d5e63..45adc81 100644 --- a/shank-macro-impl/src/error/program_error.rs +++ b/shank-macro-impl/src/error/program_error.rs @@ -43,7 +43,7 @@ impl ProgramError { |x| x.clone(), ); Self::parse_account_error_args( - &nested, + nested, &ident, variant_ident, variant_discriminant, @@ -101,8 +101,7 @@ impl TryFrom<&ParsedEnum> for ProgramErrors { .collect::>>>()?; let program_errors = program_errors .into_iter() - .filter(Option::is_some) - .map(Option::unwrap) + .flatten() .collect::>(); Ok(ProgramErrors(program_errors)) @@ -130,7 +129,7 @@ impl ProgramError { "shank expects no more than one #[error]s per variant", )) } else { - Ok(program_errors.to_owned().first().map(|x| x.clone())) + Ok(program_errors.first().cloned()) } } } diff --git a/shank-macro-impl/src/error/this_error.rs b/shank-macro-impl/src/error/this_error.rs index 5a4d4f8..a5e9966 100644 --- a/shank-macro-impl/src/error/this_error.rs +++ b/shank-macro-impl/src/error/this_error.rs @@ -44,9 +44,7 @@ pub fn extract_this_errors<'a>( .iter() .map(ProgramErrors::try_from) .collect::>>()? - .into_iter() - .map(|x| x.0) - .flatten() + .into_iter().flat_map(|x| x.0) .collect::>(); Ok(program_errors) } diff --git a/shank-macro-impl/src/instruction/account_attrs.rs b/shank-macro-impl/src/instruction/account_attrs.rs index b93da91..7749943 100644 --- a/shank-macro-impl/src/instruction/account_attrs.rs +++ b/shank-macro-impl/src/instruction/account_attrs.rs @@ -2,8 +2,8 @@ use std::convert::TryFrom; use proc_macro2::Span; use syn::{ - punctuated::Punctuated, Attribute, Error as ParseError, Ident, Lit, Meta, - MetaList, MetaNameValue, NestedMeta, Result as ParseResult, Token, + punctuated::Punctuated, Attribute, Error as ParseError, Ident, Lit, Meta, MetaList, + MetaNameValue, NestedMeta, Result as ParseResult, Token, }; const IX_ACCOUNT: &str = "account"; @@ -34,9 +34,7 @@ impl InstructionAccount { } } - pub fn from_account_attr( - attr: &Attribute, - ) -> ParseResult { + pub fn from_account_attr(attr: &Attribute) -> ParseResult { let meta = &attr.parse_meta()?; match meta { @@ -45,7 +43,7 @@ impl InstructionAccount { || Ident::new("attr_ident", Span::call_site()), |x| x.clone(), ); - Self::parse_account_attr_args(ident, &nested) + Self::parse_account_attr_args(ident, nested) } Meta::Path(_) | Meta::NameValue(_) => Err(ParseError::new_spanned( attr, @@ -73,9 +71,7 @@ impl InstructionAccount { let mut optional = false; for meta in nested { - if let Some((ident, name, value)) = - string_assign_from_nested_meta(meta)? - { + if let Some((ident, name, value)) = string_assign_from_nested_meta(meta)? { // name/desc match name.as_str() { "desc" | "description" => desc = Some(value), @@ -86,14 +82,14 @@ impl InstructionAccount { )) } "name" => account_name = Some(value), - _ => return Err(ParseError::new_spanned( - ident, - "Only desc/description or name can be assigned strings", - )), + _ => { + return Err(ParseError::new_spanned( + ident, + "Only desc/description or name can be assigned strings", + )) + } }; - } else if let Some((ident, name)) = - identifier_from_nested_meta(meta) - { + } else if let Some((ident, name)) = identifier_from_nested_meta(meta) { // signer, writable, optional ... match name.as_str() { "signer" | "sign" | "sig" | "s" => signer = true, @@ -133,9 +129,7 @@ impl InstructionAccount { desc, optional, }), - None => { - Err(ParseError::new_spanned(nested, "Missing account name")) - } + None => Err(ParseError::new_spanned(nested, "Missing account name")), } } } @@ -145,7 +139,7 @@ impl TryFrom<&[Attribute]> for InstructionAccounts { fn try_from(attrs: &[Attribute]) -> ParseResult { let accounts = attrs - .into_iter() + .iter() .filter_map(InstructionAccount::is_account_attr) .map(InstructionAccount::from_account_attr) .collect::>>()?; @@ -176,14 +170,15 @@ fn string_assign_from_nested_meta( nested_meta: &NestedMeta, ) -> ParseResult> { match nested_meta { - NestedMeta::Meta(Meta::NameValue(MetaNameValue { - path, lit, .. - })) => { + NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. })) => { let ident = path.get_ident(); if let Some(ident) = ident { - let token = match lit { + let token = match lit { Lit::Str(lit) => Ok(lit.value()), - _ => Err(ParseError::new_spanned(ident, "#[account(desc)] arg needs to be assigning to a string")), + _ => Err(ParseError::new_spanned( + ident, + "#[account(desc)] arg needs to be assigning to a string", + )), }?; Ok(Some((ident.clone(), ident.to_string(), token))) } else { @@ -194,14 +189,10 @@ fn string_assign_from_nested_meta( } } -fn identifier_from_nested_meta( - nested_meta: &NestedMeta, -) -> Option<(Ident, String)> { +pub fn identifier_from_nested_meta(nested_meta: &NestedMeta) -> Option<(Ident, String)> { match nested_meta { NestedMeta::Meta(meta) => match meta { - Meta::Path(_) => { - meta.path().get_ident().map(|x| (x.clone(), x.to_string())) - } + Meta::Path(_) => meta.path().get_ident().map(|x| (x.clone(), x.to_string())), // ignore named values and lists _ => None, }, diff --git a/shank-macro-impl/src/instruction/account_attrs_test.rs b/shank-macro-impl/src/instruction/account_attrs_test.rs index 3cdc6d4..f4b88c7 100644 --- a/shank-macro-impl/src/instruction/account_attrs_test.rs +++ b/shank-macro-impl/src/instruction/account_attrs_test.rs @@ -47,7 +47,7 @@ impl From<&InstructionAccount> for InstructionAccountWithoutIdent { writable: *writable, signer: *signer, desc: desc.clone(), - optional: optional.clone(), + optional: *optional, } } } @@ -320,10 +320,10 @@ fn account_multiple_attrs() { InstructionAccountWithoutIdent { index: None, name: name.clone(), - writable: writable.clone(), - signer: signer.clone(), + writable: *writable, + signer: *signer, desc: desc.clone(), - optional: optional.clone(), + optional: *optional, } }, ) diff --git a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs new file mode 100644 index 0000000..8c9348d --- /dev/null +++ b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs @@ -0,0 +1,298 @@ +use std::convert::TryFrom; +use std::fmt; + +use proc_macro2::Span; +use syn::{ + punctuated::Punctuated, Attribute, Error as ParseError, Ident, Meta, MetaList, NestedMeta, + Result as ParseResult, Token, +}; + +use crate::{instruction::account_attrs::identifier_from_nested_meta, types::{RustType, RustTypeContext, Primitive}}; +use crate::types::{TypeKind, Composite}; + +use super::{InstructionAccount, InstructionAccounts, InstructionVariantFields}; + +const IX_IDL: &str = "idl_instruction"; + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum IdlInstruction { + Create, + CreateBuffer, + Write, + SetAuthority, + SetBuffer, +} + +impl IdlInstruction { + fn is_idl_instruction_attr(attr: &Attribute) -> Option<&Attribute> { + match attr.path.get_ident().map(|x| { + x.to_string().as_str() == IX_IDL + }) { + Some(true) => Some(attr), + _ => None, + } + } + + fn from_idl_instruction_attr(attr: &Attribute) -> ParseResult { + let meta = &attr.parse_meta()?; + match meta { + Meta::List(MetaList { nested, .. }) => { + let ident = attr.path.get_ident().map_or_else( + || Ident::new("attr_ident", Span::call_site()), + |x| x.clone(), + ); + Self::parse_idl_instruction_attr_args(ident, nested) + } + Meta::Path(_) | Meta::NameValue(_) => Err(ParseError::new_spanned( + attr, + "#[idl_instruction] attr requires list of arguments", + )), + } + } + + fn parse_idl_instruction_attr_args( + _ident: Ident, + nested: &Punctuated, + ) -> ParseResult { + if nested.is_empty() { + return Err(ParseError::new_spanned( + nested, + "#[idl_instruction] attr requires at least the idl instruction name", + )); + } + if nested.len() > 1 { + return Err(ParseError::new_spanned( + nested, + "#[idl_instruction] attr can only have one idl instruction name", + )); + } + let nested_meta = nested.first().unwrap(); + if let Some((ident, name)) = identifier_from_nested_meta(nested_meta) { + match name.as_str() { + "Create" => Ok(IdlInstruction::Create), + "CreateBuffer" => Ok(IdlInstruction::CreateBuffer), + "SetAuthority" => Ok(IdlInstruction::SetAuthority), + "SetBuffer" => Ok(IdlInstruction::SetBuffer), + "Write" => Ok(IdlInstruction::Write), + _ => Err(ParseError::new_spanned( + ident, + "Invalid/unknown idl instruction name", + )), + } + } else { + Err(ParseError::new_spanned( + nested, + "#[idl_instruction] attr can only have one idl instruction name", + )) + } + } + + pub fn to_accounts(&self, ident: Ident) -> InstructionAccounts { + match self { + IdlInstruction::Create => InstructionAccounts(vec![InstructionAccount { + ident: ident.clone(), + index: Some(0), + name: "from".to_string(), + desc: Some("Payer of the transaction".to_string()), + signer: true, + writable: true, + optional: false, + }, InstructionAccount { + ident: ident.clone(), + index: Some(1), + name: "to".to_string(), + desc: Some("The deterministically defined 'state' account being created via `create_account_with_seed`".to_string()), + signer: false, + writable: true, + optional: false, + }, InstructionAccount { + ident: ident.clone(), + index: Some(2), + name: "base".to_string(), + desc: Some("The program-derived-address signing off on the account creation. Seeds = &[] + bump seed.".to_string()), + signer: false, + writable: false, + optional: false, + }, InstructionAccount { + ident: ident.clone(), + index: Some(3), + name: "system_program".to_string(), + desc: Some("The system program".to_string()), + signer: false, + writable: false, + optional: false, + }, InstructionAccount { + ident, + index: Some(4), + name: "program".to_string(), + desc: Some("The program whose state is being constructed".to_string()), + signer: false, + writable: false, + optional: false, + }]), + IdlInstruction::CreateBuffer => + InstructionAccounts(vec![InstructionAccount { + ident: ident.clone(), + index: Some(0), + name: "buffer".to_string(), + desc: None, + signer: false, + writable: true, + optional: false, + }, InstructionAccount { + ident, + index: Some(1), + name: "authority".to_string(), + desc: None, + signer: true, + writable: false, + optional: false, + }]), + IdlInstruction::SetBuffer => + InstructionAccounts(vec![InstructionAccount { + ident: ident.clone(), + index: Some(0), + name: "buffer".to_string(), + desc: Some("The buffer with the new idl data.".to_string()), + signer: false, + writable: true, + optional: false, + }, InstructionAccount { + ident: ident.clone(), + index: Some(1), + name: "idl".to_string(), + desc: Some("The idl account to be updated with the buffer's data".to_string()), + signer: false, + writable: true, + optional: false, + }, InstructionAccount { + ident, + index: Some(2), + name: "authority".to_string(), + desc: None, + signer: true, + writable: false, + optional: false, + }]), + IdlInstruction::SetAuthority | IdlInstruction::Write => + InstructionAccounts(vec![InstructionAccount { + ident: ident.clone(), + index: Some(0), + name: "idl".to_string(), + desc: None, + signer: false, + writable: true, + optional: false, + }, InstructionAccount { + ident, + index: Some(2), + name: "authority".to_string(), + desc: None, + signer: true, + writable: false, + optional: false, + }]), + } + } + + pub fn to_instruction_fields(&self, ident: Ident) -> InstructionVariantFields { + match self { + IdlInstruction::Create => InstructionVariantFields::Named( + vec![( + "data_len".to_string(), + RustType { + ident: ident.clone(), + kind: TypeKind::Primitive(Primitive::U64), + context: RustTypeContext::Default, + reference: crate::types::ParsedReference::Owned, + } + )] + ), + IdlInstruction::CreateBuffer => { + InstructionVariantFields::Unnamed(vec![]) + } + IdlInstruction::SetAuthority => InstructionVariantFields::Named( + vec![( + "new_authority".to_string(), + RustType { + ident: ident.clone(), + kind: TypeKind::Primitive(Primitive::U8), + context: RustTypeContext::Default, + reference: crate::types::ParsedReference::Owned + } + )] + ), + IdlInstruction::SetBuffer => { + InstructionVariantFields::Unnamed(vec![]) + } + IdlInstruction::Write => InstructionVariantFields::Named( + vec![( + "idl_data".to_string(), + RustType { + ident: ident.clone(), + kind: TypeKind::Composite(Composite::Vec, vec![ + RustType { + ident: ident.clone(), + kind: TypeKind::Primitive(Primitive::U8), + context: RustTypeContext::CollectionItem, + reference: crate::types::ParsedReference::Owned + } + ]), + context: RustTypeContext::Default, + reference: crate::types::ParsedReference::Owned, + } + )] + ) + } + } +} + +#[derive(Debug, Clone)] +pub enum IdlInstructionError { + TooManyIdlInstructions(ParseError), + NotEnoughIdlInstructions, + OtherErr(syn::Error), +} + +impl fmt::Display for IdlInstructionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + IdlInstructionError::TooManyIdlInstructions(err) => { + write!(f, "{}", err) + } + IdlInstructionError::NotEnoughIdlInstructions => { + write!(f, "no #[idl_instruction] attributes found") + } + IdlInstructionError::OtherErr(err) => { + write!(f, "{}", err) + } + } + } +} + +impl TryFrom<&[Attribute]> for IdlInstruction { + type Error = IdlInstructionError; + + fn try_from(attrs: &[Attribute]) -> Result { + let idl_instructions = attrs + .iter() + .filter_map(IdlInstruction::is_idl_instruction_attr) + .map(IdlInstruction::from_idl_instruction_attr) + .collect::>>() + .map_err(IdlInstructionError::OtherErr)?; + + if idl_instructions.len() > 1 { + Err(IdlInstructionError::TooManyIdlInstructions( + ParseError::new_spanned( + attrs.get(0), + "Only one #[idl_instruction] attr is allowed per instruction", + ), + )) + } else if idl_instructions.is_empty() { + Err(IdlInstructionError::NotEnoughIdlInstructions) + } else { + let ix = *idl_instructions.get(0).unwrap(); + Ok(ix) + } + } +} diff --git a/shank-macro-impl/src/instruction/instruction.rs b/shank-macro-impl/src/instruction/instruction.rs index 28eb1c9..451d56a 100644 --- a/shank-macro-impl/src/instruction/instruction.rs +++ b/shank-macro-impl/src/instruction/instruction.rs @@ -15,7 +15,7 @@ use crate::{ use super::{ account_attrs::{InstructionAccount, InstructionAccounts}, - InstructionStrategies, InstructionStrategy, + IdlInstruction, InstructionStrategies, InstructionStrategy, }; // ----------------- @@ -33,8 +33,7 @@ impl Instruction { skip_derive_attr_check: bool, ) -> ParseResult> { if skip_derive_attr_check - || get_derive_attr(&item_enum.attrs, DERIVE_INSTRUCTION_ATTR) - .is_some() + || get_derive_attr(&item_enum.attrs, DERIVE_INSTRUCTION_ATTR).is_some() { let parsed_enum = ParsedEnum::try_from(item_enum)?; Instruction::try_from(&parsed_enum).map(Some) @@ -48,9 +47,7 @@ impl TryFrom<&ParsedEnum> for Option { type Error = ParseError; fn try_from(parsed_enum: &ParsedEnum) -> ParseResult { - match get_derive_attr(&parsed_enum.attrs, DERIVE_INSTRUCTION_ATTR) - .map(|_| parsed_enum) - { + match get_derive_attr(&parsed_enum.attrs, DERIVE_INSTRUCTION_ATTR).map(|_| parsed_enum) { Some(ix_enum) => ix_enum.try_into().map(Some), None => Ok(None), } @@ -106,19 +103,14 @@ impl TryFrom<&ParsedEnumVariant> for InstructionVariant { .. } = variant; - let field_tys: InstructionVariantFields = if fields.len() > 0 { + let mut field_tys: InstructionVariantFields = if !fields.is_empty() { // Determine if the InstructionType is tuple or struct variant let field = fields.get(0).unwrap(); match &field.ident { Some(_) => InstructionVariantFields::Named( fields .iter() - .map(|x| { - ( - x.ident.as_ref().unwrap().to_string(), - x.rust_type.clone(), - ) - }) + .map(|x| (x.ident.as_ref().unwrap().to_string(), x.rust_type.clone())) .collect(), ), None => InstructionVariantFields::Unnamed( @@ -130,8 +122,21 @@ impl TryFrom<&ParsedEnumVariant> for InstructionVariant { }; let attrs: &[Attribute] = attrs.as_ref(); - let accounts: InstructionAccounts = attrs.try_into()?; - let strategies: InstructionStrategies = attrs.into(); + let accounts: InstructionAccounts; + let strategies: InstructionStrategies; + + let idl_instruction = IdlInstruction::try_from(attrs); + if idl_instruction.is_ok() { + let idl_ix = idl_instruction.unwrap(); + accounts = idl_ix.to_accounts(ident.clone()); + field_tys = idl_ix.to_instruction_fields(ident.clone()); + strategies = InstructionStrategies(HashSet::::new()); + } else { + let err = idl_instruction.unwrap_err(); + println!("{}", err); + accounts = attrs.try_into()?; + strategies = attrs.into(); + } Ok(Self { ident: ident.clone(), diff --git a/shank-macro-impl/src/instruction/mod.rs b/shank-macro-impl/src/instruction/mod.rs index ff5d1d5..9d70b12 100644 --- a/shank-macro-impl/src/instruction/mod.rs +++ b/shank-macro-impl/src/instruction/mod.rs @@ -1,10 +1,12 @@ mod account_attrs; mod extract_instructions; +mod idl_instruction_attrs; mod instruction; mod strategy_attrs; pub use account_attrs::*; pub use extract_instructions::*; +pub use idl_instruction_attrs::*; pub use instruction::*; pub use strategy_attrs::*; diff --git a/shank-macro-impl/src/macros/program_id.rs b/shank-macro-impl/src/macros/program_id.rs index e88bad3..d245135 100644 --- a/shank-macro-impl/src/macros/program_id.rs +++ b/shank-macro-impl/src/macros/program_id.rs @@ -23,15 +23,13 @@ impl TryFrom<&[ItemMacro]> for ProgramId { literal, path_idents, }| { - literal - .map(|lit| { + literal.and_then(|lit| { if path.ends_with("declare_id") { - Some((path_idents[0].clone(), lit.clone())) + Some((path_idents[0].clone(), lit)) } else { None } }) - .flatten() }, ) .collect(); diff --git a/shank-macro-impl/src/parsers/attrs.rs b/shank-macro-impl/src/parsers/attrs.rs index f79818c..a50ecce 100644 --- a/shank-macro-impl/src/parsers/attrs.rs +++ b/shank-macro-impl/src/parsers/attrs.rs @@ -7,8 +7,7 @@ fn flattened_idents_from_nested_meta( nested: &Punctuated, ) -> Vec { nested - .iter() - .map(|nested| match nested { + .iter().flat_map(|nested| match nested { NestedMeta::Meta(Meta::Path(path)) => { path.segments.iter().map(|x| x.ident.clone()).collect() } @@ -17,14 +16,12 @@ fn flattened_idents_from_nested_meta( } _ => vec![], }) - .flatten() .collect() } pub fn get_derive_names(attrs: &[Attribute]) -> Vec { attrs - .iter() - .map(|attr| { + .iter().flat_map(|attr| { let meta = &attr.parse_meta(); match meta { Ok(Meta::List(MetaList { path, nested, .. })) => { @@ -46,7 +43,6 @@ pub fn get_derive_names(attrs: &[Attribute]) -> Vec { Err(_) => vec![], } }) - .flatten() .collect() } diff --git a/shank-macro-impl/src/types/parsed_reference.rs b/shank-macro-impl/src/types/parsed_reference.rs index 012519e..f1e03d2 100644 --- a/shank-macro-impl/src/types/parsed_reference.rs +++ b/shank-macro-impl/src/types/parsed_reference.rs @@ -32,14 +32,11 @@ impl From<&TypeReference> for ParsedReference { .. } = r; - let lifetime_ident = match lifetime { - Some(Lifetime { ident, .. }) => Some(ident.clone()), - None => None, - }; + let lifetime_ident = lifetime.as_ref().map(|Lifetime { ident, .. }| ident.clone()); match mutability.is_some() { - true => ParsedReference::RefMut(lifetime_ident.clone()), - false => ParsedReference::Ref(lifetime_ident.clone()), + true => ParsedReference::RefMut(lifetime_ident), + false => ParsedReference::Ref(lifetime_ident), } } } diff --git a/shank-macro-impl/src/types/resolve_rust_ty.rs b/shank-macro-impl/src/types/resolve_rust_ty.rs index 10fb6a5..0c936d2 100644 --- a/shank-macro-impl/src/types/resolve_rust_ty.rs +++ b/shank-macro-impl/src/types/resolve_rust_ty.rs @@ -123,7 +123,7 @@ fn ident_and_kind_from_path(path: &Path) -> (Ident, TypeKind) { let PathSegment { ident, arguments, .. } = path.segments.first().unwrap(); - (ident.clone(), ident_to_kind(&ident, &arguments)) + (ident.clone(), ident_to_kind(ident, arguments)) } fn len_from_expr(expr: &Expr) -> ParseResult { @@ -189,8 +189,8 @@ pub fn resolve_rust_ty( }; let len = len_from_expr(len)?; let inner_ty = RustType { - kind: inner_kind.clone(), - ident: inner_ident.clone(), + kind: inner_kind, + ident: inner_ident, reference: ParsedReference::Owned, context: RustTypeContext::CollectionItem, }; @@ -239,7 +239,7 @@ pub fn resolve_rust_ty( }; Ok(RustType { - ident: ident.clone(), + ident, kind, reference, context, @@ -277,7 +277,7 @@ fn ident_to_kind(ident: &Ident, arguments: &PathArguments) -> TypeKind { _ => {} } - return TypeKind::Value(Value::Custom(ident_str.clone())); + TypeKind::Value(Value::Custom(ident_str)) } // Composite Types From 30cfe8e3eddaf5d8bbbc128cb4ad1652698055e9 Mon Sep 17 00:00:00 2001 From: ngundotra Date: Mon, 31 Oct 2022 15:08:30 -0400 Subject: [PATCH 2/6] idl instruction: add create buffer + test --- .../single_file/create_idl_instructions.json | 20 +++++++++++++++++++ .../single_file/create_idl_instructions.rs | 2 ++ .../src/instruction/idl_instruction_attrs.rs | 15 ++++++-------- shank-macro-impl/src/types/resolve_rust_ty.rs | 1 + 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json index 798c41f..8705df4 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -46,6 +46,26 @@ "type": "u8", "value": 0 } + }, + { + "name": "CreateBuffer", + "accounts": [ + { + "name": "buffer", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 1 + } } ], "metadata": { diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs index 68eaa14..c41a092 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs @@ -2,4 +2,6 @@ pub enum Instruction { #[idl_instruction(Create)] Create, + #[idl_instruction(CreateBuffer)] + CreateBuffer, } diff --git a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs index 8c9348d..b6d7252 100644 --- a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs +++ b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs @@ -8,7 +8,7 @@ use syn::{ }; use crate::{instruction::account_attrs::identifier_from_nested_meta, types::{RustType, RustTypeContext, Primitive}}; -use crate::types::{TypeKind, Composite}; +use crate::types::{TypeKind, Composite, Value}; use super::{InstructionAccount, InstructionAccounts, InstructionVariantFields}; @@ -208,23 +208,17 @@ impl IdlInstruction { } )] ), - IdlInstruction::CreateBuffer => { - InstructionVariantFields::Unnamed(vec![]) - } IdlInstruction::SetAuthority => InstructionVariantFields::Named( vec![( "new_authority".to_string(), RustType { ident: ident.clone(), - kind: TypeKind::Primitive(Primitive::U8), + kind: TypeKind::Value(Value::Custom("Pubkey".to_string())), context: RustTypeContext::Default, reference: crate::types::ParsedReference::Owned } )] ), - IdlInstruction::SetBuffer => { - InstructionVariantFields::Unnamed(vec![]) - } IdlInstruction::Write => InstructionVariantFields::Named( vec![( "idl_data".to_string(), @@ -242,7 +236,10 @@ impl IdlInstruction { reference: crate::types::ParsedReference::Owned, } )] - ) + ), + IdlInstruction::CreateBuffer | IdlInstruction::SetBuffer => { + InstructionVariantFields::Unnamed(vec![]) + } } } } diff --git a/shank-macro-impl/src/types/resolve_rust_ty.rs b/shank-macro-impl/src/types/resolve_rust_ty.rs index 0c936d2..3a2c78c 100644 --- a/shank-macro-impl/src/types/resolve_rust_ty.rs +++ b/shank-macro-impl/src/types/resolve_rust_ty.rs @@ -238,6 +238,7 @@ pub fn resolve_rust_ty( } }; + println!("[resolve_rust_ty] {:?}: {:?}", ident, kind); Ok(RustType { ident, kind, From 4fb4313a80ae25cdfdae1987090c1cd1f1fbc093 Mon Sep 17 00:00:00 2001 From: ngundotra Date: Mon, 31 Oct 2022 15:12:25 -0400 Subject: [PATCH 3/6] idl instruction: add SetBuffer + test --- .../single_file/create_idl_instructions.json | 27 +++++++++++++++++++ .../single_file/create_idl_instructions.rs | 2 ++ .../src/instruction/idl_instruction_attrs.rs | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json index 8705df4..5a66093 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -66,6 +66,33 @@ "type": "u8", "value": 1 } + }, + { + "name": "SetBuffer", + "accounts": [ + { + "name": "buffer", + "isMut": true, + "isSigner": false, + "desc": "The buffer with the new idl data." + }, + { + "name": "idl", + "isMut": true, + "isSigner": false, + "desc": "The idl account to be updated with the buffer's data." + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 2 + } } ], "metadata": { diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs index c41a092..74464d6 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs @@ -4,4 +4,6 @@ pub enum Instruction { Create, #[idl_instruction(CreateBuffer)] CreateBuffer, + #[idl_instruction(SetBuffer)] + SetBuffer, } diff --git a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs index b6d7252..a596880 100644 --- a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs +++ b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs @@ -161,7 +161,7 @@ impl IdlInstruction { ident: ident.clone(), index: Some(1), name: "idl".to_string(), - desc: Some("The idl account to be updated with the buffer's data".to_string()), + desc: Some("The idl account to be updated with the buffer's data.".to_string()), signer: false, writable: true, optional: false, From c64189b179c1bbf70a969bddbf72d126c6335f1b Mon Sep 17 00:00:00 2001 From: ngundotra Date: Mon, 31 Oct 2022 15:29:33 -0400 Subject: [PATCH 4/6] idl instruction: add setAuthority + test --- .../single_file/create_idl_instructions.json | 25 +++++++++++++++++++ .../single_file/create_idl_instructions.rs | 2 ++ 2 files changed, 27 insertions(+) diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json index 5a66093..784bc06 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -93,6 +93,31 @@ "type": "u8", "value": 2 } + }, + { + "name": "SetAuthority", + "accounts": [ + { + "name": "idl", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "newAuthority", + "type": "publicKey" + } + ], + "discriminant": { + "type": "u8", + "value": 3 + } } ], "metadata": { diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs index 74464d6..942d8a2 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs @@ -6,4 +6,6 @@ pub enum Instruction { CreateBuffer, #[idl_instruction(SetBuffer)] SetBuffer, + #[idl_instruction(SetAuthority)] + SetAuthority, } From e8d893928a97dbb17fe64ac95b6022019da39369 Mon Sep 17 00:00:00 2001 From: ngundotra Date: Mon, 31 Oct 2022 15:40:12 -0400 Subject: [PATCH 5/6] idl instruction: add Write + test --- .../single_file/create_idl_instructions.json | 25 +++++++++++++++++++ .../single_file/create_idl_instructions.rs | 2 ++ 2 files changed, 27 insertions(+) diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json index 784bc06..8f544ca 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -118,6 +118,31 @@ "type": "u8", "value": 3 } + }, + { + "name": "Write", + "accounts": [ + { + "name": "idl", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "idlData", + "type": "bytes" + } + ], + "discriminant": { + "type": "u8", + "value": 4 + } } ], "metadata": { diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs index 942d8a2..96da3e8 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.rs @@ -8,4 +8,6 @@ pub enum Instruction { SetBuffer, #[idl_instruction(SetAuthority)] SetAuthority, + #[idl_instruction(Write)] + Write, } From bf77667fac14d132d87c42f5c21a03942c4e4b86 Mon Sep 17 00:00:00 2001 From: blockiosaurus Date: Thu, 4 Jan 2024 13:25:23 -0500 Subject: [PATCH 6/6] Updating to latest. --- .../single_file/create_idl_instructions.json | 28 ++++++++++---- shank-idl/tests/instructions.rs | 6 ++- .../src/instruction/idl_instruction_attrs.rs | 18 +++++++-- .../src/instruction/instruction.rs | 37 ++++++++++++------- 4 files changed, 65 insertions(+), 24 deletions(-) diff --git a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json index 8f544ca..47c53e7 100644 --- a/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json +++ b/shank-idl/tests/fixtures/instructions/single_file/create_idl_instructions.json @@ -9,31 +9,41 @@ "name": "from", "isMut": true, "isSigner": true, - "desc": "Payer of the transaction" + "docs": [ + "Payer of the transaction" + ] }, { "name": "to", "isMut": true, "isSigner": false, - "desc": "The deterministically defined 'state' account being created via `create_account_with_seed`" + "docs": [ + "The deterministically defined 'state' account being created via `create_account_with_seed`" + ] }, { "name": "base", "isMut": false, "isSigner": false, - "desc": "The program-derived-address signing off on the account creation. Seeds = &[] + bump seed." + "docs": [ + "The program-derived-address signing off on the account creation. Seeds = &[] + bump seed." + ] }, { "name": "systemProgram", "isMut": false, "isSigner": false, - "desc": "The system program" + "docs": [ + "The system program" + ] }, { "name": "program", "isMut": false, "isSigner": false, - "desc": "The program whose state is being constructed" + "docs": [ + "The program whose state is being constructed" + ] } ], "args": [ @@ -74,13 +84,17 @@ "name": "buffer", "isMut": true, "isSigner": false, - "desc": "The buffer with the new idl data." + "docs": [ + "The buffer with the new idl data." + ] }, { "name": "idl", "isMut": true, "isSigner": false, - "desc": "The idl account to be updated with the buffer's data." + "docs": [ + "The idl account to be updated with the buffer's data." + ] }, { "name": "authority", diff --git a/shank-idl/tests/instructions.rs b/shank-idl/tests/instructions.rs index 265b034..bebe110 100644 --- a/shank-idl/tests/instructions.rs +++ b/shank-idl/tests/instructions.rs @@ -82,7 +82,7 @@ fn instruction_from_single_file_with_idl_instructions() { let file = fixtures_dir() .join("single_file") .join("create_idl_instructions.rs"); - let idl = parse_file(&file, &ParseIdlConfig::optional_program_address()) + let idl = parse_file(file, &ParseIdlConfig::optional_program_address()) .expect("Parsing should not fail") .expect("File contains IDL"); @@ -91,6 +91,10 @@ fn instruction_from_single_file_with_idl_instructions() { )) .unwrap(); + println!("IDL: {}", idl.try_into_json().unwrap()); + + println!("Expected: {}", expected_idl.try_into_json().unwrap()); + assert_eq!(idl, expected_idl); } diff --git a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs index a596880..2c2c009 100644 --- a/shank-macro-impl/src/instruction/idl_instruction_attrs.rs +++ b/shank-macro-impl/src/instruction/idl_instruction_attrs.rs @@ -95,6 +95,7 @@ impl IdlInstruction { name: "from".to_string(), desc: Some("Payer of the transaction".to_string()), signer: true, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -103,6 +104,7 @@ impl IdlInstruction { name: "to".to_string(), desc: Some("The deterministically defined 'state' account being created via `create_account_with_seed`".to_string()), signer: false, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -111,6 +113,7 @@ impl IdlInstruction { name: "base".to_string(), desc: Some("The program-derived-address signing off on the account creation. Seeds = &[] + bump seed.".to_string()), signer: false, + optional_signer: false, writable: false, optional: false, }, InstructionAccount { @@ -119,6 +122,7 @@ impl IdlInstruction { name: "system_program".to_string(), desc: Some("The system program".to_string()), signer: false, + optional_signer: false, writable: false, optional: false, }, InstructionAccount { @@ -127,6 +131,7 @@ impl IdlInstruction { name: "program".to_string(), desc: Some("The program whose state is being constructed".to_string()), signer: false, + optional_signer: false, writable: false, optional: false, }]), @@ -137,6 +142,7 @@ impl IdlInstruction { name: "buffer".to_string(), desc: None, signer: false, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -145,6 +151,7 @@ impl IdlInstruction { name: "authority".to_string(), desc: None, signer: true, + optional_signer: false, writable: false, optional: false, }]), @@ -155,6 +162,7 @@ impl IdlInstruction { name: "buffer".to_string(), desc: Some("The buffer with the new idl data.".to_string()), signer: false, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -163,6 +171,7 @@ impl IdlInstruction { name: "idl".to_string(), desc: Some("The idl account to be updated with the buffer's data.".to_string()), signer: false, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -171,6 +180,7 @@ impl IdlInstruction { name: "authority".to_string(), desc: None, signer: true, + optional_signer: false, writable: false, optional: false, }]), @@ -181,6 +191,7 @@ impl IdlInstruction { name: "idl".to_string(), desc: None, signer: false, + optional_signer: false, writable: true, optional: false, }, InstructionAccount { @@ -189,6 +200,7 @@ impl IdlInstruction { name: "authority".to_string(), desc: None, signer: true, + optional_signer: false, writable: false, optional: false, }]), @@ -201,7 +213,7 @@ impl IdlInstruction { vec![( "data_len".to_string(), RustType { - ident: ident.clone(), + ident, kind: TypeKind::Primitive(Primitive::U64), context: RustTypeContext::Default, reference: crate::types::ParsedReference::Owned, @@ -212,7 +224,7 @@ impl IdlInstruction { vec![( "new_authority".to_string(), RustType { - ident: ident.clone(), + ident, kind: TypeKind::Value(Value::Custom("Pubkey".to_string())), context: RustTypeContext::Default, reference: crate::types::ParsedReference::Owned @@ -226,7 +238,7 @@ impl IdlInstruction { ident: ident.clone(), kind: TypeKind::Composite(Composite::Vec, vec![ RustType { - ident: ident.clone(), + ident, kind: TypeKind::Primitive(Primitive::U8), context: RustTypeContext::CollectionItem, reference: crate::types::ParsedReference::Owned diff --git a/shank-macro-impl/src/instruction/instruction.rs b/shank-macro-impl/src/instruction/instruction.rs index 451d56a..7e1141f 100644 --- a/shank-macro-impl/src/instruction/instruction.rs +++ b/shank-macro-impl/src/instruction/instruction.rs @@ -33,7 +33,8 @@ impl Instruction { skip_derive_attr_check: bool, ) -> ParseResult> { if skip_derive_attr_check - || get_derive_attr(&item_enum.attrs, DERIVE_INSTRUCTION_ATTR).is_some() + || get_derive_attr(&item_enum.attrs, DERIVE_INSTRUCTION_ATTR) + .is_some() { let parsed_enum = ParsedEnum::try_from(item_enum)?; Instruction::try_from(&parsed_enum).map(Some) @@ -47,7 +48,9 @@ impl TryFrom<&ParsedEnum> for Option { type Error = ParseError; fn try_from(parsed_enum: &ParsedEnum) -> ParseResult { - match get_derive_attr(&parsed_enum.attrs, DERIVE_INSTRUCTION_ATTR).map(|_| parsed_enum) { + match get_derive_attr(&parsed_enum.attrs, DERIVE_INSTRUCTION_ATTR) + .map(|_| parsed_enum) + { Some(ix_enum) => ix_enum.try_into().map(Some), None => Ok(None), } @@ -110,7 +113,12 @@ impl TryFrom<&ParsedEnumVariant> for InstructionVariant { Some(_) => InstructionVariantFields::Named( fields .iter() - .map(|x| (x.ident.as_ref().unwrap().to_string(), x.rust_type.clone())) + .map(|x| { + ( + x.ident.as_ref().unwrap().to_string(), + x.rust_type.clone(), + ) + }) .collect(), ), None => InstructionVariantFields::Unnamed( @@ -126,16 +134,19 @@ impl TryFrom<&ParsedEnumVariant> for InstructionVariant { let strategies: InstructionStrategies; let idl_instruction = IdlInstruction::try_from(attrs); - if idl_instruction.is_ok() { - let idl_ix = idl_instruction.unwrap(); - accounts = idl_ix.to_accounts(ident.clone()); - field_tys = idl_ix.to_instruction_fields(ident.clone()); - strategies = InstructionStrategies(HashSet::::new()); - } else { - let err = idl_instruction.unwrap_err(); - println!("{}", err); - accounts = attrs.try_into()?; - strategies = attrs.into(); + match idl_instruction { + Ok(idl_ix) => { + accounts = idl_ix.to_accounts(ident.clone()); + field_tys = idl_ix.to_instruction_fields(ident.clone()); + strategies = InstructionStrategies(HashSet::< + InstructionStrategy, + >::new()); + } + Err(err) => { + println!("{}", err); + accounts = attrs.try_into()?; + strategies = attrs.into(); + } } Ok(Self {