Przeglądaj źródła

idl: Add separate spec crate (#3036)

acheron 1 rok temu
rodzic
commit
cc43e67399

+ 1 - 0
CHANGELOG.md

@@ -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
 
 

+ 9 - 0
Cargo.lock

@@ -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"

+ 3 - 2
cli/src/lib.rs

@@ -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)?,

+ 1 - 0
idl/Cargo.toml

@@ -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"

+ 16 - 0
idl/spec/Cargo.toml

@@ -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"] }

+ 1 - 0
idl/src/types.rs → idl/spec/src/lib.rs

@@ -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;
 
 

+ 30 - 35
idl/src/convert.rs

@@ -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
-    /// `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 {
         fn from(value: IdlTypeDefinitionTy) -> Self {
             match value {
             match value {
                 IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
                 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 {
                 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 {

+ 2 - 2
idl/src/lib.rs

@@ -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;

+ 2 - 2
lang/attribute/program/src/declare_program/mod.rs

@@ -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 {