shank-parse 2.0.0

A proc-macro crate that generates Rust client code from Shank/Anchor IDL JSON files for Solana programs.
Documentation
shank_parse::shank_parse!("../idl/counter.json");

#[cfg(test)]
mod tests {
    use super::counter::accounts::*;
    use super::counter::instructions::*;
    use super::counter::types::*;
    use super::counter::ID;
    use shank_parse::__private::Pubkey;

    // ── Program ID ────────────────────────────────────────────────────────────

    #[test]
    fn test_program_id() {
        assert_eq!(
            ID.to_string(),
            "8F1XtWR4wTs37nnutBvd2MWpCTfb7XAciFYkw5XHaENj"
        );
    }

    // ── accounts ─────────────────────────────────────────────────────────────

    #[test]
    fn test_counter_from_account_data() {
        let mut data = Vec::new();
        data.push(1u8);
        data.extend_from_slice(&42u64.to_le_bytes());

        let counter = Counter::from_account_data(&data).unwrap();
        assert_eq!(counter.bump, 1);
        assert_eq!(counter.count, 42);
    }

    #[test]
    fn test_counter_authority_from_account_data() {
        let authority = [7u8; 32];
        let mut data = Vec::new();
        data.extend_from_slice(&authority);
        data.push(5u8);
        data.extend_from_slice(&100u64.to_le_bytes());

        let ca = CounterAuthority::from_account_data(&data).unwrap();
        assert_eq!(ca.authority, authority);
        assert_eq!(ca.bump, 5);
        assert_eq!(ca.count, 100);
    }

    #[test]
    fn test_counter_from_account_data_insufficient() {
        let data = vec![1u8];
        assert!(Counter::from_account_data(&data).is_err());
    }

    // ── instructions ─────────────────────────────────────────────────────────

    #[test]
    fn test_init_counter_instruction() {
        let program_id = Pubkey::new_from_array([1u8; 32]);
        let accounts = InitCounterAccounts {
            payer: Pubkey::new_from_array([2u8; 32]),
            counter: Pubkey::new_from_array([3u8; 32]),
            system_program: Pubkey::new_from_array([4u8; 32]),
        };
        let args = InitCounterArgs { count: 42 };
        let ix = init_counter(&program_id, &accounts, &args).unwrap();

        assert_eq!(ix.program_id, program_id);
        assert_eq!(ix.accounts.len(), 3);
        assert_eq!(ix.data[0], 0); // discriminant
        assert_eq!(
            u64::from_le_bytes(ix.data[1..9].try_into().expect("slice")),
            42
        );
        assert!(!ix.accounts[0].is_writable); // payer: not mut
        assert!(ix.accounts[0].is_signer); // payer: signer
        assert!(ix.accounts[1].is_writable); // counter: mut
        assert!(!ix.accounts[1].is_signer); // counter: not signer
    }

    #[test]
    fn test_increase_counter_instruction() {
        let program_id = Pubkey::new_from_array([1u8; 32]);
        let accounts = IncreaseCounterAccounts {
            counter: Pubkey::new_from_array([3u8; 32]),
        };
        let ix = increase_counter(&program_id, &accounts).unwrap();

        assert_eq!(ix.program_id, program_id);
        assert_eq!(ix.accounts.len(), 1);
        assert_eq!(ix.data, vec![1u8]); // discriminant only
    }

    #[test]
    fn test_init_counter_authority_instruction() {
        let program_id = Pubkey::new_from_array([1u8; 32]);
        let accounts = InitCounterAuhthorityAccounts {
            payer: Pubkey::new_from_array([2u8; 32]),
            counter_authority: Pubkey::new_from_array([3u8; 32]),
            system_program: Pubkey::new_from_array([4u8; 32]),
        };
        let args = InitCounterAuthorityArgs { count: 99 };
        let ix = init_counter_auhthority(&program_id, &accounts, &args).unwrap();

        assert_eq!(ix.program_id, program_id);
        assert_eq!(ix.accounts.len(), 3);
        assert_eq!(ix.data[0], 2); // discriminant
    }

    #[test]
    fn test_increase_counter_authority_instruction() {
        let program_id = Pubkey::new_from_array([1u8; 32]);
        let accounts = IncreaseCounterAuthorityAccounts {
            authority: Pubkey::new_from_array([2u8; 32]),
            counter_authority: Pubkey::new_from_array([3u8; 32]),
        };
        let ix = increase_counter_authority(&program_id, &accounts).unwrap();

        assert_eq!(ix.data, vec![3u8]); // discriminant only
        assert!(ix.accounts[0].is_signer); // authority
    }

    // ── events ───────────────────────────────────────────────────────────────

    #[test]
    fn test_counter_increased_try_from_slice() {
        // event struct decoding skips no discriminant: just the payload fields
        let payload = 42u64.to_le_bytes();
        let ev = CounterIncreased::try_from_slice(&payload).unwrap();
        assert_eq!(ev.new_count, 42);
    }

    #[test]
    fn test_counter_initialized_try_from_slice() {
        let payload = 10u64.to_le_bytes();
        let ev = CounterInitialized::try_from_slice(&payload).unwrap();
        assert_eq!(ev.count, 10);
    }

    #[test]
    fn test_counter_event_enum_try_from_slice() {
        // event enum decoding reads the leading u8 variant tag, then the payload
        let mut data0 = vec![0u8]; // CounterInitialized
        data0.extend_from_slice(&10u64.to_le_bytes());
        assert_eq!(
            CounterEvent::try_from_slice(&data0).unwrap(),
            CounterEvent::CounterInitialized(CounterInitialized { count: 10 })
        );

        let mut data1 = vec![1u8]; // CounterIncreased
        data1.extend_from_slice(&20u64.to_le_bytes());
        assert_eq!(
            CounterEvent::try_from_slice(&data1).unwrap(),
            CounterEvent::CounterIncreased(CounterIncreased { new_count: 20 })
        );
    }

    #[test]
    fn test_event_try_from_slice_rejects_unknown_tag() {
        // tag 9 is not a valid variant
        let mut data = vec![9u8];
        data.extend_from_slice(&1u64.to_le_bytes());
        assert!(CounterEvent::try_from_slice(&data).is_err());
    }

    #[test]
    fn test_event_try_from_slice_rejects_truncated_payload() {
        // valid tag but missing the u64 payload
        assert!(CounterEvent::try_from_slice(&[0u8]).is_err());
    }
}