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)).
|
||||
- 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)).
|
||||
- idl: Add separate spec crate ([#3036](https://github.com/coral-xyz/anchor/pull/3036)).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
|
|
@ -291,6 +291,7 @@ dependencies = [
|
|||
name = "anchor-lang-idl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-lang-idl-spec",
|
||||
"anyhow",
|
||||
"heck 0.3.3",
|
||||
"regex",
|
||||
|
@ -299,6 +300,14 @@ dependencies = [
|
|||
"sha2 0.10.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-lang-idl-spec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-spl"
|
||||
version = "0.30.0"
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::config::{
|
|||
use anchor_client::Cluster;
|
||||
use anchor_lang::idl::{IdlAccount, IdlInstruction, ERASED_AUTHORITY};
|
||||
use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
|
||||
use anchor_lang_idl::convert::convert_idl;
|
||||
use anchor_lang_idl::types::{Idl, IdlArrayLen, IdlDefinedFields, IdlType, IdlTypeDefTy};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
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<()> {
|
||||
let idl = fs::read(path)?;
|
||||
let idl = Idl::from_slice_with_conversion(&idl)?;
|
||||
let idl = convert_idl(&idl)?;
|
||||
let out = match out {
|
||||
None => OutFile::Stdout,
|
||||
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<()> {
|
||||
let idl = fs::read(path)?;
|
||||
let idl = Idl::from_slice_with_conversion(&idl)?;
|
||||
let idl = convert_idl(&idl)?;
|
||||
let types = idl_ts(&idl)?;
|
||||
match out {
|
||||
Some(out) => fs::write(out, types)?,
|
||||
|
|
|
@ -16,6 +16,7 @@ build = ["regex"]
|
|||
convert = ["heck", "sha2"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang-idl-spec = { path = "./spec", version = "0.1.0" }
|
||||
anyhow = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
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),
|
||||
}
|
||||
|
||||
// TODO: Move to utils crate
|
||||
impl FromStr for IdlType {
|
||||
type Err = anyhow::Error;
|
||||
|
|
@ -2,30 +2,28 @@ use anyhow::{anyhow, Result};
|
|||
|
||||
use crate::types::Idl;
|
||||
|
||||
impl Idl {
|
||||
/// Create an [`Idl`] value with additional support for older specs based on the
|
||||
/// `idl.metadata.spec` field.
|
||||
///
|
||||
/// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
|
||||
/// (pre Anchor v0.30.0).
|
||||
///
|
||||
/// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
|
||||
/// program's address otherwise an error will be returned.
|
||||
pub fn from_slice_with_conversion(idl: &[u8]) -> Result<Self> {
|
||||
let value = serde_json::from_slice::<serde_json::Value>(idl)?;
|
||||
let spec = value
|
||||
.get("metadata")
|
||||
.and_then(|m| m.get("spec"))
|
||||
.and_then(|spec| spec.as_str());
|
||||
match spec {
|
||||
// New standard
|
||||
Some(spec) => match spec {
|
||||
"0.1.0" => serde_json::from_value(value).map_err(Into::into),
|
||||
_ => Err(anyhow!("IDL spec not supported: `{spec}`")),
|
||||
},
|
||||
// Legacy
|
||||
None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
|
||||
}
|
||||
/// Create an [`Idl`] value with additional support for older specs based on the
|
||||
/// `idl.metadata.spec` field.
|
||||
///
|
||||
/// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
|
||||
/// (pre Anchor v0.30.0).
|
||||
///
|
||||
/// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
|
||||
/// program's address otherwise an error will be returned.
|
||||
pub fn convert_idl(idl: &[u8]) -> Result<Idl> {
|
||||
let value = serde_json::from_slice::<serde_json::Value>(idl)?;
|
||||
let spec = value
|
||||
.get("metadata")
|
||||
.and_then(|m| m.get("spec"))
|
||||
.and_then(|spec| spec.as_str());
|
||||
match spec {
|
||||
// New standard
|
||||
Some(spec) => match spec {
|
||||
"0.1.0" => serde_json::from_value(value).map_err(Into::into),
|
||||
_ => Err(anyhow!("IDL spec not supported: `{spec}`")),
|
||||
},
|
||||
// Legacy
|
||||
None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,10 +431,11 @@ mod legacy {
|
|||
fn from(value: IdlTypeDefinitionTy) -> Self {
|
||||
match value {
|
||||
IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
|
||||
fields: fields
|
||||
.is_empty()
|
||||
.then(|| None)
|
||||
.unwrap_or_else(|| Some(fields.into())),
|
||||
fields: fields.is_empty().then(|| None).unwrap_or_else(|| {
|
||||
Some(t::IdlDefinedFields::Named(
|
||||
fields.into_iter().map(Into::into).collect(),
|
||||
))
|
||||
}),
|
||||
},
|
||||
IdlTypeDefinitionTy::Enum { variants } => Self::Enum {
|
||||
variants: variants
|
||||
|
@ -444,7 +443,9 @@ mod legacy {
|
|||
.map(|variant| t::IdlEnumVariant {
|
||||
name: variant.name,
|
||||
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(
|
||||
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 {
|
||||
fn from(value: IdlType) -> Self {
|
||||
match value {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//! Anchor IDL.
|
||||
|
||||
pub mod types;
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
pub mod build;
|
||||
|
||||
#[cfg(feature = "convert")]
|
||||
pub mod convert;
|
||||
|
||||
pub use anchor_lang_idl_spec as types;
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
pub use serde_json;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
mod common;
|
||||
mod mods;
|
||||
|
||||
use anchor_lang_idl::types::Idl;
|
||||
use anchor_lang_idl::{convert::convert_idl, types::Idl};
|
||||
use anyhow::anyhow;
|
||||
use quote::{quote, ToTokens};
|
||||
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(std::fs::read)?
|
||||
.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 {
|
||||
|
|
Loading…
Reference in New Issue