idl: Move IDL types from the `anchor-syn` crate to the new IDL crate (#2882)

This commit is contained in:
acheron 2024-04-02 20:01:27 +02:00 committed by GitHub
parent 475c694355
commit c138a55b72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 584 additions and 592 deletions

View File

@ -87,6 +87,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- ts: `Program` instances use camelCase for everything ([#2824](https://github.com/coral-xyz/anchor/pull/2824)).
- ts: Remove discriminator functions ([#2824](https://github.com/coral-xyz/anchor/pull/2824)).
- ts: Remove `programId` parameter of the `Program` constructor ([#2864](https://github.com/coral-xyz/anchor/pull/2864)).
- idl, syn: Move IDL types from the `anchor-syn` crate to the new IDL crate ([#2882](https://github.com/coral-xyz/anchor/pull/2882)).
## [0.29.0] - 2023-10-16

3
Cargo.lock generated
View File

@ -170,6 +170,7 @@ dependencies = [
name = "anchor-attribute-program"
version = "0.29.0"
dependencies = [
"anchor-idl",
"anchor-syn",
"anyhow",
"bs58 0.5.0",
@ -286,7 +287,7 @@ dependencies = [
"anchor-derive-accounts",
"anchor-derive-serde",
"anchor-derive-space",
"anchor-syn",
"anchor-idl",
"arrayref",
"base64 0.21.7",
"bincode",

View File

@ -13,18 +13,13 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
build = [
"anyhow",
"regex",
"serde",
"serde_json",
]
build = ["anchor-syn", "regex"]
[dependencies]
anchor-syn = { path = "../lang/syn", version = "0.29.0", features = ["idl-types"] }
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# `build` feature only
anyhow = { version = "1", optional = true }
anchor-syn = { path = "../lang/syn", version = "0.29.0", optional = true }
regex = { version = "1", optional = true }
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }

View File

@ -12,6 +12,38 @@ use serde::Deserialize;
use crate::types::{Idl, IdlEvent, IdlTypeDef};
/// A trait that types must implement in order to include the type in the IDL definition.
///
/// This trait is automatically implemented for Anchor all types that use the `AnchorSerialize`
/// proc macro. Note that manually implementing the `AnchorSerialize` trait does **NOT** have the
/// same effect.
///
/// Types that don't implement this trait will cause a compile error during the IDL generation.
///
/// The default implementation of the trait allows the program to compile but the type does **NOT**
/// get included in the IDL.
pub trait IdlBuild {
/// Create an IDL type definition for the type.
///
/// The type is only included in the IDL if this method returns `Some`.
fn create_type() -> Option<IdlTypeDef> {
None
}
/// Insert all types that are included in the current type definition to the given map.
fn insert_types(_types: &mut BTreeMap<String, IdlTypeDef>) {}
/// Get the full module path of the type.
///
/// The full path will be used in the case of a conflicting type definition, e.g. when there
/// are multiple structs with the same name.
///
/// The default implementation covers most cases.
fn get_full_path() -> String {
std::any::type_name::<Self>().into()
}
}
/// Generate IDL via compilation.
pub fn build_idl(
program_path: impl AsRef<Path>,

View File

@ -4,3 +4,6 @@ pub mod types;
#[cfg(feature = "build")]
pub mod build;
#[cfg(feature = "build")]
pub use serde_json;

View File

@ -1,3 +1,486 @@
//! IDL types are re-exported from [`anchor_syn`].
use std::str::FromStr;
pub use anchor_syn::idl::types::*;
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
/// IDL specification Semantic Version
pub const IDL_SPEC: &str = "0.1.0";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Idl {
pub address: String,
pub metadata: IdlMetadata,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
pub instructions: Vec<IdlInstruction>,
#[serde(default, skip_serializing_if = "is_default")]
pub accounts: Vec<IdlAccount>,
#[serde(default, skip_serializing_if = "is_default")]
pub events: Vec<IdlEvent>,
#[serde(default, skip_serializing_if = "is_default")]
pub errors: Vec<IdlErrorCode>,
#[serde(default, skip_serializing_if = "is_default")]
pub types: Vec<IdlTypeDef>,
#[serde(default, skip_serializing_if = "is_default")]
pub constants: Vec<IdlConst>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlMetadata {
pub name: String,
pub version: String,
pub spec: String,
#[serde(skip_serializing_if = "is_default")]
pub description: Option<String>,
#[serde(skip_serializing_if = "is_default")]
pub repository: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub dependencies: Vec<IdlDependency>,
#[serde(skip_serializing_if = "is_default")]
pub contact: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlDependency {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstruction {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
pub discriminator: IdlDiscriminator,
pub accounts: Vec<IdlInstructionAccountItem>,
pub args: Vec<IdlField>,
#[serde(skip_serializing_if = "is_default")]
pub returns: Option<IdlType>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IdlInstructionAccountItem {
Composite(IdlInstructionAccounts),
Single(IdlInstructionAccount),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstructionAccount {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub writable: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub signer: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub optional: bool,
#[serde(skip_serializing_if = "is_default")]
pub address: Option<String>,
#[serde(skip_serializing_if = "is_default")]
pub pda: Option<IdlPda>,
#[serde(default, skip_serializing_if = "is_default")]
pub relations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstructionAccounts {
pub name: String,
pub accounts: Vec<IdlInstructionAccountItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlPda {
pub seeds: Vec<IdlSeed>,
#[serde(skip_serializing_if = "is_default")]
pub program: Option<IdlSeed>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlSeed {
Const(IdlSeedConst),
Arg(IdlSeedArg),
Account(IdlSeedAccount),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedConst {
pub value: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedArg {
pub path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedAccount {
pub path: String,
#[serde(skip_serializing_if = "is_default")]
pub account: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlAccount {
pub name: String,
pub discriminator: IdlDiscriminator,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlEvent {
pub name: String,
pub discriminator: IdlDiscriminator,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlConst {
pub name: String,
#[serde(rename = "type")]
pub ty: IdlType,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IdlErrorCode {
pub code: u32,
pub name: String,
#[serde(skip_serializing_if = "is_default")]
pub msg: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlField {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(rename = "type")]
pub ty: IdlType,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlTypeDef {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub serialization: IdlSerialization,
#[serde(skip_serializing_if = "is_default")]
pub repr: Option<IdlRepr>,
#[serde(default, skip_serializing_if = "is_default")]
pub generics: Vec<IdlTypeDefGeneric>,
#[serde(rename = "type")]
pub ty: IdlTypeDefTy,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum IdlSerialization {
#[default]
Borsh,
Bytemuck,
BytemuckUnsafe,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlRepr {
Rust(IdlReprModifier),
C(IdlReprModifier),
Transparent,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlReprModifier {
#[serde(default, skip_serializing_if = "is_default")]
pub packed: bool,
#[serde(skip_serializing_if = "is_default")]
pub align: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlTypeDefGeneric {
Type {
name: String,
},
Const {
name: String,
#[serde(rename = "type")]
ty: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlTypeDefTy {
Struct {
#[serde(skip_serializing_if = "is_default")]
fields: Option<IdlDefinedFields>,
},
Enum {
variants: Vec<IdlEnumVariant>,
},
Type {
alias: IdlType,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlEnumVariant {
pub name: String,
#[serde(skip_serializing_if = "is_default")]
pub fields: Option<IdlDefinedFields>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IdlDefinedFields {
Named(Vec<IdlField>),
Tuple(Vec<IdlType>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum IdlArrayLen {
Generic(String),
#[serde(untagged)]
Value(usize),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlGenericArg {
Type {
#[serde(rename = "type")]
ty: IdlType,
},
Const {
value: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum IdlType {
Bool,
U8,
I8,
U16,
I16,
U32,
I32,
F32,
U64,
I64,
F64,
U128,
I128,
U256,
I256,
Bytes,
String,
Pubkey,
Option(Box<IdlType>),
Vec(Box<IdlType>),
Array(Box<IdlType>, IdlArrayLen),
Defined {
name: String,
#[serde(default, skip_serializing_if = "is_default")]
generics: Vec<IdlGenericArg>,
},
Generic(String),
}
impl FromStr for IdlType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut s = s.to_owned();
s.retain(|c| !c.is_whitespace());
let r = match s.as_str() {
"bool" => IdlType::Bool,
"u8" => IdlType::U8,
"i8" => IdlType::I8,
"u16" => IdlType::U16,
"i16" => IdlType::I16,
"u32" => IdlType::U32,
"i32" => IdlType::I32,
"f32" => IdlType::F32,
"u64" => IdlType::U64,
"i64" => IdlType::I64,
"f64" => IdlType::F64,
"u128" => IdlType::U128,
"i128" => IdlType::I128,
"u256" => IdlType::U256,
"i256" => IdlType::I256,
"Vec<u8>" => IdlType::Bytes,
"String" | "&str" | "&'staticstr" => IdlType::String,
"Pubkey" => IdlType::Pubkey,
_ => {
if let Some(inner) = s.strip_prefix("Option<") {
let inner_ty = Self::from_str(
inner
.strip_suffix('>')
.ok_or_else(|| anyhow!("Invalid Option"))?,
)?;
return Ok(IdlType::Option(Box::new(inner_ty)));
}
if let Some(inner) = s.strip_prefix("Vec<") {
let inner_ty = Self::from_str(
inner
.strip_suffix('>')
.ok_or_else(|| anyhow!("Invalid Vec"))?,
)?;
return Ok(IdlType::Vec(Box::new(inner_ty)));
}
if s.starts_with('[') {
fn array_from_str(inner: &str) -> IdlType {
match inner.strip_suffix(']') {
Some(nested_inner) => array_from_str(&nested_inner[1..]),
None => {
let (raw_type, raw_length) = inner.rsplit_once(';').unwrap();
let ty = IdlType::from_str(raw_type).unwrap();
let len = match raw_length.replace('_', "").parse::<usize>() {
Ok(len) => IdlArrayLen::Value(len),
Err(_) => IdlArrayLen::Generic(raw_length.to_owned()),
};
IdlType::Array(Box::new(ty), len)
}
}
}
return Ok(array_from_str(&s));
}
// Defined
let (name, generics) = if let Some(i) = s.find('<') {
(
s.get(..i).unwrap().to_owned(),
s.get(i + 1..)
.unwrap()
.strip_suffix('>')
.unwrap()
.split(',')
.map(|g| g.trim().to_owned())
.map(|g| {
if g.parse::<bool>().is_ok()
|| g.parse::<u128>().is_ok()
|| g.parse::<i128>().is_ok()
|| g.parse::<char>().is_ok()
{
Ok(IdlGenericArg::Const { value: g })
} else {
Self::from_str(&g).map(|ty| IdlGenericArg::Type { ty })
}
})
.collect::<Result<Vec<_>, _>>()?,
)
} else {
(s.to_owned(), vec![])
};
IdlType::Defined { name, generics }
}
};
Ok(r)
}
}
pub type IdlDiscriminator = Vec<u8>;
/// Get whether the given data is the default of its type.
fn is_default<T: Default + PartialEq>(it: &T) -> bool {
*it == T::default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn option() {
assert_eq!(
IdlType::from_str("Option<bool>").unwrap(),
IdlType::Option(Box::new(IdlType::Bool))
)
}
#[test]
fn vector() {
assert_eq!(
IdlType::from_str("Vec<bool>").unwrap(),
IdlType::Vec(Box::new(IdlType::Bool))
)
}
#[test]
fn array() {
assert_eq!(
IdlType::from_str("[Pubkey; 16]").unwrap(),
IdlType::Array(Box::new(IdlType::Pubkey), IdlArrayLen::Value(16))
);
}
#[test]
fn array_with_underscored_length() {
assert_eq!(
IdlType::from_str("[u8; 50_000]").unwrap(),
IdlType::Array(Box::new(IdlType::U8), IdlArrayLen::Value(50000))
);
}
#[test]
fn multidimensional_array() {
assert_eq!(
IdlType::from_str("[[u8; 16]; 32]").unwrap(),
IdlType::Array(
Box::new(IdlType::Array(
Box::new(IdlType::U8),
IdlArrayLen::Value(16)
)),
IdlArrayLen::Value(32)
)
);
}
#[test]
fn generic_array() {
assert_eq!(
IdlType::from_str("[u64; T]").unwrap(),
IdlType::Array(Box::new(IdlType::U64), IdlArrayLen::Generic("T".into()))
);
}
#[test]
fn defined() {
assert_eq!(
IdlType::from_str("MyStruct").unwrap(),
IdlType::Defined {
name: "MyStruct".into(),
generics: vec![]
}
)
}
#[test]
fn defined_with_generics() {
assert_eq!(
IdlType::from_str("MyStruct<Pubkey, u64, 8>").unwrap(),
IdlType::Defined {
name: "MyStruct".into(),
generics: vec![
IdlGenericArg::Type {
ty: IdlType::Pubkey
},
IdlGenericArg::Type { ty: IdlType::U64 },
IdlGenericArg::Const { value: "8".into() },
],
}
)
}
}

View File

@ -33,7 +33,7 @@ idl-build = [
"anchor-attribute-program/idl-build",
"anchor-derive-accounts/idl-build",
"anchor-derive-serde/idl-build",
"anchor-syn/idl-build",
"anchor-idl/build",
]
init-if-needed = ["anchor-derive-accounts/init-if-needed"]
interface-instructions = ["anchor-attribute-program/interface-instructions"]
@ -49,8 +49,8 @@ anchor-derive-accounts = { path = "./derive/accounts", version = "0.29.0" }
anchor-derive-serde = { path = "./derive/serde", version = "0.29.0" }
anchor-derive-space = { path = "./derive/space", version = "0.29.0" }
# `anchor-syn` should only be included with `idl-build` feature
anchor-syn = { path = "./syn", version = "0.29.0", optional = true }
# `anchor-idl` should only be included with `idl-build` feature
anchor-idl = { path = "../idl", version = "0.29.0", optional = true }
arrayref = "0.3"
base64 = "0.21"

View File

@ -413,7 +413,7 @@ pub fn zero_copy(
#ret
})
.unwrap();
let idl_build_impl = anchor_syn::idl::build::impl_idl_build_struct(&zc_struct);
let idl_build_impl = anchor_syn::idl::impl_idl_build_struct(&zc_struct);
return proc_macro::TokenStream::from(quote! {
#ret
#idl_build_impl
@ -436,7 +436,7 @@ pub fn declare_id(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[cfg(feature = "idl-build")]
{
let idl_print = anchor_syn::idl::build::gen_idl_print_fn_address(address);
let idl_print = anchor_syn::idl::gen_idl_print_fn_address(address);
return proc_macro::TokenStream::from(quote! {
#ret
#idl_print

View File

@ -13,7 +13,7 @@ pub fn constant(
let ts = match syn::parse(input).unwrap() {
syn::Item::Const(item) => {
let idl_print = anchor_syn::idl::build::gen_idl_print_fn_constant(&item);
let idl_print = anchor_syn::idl::gen_idl_print_fn_constant(&item);
quote! {
#item
#idl_print

View File

@ -46,7 +46,7 @@ pub fn event(
#[cfg(feature = "idl-build")]
{
let idl_build = anchor_syn::idl::build::gen_idl_print_fn_event(&event_strct);
let idl_build = anchor_syn::idl::gen_idl_print_fn_event(&event_strct);
return proc_macro::TokenStream::from(quote! {
#ret
#idl_build

View File

@ -17,7 +17,8 @@ idl-build = ["anchor-syn/idl-build"]
interface-instructions = ["anchor-syn/interface-instructions"]
[dependencies]
anchor-syn = { path = "../../syn", version = "0.29.0", features = ["idl-types"] }
anchor-idl = { path = "../../../idl", version = "0.29.0" }
anchor-syn = { path = "../../syn", version = "0.29.0" }
anyhow = "1"
bs58 = "0.5"
heck = "0.3"

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::{
use anchor_idl::types::{
Idl, IdlArrayLen, IdlDefinedFields, IdlField, IdlGenericArg, IdlRepr, IdlSerialization,
IdlType, IdlTypeDef, IdlTypeDefGeneric, IdlTypeDefTy,
};

View File

@ -1,7 +1,7 @@
mod common;
mod mods;
use anchor_syn::idl::types::Idl;
use anchor_idl::types::Idl;
use anyhow::anyhow;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::{Idl, IdlSerialization};
use anchor_idl::types::{Idl, IdlSerialization};
use quote::{format_ident, quote};
use super::common::{convert_idl_type_def_to_ts, gen_discriminator, get_canonical_program_id};

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::Idl;
use anchor_idl::types::Idl;
use quote::quote;
use super::common::gen_accounts_common;

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::{Idl, IdlType};
use anchor_idl::types::{Idl, IdlType};
use quote::{format_ident, quote, ToTokens};
use super::common::convert_idl_type_to_str;

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::Idl;
use anchor_idl::types::Idl;
use heck::CamelCase;
use quote::{format_ident, quote};

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::Idl;
use anchor_idl::types::Idl;
use quote::{format_ident, quote};
use super::common::{convert_idl_type_def_to_ts, gen_discriminator};

View File

@ -1,6 +1,6 @@
use anchor_idl::types::{Idl, IdlInstructionAccountItem};
use anchor_syn::{
codegen::accounts::{__client_accounts, __cpi_client_accounts},
idl::types::{Idl, IdlInstructionAccountItem},
parser::accounts,
AccountsStruct,
};

View File

@ -1,4 +1,4 @@
use anchor_syn::idl::types::Idl;
use anchor_idl::types::Idl;
use quote::quote;
use super::common::convert_idl_type_def_to_ts;

View File

@ -32,7 +32,7 @@ pub fn anchor_serialize(input: TokenStream) -> TokenStream {
#[cfg(feature = "idl-build")]
{
use anchor_syn::idl::build::*;
use anchor_syn::idl::*;
use quote::quote;
let idl_build_impl = match syn::parse(input).unwrap() {

View File

@ -79,3 +79,6 @@ impl IdlAccount {
"anchor:idl"
}
}
#[cfg(feature = "idl-build")]
pub use anchor_idl::{build::IdlBuild, *};

View File

@ -65,7 +65,7 @@ pub use solana_program;
pub use anchor_attribute_event::{emit_cpi, event_cpi};
#[cfg(feature = "idl-build")]
pub use anchor_syn::{self, idl::build::IdlBuild};
pub use idl::IdlBuild;
#[cfg(feature = "interface-instructions")]
pub use anchor_attribute_program::interface;
@ -423,7 +423,7 @@ pub mod prelude {
pub use super::{emit_cpi, event_cpi};
#[cfg(feature = "idl-build")]
pub use super::IdlBuild;
pub use super::idl::IdlBuild;
#[cfg(feature = "interface-instructions")]
pub use super::interface;

View File

@ -17,8 +17,7 @@ allow-missing-optionals = []
anchor-debug = []
event-cpi = []
hash = []
idl-build = ["idl-types", "cargo_toml"]
idl-types = []
idl-build = ["cargo_toml"]
init-if-needed = []
interface-instructions = []

View File

@ -37,7 +37,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
#[cfg(feature = "idl-build")]
{
let idl_build_impl = crate::idl::build::gen_idl_build_impl_accounts_struct(accs);
let idl_build_impl = crate::idl::gen_idl_build_impl_accounts_struct(accs);
return quote! {
#ret
#idl_build_impl

View File

@ -100,7 +100,7 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
#[cfg(feature = "idl-build")]
{
let idl_print = crate::idl::build::gen_idl_print_fn_error(&error);
let idl_print = crate::idl::gen_idl_print_fn_error(&error);
return quote! {
#ret
#idl_print

View File

@ -39,7 +39,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
#[cfg(feature = "idl-build")]
{
let idl_build_impl = crate::idl::build::gen_idl_print_fn_program(program);
let idl_build_impl = crate::idl::gen_idl_print_fn_program(program);
return quote! {
#ret
#idl_build_impl

View File

@ -1,21 +0,0 @@
#![allow(dead_code)]
mod accounts;
mod address;
mod common;
mod constant;
mod defined;
mod error;
mod event;
mod external;
mod program;
pub use accounts::gen_idl_build_impl_accounts_struct;
pub use address::gen_idl_print_fn_address;
pub use constant::gen_idl_print_fn_constant;
pub use defined::{impl_idl_build_enum, impl_idl_build_struct, impl_idl_build_union, IdlBuild};
pub use error::gen_idl_print_fn_error;
pub use event::gen_idl_print_fn_event;
pub use program::gen_idl_print_fn_program;
pub use serde_json;

View File

@ -26,11 +26,11 @@ pub fn get_no_docs() -> bool {
}
pub fn get_idl_module_path() -> TokenStream {
quote!(anchor_lang::anchor_syn::idl::types)
quote!(anchor_lang::idl::types)
}
pub fn get_serde_json_module_path() -> TokenStream {
quote!(anchor_lang::anchor_syn::idl::build::serde_json)
quote!(anchor_lang::idl::serde_json)
}
pub fn gen_print_section(name: &str, value: impl ToTokens) -> TokenStream {

View File

@ -1,55 +1,21 @@
use std::collections::BTreeMap;
use anyhow::{anyhow, Result};
use proc_macro2::TokenStream;
use quote::quote;
use super::common::{get_idl_module_path, get_no_docs};
use crate::{idl::types::IdlTypeDef, parser::docs};
use crate::parser::docs;
/// A trait that types must implement in order to include the type in the IDL definition.
///
/// This trait is automatically implemented for Anchor all types that use the `AnchorSerialize`
/// proc macro. Note that manually implementing the `AnchorSerialize` trait does **NOT** have the
/// same effect.
///
/// Types that don't implement this trait will cause a compile error during the IDL generation.
///
/// The default implementation of the trait allows the program to compile but the type does **NOT**
/// get included in the IDL.
pub trait IdlBuild {
/// Create an IDL type definition for the type.
///
/// The type is only included in the IDL if this method returns `Some`.
fn create_type() -> Option<IdlTypeDef> {
None
}
/// Insert all types that are included in the current type definition to the given map.
fn insert_types(_types: &mut BTreeMap<String, IdlTypeDef>) {}
/// Get the full module path of the type.
///
/// The full path will be used in the case of a conflicting type definition, e.g. when there
/// are multiple structs with the same name.
///
/// The default implementation covers most cases.
fn get_full_path() -> String {
std::any::type_name::<Self>().into()
}
}
/// Generate [`IdlBuild`] impl for a struct.
/// Generate `IdlBuild` impl for a struct.
pub fn impl_idl_build_struct(item: &syn::ItemStruct) -> TokenStream {
impl_idl_build(&item.ident, &item.generics, gen_idl_type_def_struct(item))
}
/// Generate [`IdlBuild`] impl for an enum.
/// Generate `IdlBuild` impl for an enum.
pub fn impl_idl_build_enum(item: &syn::ItemEnum) -> TokenStream {
impl_idl_build(&item.ident, &item.generics, gen_idl_type_def_enum(item))
}
/// Generate [`IdlBuild`] impl for a union.
/// Generate `IdlBuild` impl for a union.
///
/// Unions are not currently supported in the IDL.
pub fn impl_idl_build_union(item: &syn::ItemUnion) -> TokenStream {
@ -60,7 +26,7 @@ pub fn impl_idl_build_union(item: &syn::ItemUnion) -> TokenStream {
)
}
/// Generate [`IdlBuild`] implementation.
/// Generate `IdlBuild` implementation.
fn impl_idl_build(
ident: &syn::Ident,
generics: &syn::Generics,
@ -68,7 +34,7 @@ fn impl_idl_build(
) -> TokenStream {
let idl = get_idl_module_path();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let idl_build_trait = quote!(anchor_lang::anchor_syn::idl::build::IdlBuild);
let idl_build_trait = quote!(anchor_lang::idl::build::IdlBuild);
let (idl_type_def, insert_defined) = match type_def {
Ok((ts, defined)) => (

View File

@ -1,4 +1,19 @@
pub mod types;
#![allow(dead_code)]
#[cfg(feature = "idl-build")]
pub mod build;
mod accounts;
mod address;
mod common;
mod constant;
mod defined;
mod error;
mod event;
mod external;
mod program;
pub use accounts::gen_idl_build_impl_accounts_struct;
pub use address::gen_idl_print_fn_address;
pub use constant::gen_idl_print_fn_constant;
pub use defined::{impl_idl_build_enum, impl_idl_build_struct, impl_idl_build_union};
pub use error::gen_idl_print_fn_error;
pub use event::gen_idl_print_fn_event;
pub use program::gen_idl_print_fn_program;

View File

@ -1,486 +0,0 @@
use std::str::FromStr;
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
/// IDL specification Semantic Version
pub const IDL_SPEC: &str = "0.1.0";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Idl {
pub address: String,
pub metadata: IdlMetadata,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
pub instructions: Vec<IdlInstruction>,
#[serde(default, skip_serializing_if = "is_default")]
pub accounts: Vec<IdlAccount>,
#[serde(default, skip_serializing_if = "is_default")]
pub events: Vec<IdlEvent>,
#[serde(default, skip_serializing_if = "is_default")]
pub errors: Vec<IdlErrorCode>,
#[serde(default, skip_serializing_if = "is_default")]
pub types: Vec<IdlTypeDef>,
#[serde(default, skip_serializing_if = "is_default")]
pub constants: Vec<IdlConst>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlMetadata {
pub name: String,
pub version: String,
pub spec: String,
#[serde(skip_serializing_if = "is_default")]
pub description: Option<String>,
#[serde(skip_serializing_if = "is_default")]
pub repository: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub dependencies: Vec<IdlDependency>,
#[serde(skip_serializing_if = "is_default")]
pub contact: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlDependency {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstruction {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
pub discriminator: IdlDiscriminator,
pub accounts: Vec<IdlInstructionAccountItem>,
pub args: Vec<IdlField>,
#[serde(skip_serializing_if = "is_default")]
pub returns: Option<IdlType>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IdlInstructionAccountItem {
Composite(IdlInstructionAccounts),
Single(IdlInstructionAccount),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstructionAccount {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub writable: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub signer: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub optional: bool,
#[serde(skip_serializing_if = "is_default")]
pub address: Option<String>,
#[serde(skip_serializing_if = "is_default")]
pub pda: Option<IdlPda>,
#[serde(default, skip_serializing_if = "is_default")]
pub relations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlInstructionAccounts {
pub name: String,
pub accounts: Vec<IdlInstructionAccountItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlPda {
pub seeds: Vec<IdlSeed>,
#[serde(skip_serializing_if = "is_default")]
pub program: Option<IdlSeed>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlSeed {
Const(IdlSeedConst),
Arg(IdlSeedArg),
Account(IdlSeedAccount),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedConst {
pub value: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedArg {
pub path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlSeedAccount {
pub path: String,
#[serde(skip_serializing_if = "is_default")]
pub account: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlAccount {
pub name: String,
pub discriminator: IdlDiscriminator,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlEvent {
pub name: String,
pub discriminator: IdlDiscriminator,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlConst {
pub name: String,
#[serde(rename = "type")]
pub ty: IdlType,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IdlErrorCode {
pub code: u32,
pub name: String,
#[serde(skip_serializing_if = "is_default")]
pub msg: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlField {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(rename = "type")]
pub ty: IdlType,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlTypeDef {
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub docs: Vec<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub serialization: IdlSerialization,
#[serde(skip_serializing_if = "is_default")]
pub repr: Option<IdlRepr>,
#[serde(default, skip_serializing_if = "is_default")]
pub generics: Vec<IdlTypeDefGeneric>,
#[serde(rename = "type")]
pub ty: IdlTypeDefTy,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum IdlSerialization {
#[default]
Borsh,
Bytemuck,
BytemuckUnsafe,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlRepr {
Rust(IdlReprModifier),
C(IdlReprModifier),
Transparent,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlReprModifier {
#[serde(default, skip_serializing_if = "is_default")]
pub packed: bool,
#[serde(skip_serializing_if = "is_default")]
pub align: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlTypeDefGeneric {
Type {
name: String,
},
Const {
name: String,
#[serde(rename = "type")]
ty: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlTypeDefTy {
Struct {
#[serde(skip_serializing_if = "is_default")]
fields: Option<IdlDefinedFields>,
},
Enum {
variants: Vec<IdlEnumVariant>,
},
Type {
alias: IdlType,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdlEnumVariant {
pub name: String,
#[serde(skip_serializing_if = "is_default")]
pub fields: Option<IdlDefinedFields>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum IdlDefinedFields {
Named(Vec<IdlField>),
Tuple(Vec<IdlType>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum IdlArrayLen {
Generic(String),
#[serde(untagged)]
Value(usize),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum IdlGenericArg {
Type {
#[serde(rename = "type")]
ty: IdlType,
},
Const {
value: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum IdlType {
Bool,
U8,
I8,
U16,
I16,
U32,
I32,
F32,
U64,
I64,
F64,
U128,
I128,
U256,
I256,
Bytes,
String,
Pubkey,
Option(Box<IdlType>),
Vec(Box<IdlType>),
Array(Box<IdlType>, IdlArrayLen),
Defined {
name: String,
#[serde(default, skip_serializing_if = "is_default")]
generics: Vec<IdlGenericArg>,
},
Generic(String),
}
impl FromStr for IdlType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut s = s.to_owned();
s.retain(|c| !c.is_whitespace());
let r = match s.as_str() {
"bool" => IdlType::Bool,
"u8" => IdlType::U8,
"i8" => IdlType::I8,
"u16" => IdlType::U16,
"i16" => IdlType::I16,
"u32" => IdlType::U32,
"i32" => IdlType::I32,
"f32" => IdlType::F32,
"u64" => IdlType::U64,
"i64" => IdlType::I64,
"f64" => IdlType::F64,
"u128" => IdlType::U128,
"i128" => IdlType::I128,
"u256" => IdlType::U256,
"i256" => IdlType::I256,
"Vec<u8>" => IdlType::Bytes,
"String" | "&str" | "&'staticstr" => IdlType::String,
"Pubkey" => IdlType::Pubkey,
_ => {
if let Some(inner) = s.strip_prefix("Option<") {
let inner_ty = Self::from_str(
inner
.strip_suffix('>')
.ok_or_else(|| anyhow!("Invalid Option"))?,
)?;
return Ok(IdlType::Option(Box::new(inner_ty)));
}
if let Some(inner) = s.strip_prefix("Vec<") {
let inner_ty = Self::from_str(
inner
.strip_suffix('>')
.ok_or_else(|| anyhow!("Invalid Vec"))?,
)?;
return Ok(IdlType::Vec(Box::new(inner_ty)));
}
if s.starts_with('[') {
fn array_from_str(inner: &str) -> IdlType {
match inner.strip_suffix(']') {
Some(nested_inner) => array_from_str(&nested_inner[1..]),
None => {
let (raw_type, raw_length) = inner.rsplit_once(';').unwrap();
let ty = IdlType::from_str(raw_type).unwrap();
let len = match raw_length.replace('_', "").parse::<usize>() {
Ok(len) => IdlArrayLen::Value(len),
Err(_) => IdlArrayLen::Generic(raw_length.to_owned()),
};
IdlType::Array(Box::new(ty), len)
}
}
}
return Ok(array_from_str(&s));
}
// Defined
let (name, generics) = if let Some(i) = s.find('<') {
(
s.get(..i).unwrap().to_owned(),
s.get(i + 1..)
.unwrap()
.strip_suffix('>')
.unwrap()
.split(',')
.map(|g| g.trim().to_owned())
.map(|g| {
if g.parse::<bool>().is_ok()
|| g.parse::<u128>().is_ok()
|| g.parse::<i128>().is_ok()
|| g.parse::<char>().is_ok()
{
Ok(IdlGenericArg::Const { value: g })
} else {
Self::from_str(&g).map(|ty| IdlGenericArg::Type { ty })
}
})
.collect::<Result<Vec<_>, _>>()?,
)
} else {
(s.to_owned(), vec![])
};
IdlType::Defined { name, generics }
}
};
Ok(r)
}
}
pub type IdlDiscriminator = Vec<u8>;
/// Get whether the given data is the default of its type.
fn is_default<T: Default + PartialEq>(it: &T) -> bool {
*it == T::default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn option() {
assert_eq!(
IdlType::from_str("Option<bool>").unwrap(),
IdlType::Option(Box::new(IdlType::Bool))
)
}
#[test]
fn vector() {
assert_eq!(
IdlType::from_str("Vec<bool>").unwrap(),
IdlType::Vec(Box::new(IdlType::Bool))
)
}
#[test]
fn array() {
assert_eq!(
IdlType::from_str("[Pubkey; 16]").unwrap(),
IdlType::Array(Box::new(IdlType::Pubkey), IdlArrayLen::Value(16))
);
}
#[test]
fn array_with_underscored_length() {
assert_eq!(
IdlType::from_str("[u8; 50_000]").unwrap(),
IdlType::Array(Box::new(IdlType::U8), IdlArrayLen::Value(50000))
);
}
#[test]
fn multidimensional_array() {
assert_eq!(
IdlType::from_str("[[u8; 16]; 32]").unwrap(),
IdlType::Array(
Box::new(IdlType::Array(
Box::new(IdlType::U8),
IdlArrayLen::Value(16)
)),
IdlArrayLen::Value(32)
)
);
}
#[test]
fn generic_array() {
assert_eq!(
IdlType::from_str("[u64; T]").unwrap(),
IdlType::Array(Box::new(IdlType::U64), IdlArrayLen::Generic("T".into()))
);
}
#[test]
fn defined() {
assert_eq!(
IdlType::from_str("MyStruct").unwrap(),
IdlType::Defined {
name: "MyStruct".into(),
generics: vec![]
}
)
}
#[test]
fn defined_with_generics() {
assert_eq!(
IdlType::from_str("MyStruct<Pubkey, u64, 8>").unwrap(),
IdlType::Defined {
name: "MyStruct".into(),
generics: vec![
IdlGenericArg::Type {
ty: IdlType::Pubkey
},
IdlGenericArg::Type { ty: IdlType::U64 },
IdlGenericArg::Const { value: "8".into() },
],
}
)
}
}

View File

@ -3,7 +3,7 @@
pub mod codegen;
pub mod parser;
#[cfg(feature = "idl-types")]
#[cfg(feature = "idl-build")]
pub mod idl;
#[cfg(feature = "hash")]

View File

@ -366,7 +366,7 @@ mod wrapped {
use super::*;
#[cfg(feature = "idl-build")]
use anchor_lang::anchor_syn::idl::types::*;
use anchor_lang::idl::types::*;
pub struct Feature(anchor_lang::solana_program::feature::Feature);