|
@@ -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() },
|
|
|
|
+ ],
|
|
|
|
+ }
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+}
|