idl: Add separate spec crate (#3036)
This commit is contained in:
parent
9c17d65a76
commit
cc43e67399
|
@ -29,6 +29,7 @@ The minor version will be incremented upon a breaking change and the patch versi
|
||||||
- cli: Sync program ids on the initial build ([#3023](https://github.com/coral-xyz/anchor/pull/3023)).
|
- cli: Sync program ids on the initial build ([#3023](https://github.com/coral-xyz/anchor/pull/3023)).
|
||||||
- idl: Remove `anchor-syn` dependency ([#3030](https://github.com/coral-xyz/anchor/pull/3030)).
|
- idl: Remove `anchor-syn` dependency ([#3030](https://github.com/coral-xyz/anchor/pull/3030)).
|
||||||
- lang: Add `const` of program ID to `declare_id!` and `declare_program!` ([#3019](https://github.com/coral-xyz/anchor/pull/3019)).
|
- lang: Add `const` of program ID to `declare_id!` and `declare_program!` ([#3019](https://github.com/coral-xyz/anchor/pull/3019)).
|
||||||
|
- idl: Add separate spec crate ([#3036](https://github.com/coral-xyz/anchor/pull/3036)).
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
|
|
@ -291,6 +291,7 @@ dependencies = [
|
||||||
name = "anchor-lang-idl"
|
name = "anchor-lang-idl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anchor-lang-idl-spec",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"heck 0.3.3",
|
"heck 0.3.3",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -299,6 +300,14 @@ dependencies = [
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anchor-lang-idl-spec"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-spl"
|
name = "anchor-spl"
|
||||||
version = "0.30.0"
|
version = "0.30.0"
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::config::{
|
||||||
use anchor_client::Cluster;
|
use anchor_client::Cluster;
|
||||||
use anchor_lang::idl::{IdlAccount, IdlInstruction, ERASED_AUTHORITY};
|
use anchor_lang::idl::{IdlAccount, IdlInstruction, ERASED_AUTHORITY};
|
||||||
use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
|
use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
|
||||||
|
use anchor_lang_idl::convert::convert_idl;
|
||||||
use anchor_lang_idl::types::{Idl, IdlArrayLen, IdlDefinedFields, IdlType, IdlTypeDefTy};
|
use anchor_lang_idl::types::{Idl, IdlArrayLen, IdlDefinedFields, IdlType, IdlTypeDefTy};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use checks::{check_anchor_version, check_overflow};
|
use checks::{check_anchor_version, check_overflow};
|
||||||
|
@ -2734,7 +2735,7 @@ fn idl_fetch(cfg_override: &ConfigOverride, address: Pubkey, out: Option<String>
|
||||||
|
|
||||||
fn idl_convert(path: String, out: Option<String>) -> Result<()> {
|
fn idl_convert(path: String, out: Option<String>) -> Result<()> {
|
||||||
let idl = fs::read(path)?;
|
let idl = fs::read(path)?;
|
||||||
let idl = Idl::from_slice_with_conversion(&idl)?;
|
let idl = convert_idl(&idl)?;
|
||||||
let out = match out {
|
let out = match out {
|
||||||
None => OutFile::Stdout,
|
None => OutFile::Stdout,
|
||||||
Some(out) => OutFile::File(PathBuf::from(out)),
|
Some(out) => OutFile::File(PathBuf::from(out)),
|
||||||
|
@ -2744,7 +2745,7 @@ fn idl_convert(path: String, out: Option<String>) -> Result<()> {
|
||||||
|
|
||||||
fn idl_type(path: String, out: Option<String>) -> Result<()> {
|
fn idl_type(path: String, out: Option<String>) -> Result<()> {
|
||||||
let idl = fs::read(path)?;
|
let idl = fs::read(path)?;
|
||||||
let idl = Idl::from_slice_with_conversion(&idl)?;
|
let idl = convert_idl(&idl)?;
|
||||||
let types = idl_ts(&idl)?;
|
let types = idl_ts(&idl)?;
|
||||||
match out {
|
match out {
|
||||||
Some(out) => fs::write(out, types)?,
|
Some(out) => fs::write(out, types)?,
|
||||||
|
|
|
@ -16,6 +16,7 @@ build = ["regex"]
|
||||||
convert = ["heck", "sha2"]
|
convert = ["heck", "sha2"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anchor-lang-idl-spec = { path = "./spec", version = "0.1.0" }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "anchor-lang-idl-spec"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Anchor Maintainers <accounts@200ms.io>"]
|
||||||
|
repository = "https://github.com/coral-xyz/anchor"
|
||||||
|
edition = "2021"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
description = "Anchor framework IDL spec"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
|
@ -309,6 +309,7 @@ pub enum IdlType {
|
||||||
Generic(String),
|
Generic(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move to utils crate
|
||||||
impl FromStr for IdlType {
|
impl FromStr for IdlType {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
|
@ -2,30 +2,28 @@ use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use crate::types::Idl;
|
use crate::types::Idl;
|
||||||
|
|
||||||
impl Idl {
|
/// Create an [`Idl`] value with additional support for older specs based on the
|
||||||
/// Create an [`Idl`] value with additional support for older specs based on the
|
/// `idl.metadata.spec` field.
|
||||||
/// `idl.metadata.spec` field.
|
///
|
||||||
///
|
/// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
|
||||||
/// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
|
/// (pre Anchor v0.30.0).
|
||||||
/// (pre Anchor v0.30.0).
|
///
|
||||||
///
|
/// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
|
||||||
/// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
|
/// program's address otherwise an error will be returned.
|
||||||
/// program's address otherwise an error will be returned.
|
pub fn convert_idl(idl: &[u8]) -> Result<Idl> {
|
||||||
pub fn from_slice_with_conversion(idl: &[u8]) -> Result<Self> {
|
let value = serde_json::from_slice::<serde_json::Value>(idl)?;
|
||||||
let value = serde_json::from_slice::<serde_json::Value>(idl)?;
|
let spec = value
|
||||||
let spec = value
|
.get("metadata")
|
||||||
.get("metadata")
|
.and_then(|m| m.get("spec"))
|
||||||
.and_then(|m| m.get("spec"))
|
.and_then(|spec| spec.as_str());
|
||||||
.and_then(|spec| spec.as_str());
|
match spec {
|
||||||
match spec {
|
// New standard
|
||||||
// New standard
|
Some(spec) => match spec {
|
||||||
Some(spec) => match spec {
|
"0.1.0" => serde_json::from_value(value).map_err(Into::into),
|
||||||
"0.1.0" => serde_json::from_value(value).map_err(Into::into),
|
_ => Err(anyhow!("IDL spec not supported: `{spec}`")),
|
||||||
_ => Err(anyhow!("IDL spec not supported: `{spec}`")),
|
},
|
||||||
},
|
// Legacy
|
||||||
// Legacy
|
None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
|
||||||
None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,10 +431,11 @@ mod legacy {
|
||||||
fn from(value: IdlTypeDefinitionTy) -> Self {
|
fn from(value: IdlTypeDefinitionTy) -> Self {
|
||||||
match value {
|
match value {
|
||||||
IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
|
IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
|
||||||
fields: fields
|
fields: fields.is_empty().then(|| None).unwrap_or_else(|| {
|
||||||
.is_empty()
|
Some(t::IdlDefinedFields::Named(
|
||||||
.then(|| None)
|
fields.into_iter().map(Into::into).collect(),
|
||||||
.unwrap_or_else(|| Some(fields.into())),
|
))
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
IdlTypeDefinitionTy::Enum { variants } => Self::Enum {
|
IdlTypeDefinitionTy::Enum { variants } => Self::Enum {
|
||||||
variants: variants
|
variants: variants
|
||||||
|
@ -444,7 +443,9 @@ mod legacy {
|
||||||
.map(|variant| t::IdlEnumVariant {
|
.map(|variant| t::IdlEnumVariant {
|
||||||
name: variant.name,
|
name: variant.name,
|
||||||
fields: variant.fields.map(|fields| match fields {
|
fields: variant.fields.map(|fields| match fields {
|
||||||
EnumFields::Named(fields) => fields.into(),
|
EnumFields::Named(fields) => t::IdlDefinedFields::Named(
|
||||||
|
fields.into_iter().map(Into::into).collect(),
|
||||||
|
),
|
||||||
EnumFields::Tuple(tys) => t::IdlDefinedFields::Tuple(
|
EnumFields::Tuple(tys) => t::IdlDefinedFields::Tuple(
|
||||||
tys.into_iter().map(Into::into).collect(),
|
tys.into_iter().map(Into::into).collect(),
|
||||||
),
|
),
|
||||||
|
@ -469,12 +470,6 @@ mod legacy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<IdlField>> for t::IdlDefinedFields {
|
|
||||||
fn from(value: Vec<IdlField>) -> Self {
|
|
||||||
Self::Named(value.into_iter().map(Into::into).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<IdlType> for t::IdlType {
|
impl From<IdlType> for t::IdlType {
|
||||||
fn from(value: IdlType) -> Self {
|
fn from(value: IdlType) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! Anchor IDL.
|
//! Anchor IDL.
|
||||||
|
|
||||||
pub mod types;
|
|
||||||
|
|
||||||
#[cfg(feature = "build")]
|
#[cfg(feature = "build")]
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
|
||||||
#[cfg(feature = "convert")]
|
#[cfg(feature = "convert")]
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
|
||||||
|
pub use anchor_lang_idl_spec as types;
|
||||||
|
|
||||||
#[cfg(feature = "build")]
|
#[cfg(feature = "build")]
|
||||||
pub use serde_json;
|
pub use serde_json;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
mod common;
|
mod common;
|
||||||
mod mods;
|
mod mods;
|
||||||
|
|
||||||
use anchor_lang_idl::types::Idl;
|
use anchor_lang_idl::{convert::convert_idl, types::Idl};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
|
@ -45,7 +45,7 @@ fn get_idl(name: &syn::Ident) -> anyhow::Result<Idl> {
|
||||||
.map(|idl_dir| idl_dir.join(name.to_string()).with_extension("json"))
|
.map(|idl_dir| idl_dir.join(name.to_string()).with_extension("json"))
|
||||||
.map(std::fs::read)?
|
.map(std::fs::read)?
|
||||||
.map_err(|e| anyhow!("Failed to read IDL `{name}`: {e}"))
|
.map_err(|e| anyhow!("Failed to read IDL `{name}`: {e}"))
|
||||||
.map(|buf| Idl::from_slice_with_conversion(&buf))?
|
.map(|buf| convert_idl(&buf))?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_program(idl: &Idl, name: &syn::Ident) -> proc_macro2::TokenStream {
|
fn gen_program(idl: &Idl, name: &syn::Ident) -> proc_macro2::TokenStream {
|
||||||
|
|
Loading…
Reference in New Issue