carbon_proc_macros/
lib.rs

1//! # Carbon Proc Macros
2//!
3//! `carbon-proc-macros` is a collection of procedural macros designed to
4//! simplify and enhance Rust-based development for Solana programs using the
5//! Carbon framework. This crate provides macros for generating
6//! deserialization implementations, instruction decoders, and type conversions.
7//!
8//! ## Overview
9//!
10//! The macros in this crate are intended to streamline common patterns
11//! encountered when working with Carbon, particularly around deserialization,
12//! instruction decoding, and structuring custom types. By leveraging
13//! `carbon-proc-macros`, you can reduce the amount of manual coding and ensure
14//! consistent, performant handling of Solana-specific data structures.
15//!
16//! ## Key Features
17//!
18//! - **`CarbonDeserialize`**: Automatically implement the `CarbonDeserialize`
19//!   trait for structs and enums, enabling Borsh-based deserialization with
20//!   optional discriminators for type validation.
21//! - **`Instruction Decoder Collection`**: Create and manage complex
22//!   instruction decoders for multiple Solana programs, simplifying how
23//!   instructions are parsed and categorized.
24//! - **`InstructionType` Derivation**: Derive `InstructionType` enums that
25//!   mirror existing enum structures, providing a simplified, data-free version
26//!   of each variant.
27//!
28//! ## Usage
29//!
30//! To use any of the provided macros, simply import the desired macro into your
31//! Rust program and apply it to the relevant struct or enum.
32//!
33//! ## Notes
34//!
35//! - This crate relies on the `borsh` library for serialization and
36//!   deserialization, so ensure the relevant dependencies are included in your
37//!   project.
38//! - The macros provided are optimized for use within the Carbon framework.
39//!
40//! ## Contribution
41//!
42//! Contributions are welcome! If you have ideas for improving or expanding the
43//! functionality of `carbon_macros`, please consider submitting a pull request
44//! or opening an issue on the project’s GitHub repository.
45use {
46    borsh_derive_internal_satellite::*,
47    proc_macro::TokenStream,
48    proc_macro2::{Span, TokenStream as TokenStream2},
49    quote::{format_ident, quote},
50    syn::{
51        parse::{Parse, ParseStream},
52        parse_macro_input, DeriveInput, Ident, Item, ItemEnum, Lit, Meta, NestedMeta, Token,
53        TypePath,
54    },
55};
56
57/// Automatically generates an implementation of the `CarbonDeserialize` trait.
58///
59/// This derive macro creates the `CarbonDeserialize` implementation for a given
60/// struct or enum, enabling deserialization from a byte slice using the `borsh`
61/// serialization format. If a field in the struct or enum is marked with the
62/// `#[carbon(discriminator)]` attribute, the macro uses this field's value as a
63/// discriminator to match and validate data during deserialization.
64///
65/// # Syntax
66///
67/// To use this macro, annotate your struct or enum with
68/// `#[derive(CarbonDeserialize)]`. Optionally, use the `#[carbon(discriminator
69/// = "0x...")]` attribute to specify a unique discriminator for this type. This
70/// discriminator is validated at the start of the byte slice before proceeding
71/// with full deserialization.
72///
73/// ```ignore
74/// #[derive(CarbonDeserialize)]
75/// #[carbon(discriminator = "0x1234")]
76/// struct MyStruct {
77///     id: u32,
78///     data: String,
79/// }
80/// ```
81///
82/// # Example
83///
84/// ```ignore
85/// use carbon_proc_macros::CarbonDeserialize;
86///
87/// #[derive(CarbonDeserialize)]
88/// #[carbon(discriminator = "0x01")]
89/// struct Message {
90///     header: u16,
91///     body: Vec<u8>,
92/// }
93///
94/// let bytes = vec![0x01, 0x00, 0x10, 0x20, 0x30]; // Serialized data
95/// let message = Message::deserialize(&bytes)
96///     .expect("Failed to deserialize `Message`");
97/// ```
98///
99/// # Parameters
100///
101/// - `input_token_stream`: A `TokenStream` containing the syntax tree of the
102///   input type (struct or enum). The macro parses this to generate the
103///   corresponding `CarbonDeserialize` implementation.
104///
105/// # Return
106///
107/// Returns a `TokenStream` representing the generated `CarbonDeserialize`
108/// implementation. The function expects the target type to implement the
109/// `borsh::BorshDeserialize` trait to support deserialization.
110///
111/// # Notes
112///
113/// - The `#[carbon(discriminator = "0x...")]` attribute is optional. If not
114///   provided, the deserialization proceeds without a discriminator check.
115/// - Ensure the discriminator matches the data's format exactly, as the
116///   deserialization will return `None` if there is a mismatch.
117/// - The macro will panic if the discriminator is invalid or not provided
118///   correctly as a hex string when expected.
119///
120/// # Errors
121///
122/// - The macro will return `None` during deserialization if the data is shorter
123///   than the discriminator or if there is a mismatch between the provided and
124///   expected discriminators.
125#[proc_macro_derive(CarbonDeserialize, attributes(carbon))]
126pub fn carbon_deserialize_derive(input_token_stream: TokenStream) -> TokenStream {
127    let derive_input = input_token_stream.clone();
128    let input = parse_macro_input!(derive_input as DeriveInput);
129    let name = &input.ident;
130
131    let discriminator = get_discriminator(&input.attrs).unwrap_or(quote! { &[] });
132    let deser = gen_borsh_deserialize(input_token_stream);
133
134    let expanded = quote! {
135        #deser
136
137        #[automatically_derived]
138        impl carbon_core::deserialize::CarbonDeserialize for #name {
139            const DISCRIMINATOR: &'static [u8] = #discriminator;
140
141            fn deserialize(data: &[u8]) -> Option<Self> {
142                if data.len() < Self::DISCRIMINATOR.len() {
143                    return None;
144                }
145
146
147                let (disc, mut rest) = data.split_at(Self::DISCRIMINATOR.len());
148                if disc != Self::DISCRIMINATOR {
149                    return None;
150                }
151
152                 match carbon_core::borsh::BorshDeserialize::deserialize(&mut rest) {
153                    Ok(res) => {
154                        if !rest.is_empty() {
155                            carbon_core::log::debug!(
156                                "Not all bytes were read when deserializing {}: {} bytes remaining",
157                                stringify!(#name),
158                                rest.len(),
159                            );
160                        }
161                        Some(res)
162                    }
163                    Err(_) => None,
164                }
165            }
166        }
167    };
168
169    TokenStream::from(expanded)
170}
171
172/// Generates an implementation of the `CarbonDeserialize` trait for a given
173/// type.
174///
175/// This procedural macro automatically derives the `CarbonDeserialize` trait
176/// for structs, enums, or unions, enabling them to be deserialized using Borsh
177/// serialization format. The generated implementation includes type checking
178/// and allows for customized deserialization using the `#[carbon]` attribute to
179/// specify a unique discriminator for the type.
180///
181/// # Syntax
182///
183/// To use this macro, annotate the target type with
184/// `#[derive(CarbonDeserialize)]`. Optionally, you can specify a
185/// `#[carbon(discriminator = "...")]` attribute to define a custom
186/// discriminator, which will be checked during deserialization.
187///
188/// # Example
189///
190/// ```ignore
191/// use carbon_proc_macros::CarbonDeserialize;
192///
193/// #[derive(CarbonDeserialize)]
194/// #[carbon(discriminator = "0x1234")]
195/// struct MyStruct {
196///     id: u32,
197///     name: String,
198/// }
199///
200/// let bytes = ...; // serialized bytes
201/// let my_struct = MyStruct::deserialize(&bytes)
202///     .expect("Failed to deserialize `MyStruct`");
203/// ```
204///
205/// # Parameters
206///
207/// - `input_token_stream`: A `TokenStream` containing the parsed syntax tree of
208///   the target type definition. This input is processed to generate the
209///   appropriate `CarbonDeserialize` implementation.
210///
211/// # Return
212///
213/// Returns a `TokenStream` containing the implementation of the
214/// `CarbonDeserialize` trait for the given type. If successful, this enables
215/// Borsh deserialization with the custom discriminator check.
216///
217/// # Errors
218///
219/// This macro will panic if the target type is not a struct, enum, or union, as
220/// these are the only supported forms for `CarbonDeserialize` derivation.
221/// Additionally, an invalid or missing `#[carbon]` attribute may result in a
222/// deserialization failure due to discriminator mismatch.
223///
224/// # Notes
225///
226/// - Ensure the discriminator length matches the expected format in serialized
227///   data; otherwise, deserialization will return `None`.
228/// - This macro leverages the Borsh serialization framework and assumes that
229///   the type implements `BorshDeserialize` for successful deserialization.
230fn gen_borsh_deserialize(input: TokenStream) -> TokenStream2 {
231    let cratename = Ident::new("borsh", Span::call_site());
232
233    let item: Item = syn::parse(input).expect("Failed to parse input");
234    let res = match item {
235        Item::Struct(item) => struct_de(&item, cratename),
236        Item::Enum(item) => enum_de(&item, cratename),
237        Item::Union(item) => union_de(&item, cratename),
238        // Derive macros can only be defined on structs, enums, and unions.
239        _ => unreachable!(),
240    };
241
242    match res {
243        Ok(res) => res,
244        Err(err) => err.to_compile_error(),
245    }
246}
247
248/// Extracts the discriminator value from a set of attributes.
249///
250/// This function searches through a list of attributes for a `carbon` attribute
251/// containing a `discriminator` key in the format `carbon(discriminator =
252/// "0x...")`. If found, it parses the discriminator as a hexadecimal string and
253/// returns it as a byte slice within a `TokenStream`. If the
254/// `carbon(discriminator = "...")` attribute is not present, the function
255/// returns `None`.
256///
257/// # Syntax
258///
259/// The attribute should be specified in the format:
260///
261/// ```ignore
262/// #[carbon(discriminator = "0x...")]
263/// ```
264///
265/// # Example
266///
267/// ```ignore
268/// use syn::Attribute;
269///
270/// // Example attribute with a discriminator
271/// let attrs: Vec<Attribute> = vec![parse_quote!(#[carbon(discriminator = "0x1234")])];
272/// let discriminator = get_discriminator(&attrs);
273///
274/// assert!(discriminator.is_some());
275/// ```
276///
277/// # Parameters
278///
279/// - `attrs`: A reference to a slice of `syn::Attribute` items. These represent
280///   the attributes attached to a Rust item, from which the function will
281///   attempt to extract the discriminator.
282///
283/// # Return
284///
285/// Returns an `Option<TokenStream>` containing the parsed byte slice if a
286/// valid `carbon(discriminator = "...")` attribute is found. If the attribute
287/// is not present, or if the value is not a valid hexadecimal string, the
288/// function returns `None`.
289///
290/// # Errors
291///
292/// If the `carbon(discriminator = "...")` attribute contains an invalid hex
293/// string, this function will panic with an error message indicating
294/// "Invalid hex string". To avoid runtime panics, ensure that the hex string
295/// provided is correctly formatted.
296///
297/// # Notes
298///
299/// - The `discriminator` value must be a hexadecimal string prefixed with "0x".
300/// - If the hex string is invalid, an error will be raised; consider adding
301///   further error handling if required for your application.
302fn get_discriminator(attrs: &[syn::Attribute]) -> Option<quote::__private::TokenStream> {
303    attrs.iter().find_map(|attr| {
304        if attr.path.is_ident("carbon") {
305            attr.parse_meta().ok().and_then(|meta| {
306                if let Meta::List(list) = meta {
307                    list.nested.iter().find_map(|nested| {
308                        if let NestedMeta::Meta(Meta::NameValue(nv)) = nested {
309                            if nv.path.is_ident("discriminator") {
310                                if let Lit::Str(lit_str) = &nv.lit {
311                                    let disc_str = lit_str.value();
312                                    let disc_bytes = hex::decode(disc_str.trim_start_matches("0x"))
313                                        .expect("Invalid hex string");
314                                    let disc_array = disc_bytes.as_slice();
315                                    return Some(quote! { &[#(#disc_array),*] });
316                                }
317                            }
318                        }
319                        None
320                    })
321                } else {
322                    None
323                }
324            })
325        } else {
326            None
327        }
328    })
329}
330
331/// Represents the parsed input for instruction decoder collection macros.
332///
333/// The `InstructionMacroInput` struct holds the essential elements required to
334/// generate instruction decoding logic within
335/// `instruction_decoder_collection!` and `instruction_decoder_collection_fast!`.
336/// It includes the names of the enums for instructions, instruction types, and
337/// programs, along with a collection of `InstructionEntry` mappings that define
338/// the relationships between program variants, decoder expressions, and
339/// instruction types.
340///
341/// # Fields
342///
343/// - `instructions_enum_name`: The identifier for the enum representing the
344///   instructions. This enum contains the primary instruction variants to be
345///   used within the macro.
346/// - `instruction_types_enum_name`: The identifier for the enum representing
347///   the various types of instructions. This enum categorizes instructions by
348///   their specific types.
349/// - `programs_enum_name`: The identifier for the enum representing the
350///   programs. This enum is used to identify different programs and their
351///   corresponding variants in the macro.
352/// - `entries`: A vector of `InstructionEntry` items, each of which maps a
353///   program variant to a decoder expression and an instruction type, defining
354///   how each instruction should be processed.
355///
356/// # Example
357///
358/// ```ignore
359/// use syn::Ident;
360/// use syn::parse_quote;
361///
362/// let instructions_enum_name: Ident = parse_quote!(InstructionsEnum);
363/// let instruction_types_enum_name: Ident = parse_quote!(InstructionTypesEnum);
364/// let programs_enum_name: Ident = parse_quote!(ProgramsEnum);
365/// let entries = vec![
366///     InstructionEntry {
367///         program_variant: parse_quote!(MyProgram),
368///         decoder_expr: parse_quote!(my_decoder),
369///         instruction_type: parse_quote!(MyInstructionType),
370///     },
371/// ];
372///
373/// let input = InstructionMacroInput {
374///     instructions_enum_name,
375///     instruction_types_enum_name,
376///     programs_enum_name,
377///     entries,
378/// };
379/// ```
380///
381/// # Usage
382///
383/// The `InstructionMacroInput` struct is primarily used within procedural
384/// macros for parsing and storing elements required for generating complex
385/// decoding logic. Each field serves a specific role in defining how
386/// instructions are categorized, decoded, and mapped to programs.
387///
388/// # Notes
389///
390/// - Ensure that all identifiers correspond to valid enums in your macro
391///   context, as these will be referenced directly when generating code.
392/// - The `entries` vector should contain an `InstructionEntry` for each mapping
393///   you wish to include. Each entry specifies a program variant and the logic
394///   to decode its instructions.
395struct InstructionMacroInput {
396    instructions_enum_name: Ident,
397    instruction_types_enum_name: Ident,
398    programs_enum_name: Ident,
399    entries: Vec<InstructionEntry>,
400}
401
402/// Represents a mapping between a program variant, its decoder expression, and
403/// an instruction type.
404///
405/// The `InstructionEntry` struct is used to define individual mappings within
406/// the `instruction_decoder_collection!` macro. Each entry specifies a unique
407/// program variant, decoder for its instructions, and the
408/// resulting instruction type. This structure enables the macro to understand
409/// and process different program instructions efficiently.
410///
411/// # Fields
412///
413/// - `program_variant`: An `Ident` representing the variant of the program
414///   enum. This is used to match against specific programs within the macro.
415/// - `decoder_expr`: An expression (`syn::Expr`) that defines the decoding
416///   logic for this program variant.
417/// - `instruction_type`: A `TypePath` that specifies the type of instruction
418///   resulting from the decoding process. This type should correspond to one of
419///   the variants in the instruction types enum.
420///
421/// # Example
422///
423/// ```ignore
424///
425/// let program_variant: Ident = parse_quote!(MyProgram);
426/// let decoder_expr: Expr = parse_quote!(MyDecoder);
427/// let instruction_type: TypePath = parse_quote!(MyInstructionType);
428///
429/// let entry = InstructionEntry {
430///     program_variant,
431///     decoder_expr,
432///     instruction_type,
433/// };
434/// ```
435///
436/// # Usage
437///
438/// The `InstructionEntry` struct is used as part of a vector within the
439/// `InstructionMacroInput` struct. Each entry allows the macro to handle
440/// multiple programs and their associated instruction types in a modular
441/// and scalable manner. By specifying each program's decoding logic and
442/// instruction type, the macro can dynamically adapt to different program
443/// requirements.
444///
445/// # Notes
446///
447/// - Ensure that `decoder_expr` correctly implements the decoding functionality
448///   expected by the associated `instruction_type`. Misalignment between the
449///   decoder expression and the expected instruction type can lead to runtime
450///   errors.
451/// - This struct is typically not used standalone but as part of a collection
452///   that defines multiple program-instruction mappings for procedural macros.
453struct InstructionEntry {
454    program_variant: Ident,
455    program_id_path: Option<syn::Path>,
456    decoder_expr: syn::Expr,
457    instruction_type: TypePath,
458}
459
460/// Parses input for the instruction decoder collection macros.
461///
462/// This implementation of the `Parse` trait is responsible for parsing the
463/// input provided to `instruction_decoder_collection!` and
464/// `instruction_decoder_collection_fast!`. It accepts either the legacy
465/// 3-part entry form or the new 4-part entry form, and produces a unified
466/// `InstructionMacroInput` structure used to generate decoding logic.
467///
468/// # Syntax
469///
470/// The input format for the macro should follow this structure:
471///
472/// ```ignore
473/// instruction_decoder_collection_fast!(
474///     InstructionsEnum, InstructionTypesEnum, ProgramsEnum,
475///     // 4-part: ProgramVariant => ProgramIdPath => DecoderExpr => InstructionType
476///     ProgramVariant => my_decoder_crate::PROGRAM_ID => decoder_expr => InstructionType,
477///     // 3-part (legacy): ProgramVariant => DecoderExpr => InstructionType
478///     AnotherVariant => decoder_expr => AnotherInstructionType,
479/// );
480/// ```
481///
482/// - `InstructionsEnum`: Identifier for the enum representing instruction names
483///   with data.
484/// - `InstructionTypesEnum`: Identifier for the enum representing types of
485///   instructions.
486/// - `ProgramsEnum`: Identifier for the enum representing program types.
487/// - Each `InstructionEntry` is either:
488///   - 4-part: program variant, program id path, decoder expression, and
489///     instruction type; or
490///   - 3-part: program variant, decoder expression, and instruction type.
491///
492/// # Example
493///
494/// ```ignore
495///
496/// let input = parse_quote! {
497///     MyInstructionsEnum, MyInstructionTypesEnum, MyProgramsEnum,
498///     MyProgram => my_decoder => MyInstruction,
499///     AnotherProgram => another_decoder => AnotherInstruction,
500/// };
501///
502/// let parsed_input: InstructionMacroInput = syn::parse2(input)
503///     .expect("Failed to parse macro input");
504/// ```
505///
506/// # Parameters
507///
508/// - `input`: A `ParseStream` representing the macro input, expected to
509///   contain:
510///   - An enum name for instructions
511///   - An enum name for instruction types
512///   - An enum name for program types
513///   - A series of `InstructionEntry` mappings for program variants and
514///     instructions.
515///
516/// # Return
517///
518/// Returns a `syn::Result<Self>`, which will be an `InstructionMacroInput`
519/// containing the parsed components if successful. On failure, returns a
520/// `syn::Error` indicating the specific parsing issue.
521///
522/// # Notes
523///
524/// - The macro requires the input format to be strictly adhered to, with commas
525///   separating the enum identifiers and each `InstructionEntry` mapping.
526///   Ensure that all mappings include `=>` separators between program variants,
527///   decoder expressions, and instruction types.
528/// - This parsing process is typically used within a procedural macro and may
529///   be subject to Rust's macro hygiene and parsing rules.
530///
531/// # Errors
532///
533/// An error will be returned if:
534/// - An identifier or component is missing or improperly formatted
535/// - The input stream does not conform to the expected comma-separated format
536impl Parse for InstructionMacroInput {
537    fn parse(input: ParseStream) -> syn::Result<Self> {
538        let instructions_enum_name: Ident = input.parse()?;
539        input.parse::<Token![,]>()?;
540        let instruction_types_enum_name: Ident = input.parse()?;
541        input.parse::<Token![,]>()?;
542        let programs_enum_name: Ident = input.parse()?;
543        input.parse::<Token![,]>()?;
544
545        let mut entries = Vec::new();
546
547        while !input.is_empty() {
548            let program_variant: Ident = input.parse()?;
549            input.parse::<Token![=>]>()?;
550
551            // Attempt to parse 4-part syntax: variant => PROGRAM_ID_PATH => DECODER => INSTRUCTION
552            // Use a forked parser to decide without consuming on failure.
553            let mut use_four_part = false;
554            let fork = input.fork();
555            let program_id_path_candidate: syn::Path = match fork.parse() {
556                Ok(p) => p,
557                Err(_) => {
558                    // Cannot parse path; must be legacy 3-part
559                    syn::Path {
560                        leading_colon: None,
561                        segments: syn::punctuated::Punctuated::new(),
562                    }
563                }
564            };
565
566            if !program_id_path_candidate.segments.is_empty()
567                && fork.parse::<Token![=>]>().is_ok()
568                && fork.parse::<syn::Expr>().is_ok()
569                && fork.parse::<Token![=>]>().is_ok()
570                && fork.parse::<TypePath>().is_ok()
571            {
572                use_four_part = true;
573            }
574
575            if use_four_part {
576                let program_id_path: syn::Path = input.parse()?;
577                input.parse::<Token![=>]>()?;
578                let decoder_expr: syn::Expr = input.parse()?;
579                input.parse::<Token![=>]>()?;
580                let instruction_type: TypePath = input.parse()?;
581
582                entries.push(InstructionEntry {
583                    program_variant,
584                    program_id_path: Some(program_id_path),
585                    decoder_expr,
586                    instruction_type,
587                });
588            } else {
589                // Legacy 3-part syntax: variant => DECODER => INSTRUCTION
590                let decoder_expr: syn::Expr = input.parse()?;
591                input.parse::<Token![=>]>()?;
592                let instruction_type: TypePath = input.parse()?;
593
594                entries.push(InstructionEntry {
595                    program_variant,
596                    program_id_path: None,
597                    decoder_expr,
598                    instruction_type,
599                });
600            }
601
602            if input.peek(Token![,]) {
603                input.parse::<Token![,]>()?;
604            }
605        }
606
607        Ok(InstructionMacroInput {
608            instructions_enum_name,
609            instruction_types_enum_name,
610            programs_enum_name,
611            entries,
612        })
613    }
614}
615
616/// Generates a collection of instruction decoders and associated enums.
617///
618/// Deprecated: Prefer `instruction_decoder_collection_fast!`, which dispatches
619/// by `program_id` using a `match` and is more efficient.
620///
621/// This macro creates a set of enums and implementations to handle decoding
622/// of instructions for multiple Solana programs. It generates:
623/// 1. An enum for all instructions
624/// 2. An enum for all instruction types
625/// 3. An enum for all programs
626/// 4. An implementation of InstructionDecoderCollection trait
627///
628/// # Syntax
629///
630/// The macro takes the following arguments:
631/// 1. Name for the all-encompassing instructions enum
632/// 2. Name for the all-encompassing instruction types enum
633/// 3. Name for the programs enum
634/// 4. One or more entries, each consisting of:
635///    - Program variant name
636///    - Decoder expression
637///    - Instruction enum for the program
638///
639/// # Example
640///
641/// ```ignore
642/// instruction_decoder_collection!(
643///     AllInstructions, AllInstructionTypes, AllPrograms,
644///     JupSwap => JupiterDecoder => JupiterInstruction,
645///     MeteoraSwap => MeteoraDecoder => MeteoraInstruction
646/// );
647/// ```
648///
649///
650/// This example will generate:
651/// - AllInstructions enum with variants JupSwap(JupiterInstruction) and
652///   MeteoraSwap(MeteoraInstruction)
653/// - AllInstructionTypes enum with variants JupSwap(JupiterInstructionType) and
654///   MeteoraSwap(MeteoraInstructionType)
655/// - AllPrograms enum with variants JupSwap and MeteoraSwap
656/// - An implementation of InstructionDecoderCollection for AllInstructions
657///
658/// # Generated Code
659///
660/// The macro generates the following:
661/// 1. An enum AllInstructions containing variants for each program's
662///    instructions
663/// 2. An enum AllInstructionTypes containing variants for each program's
664///    instruction types
665/// 3. An enum AllPrograms listing all programs
666/// 4. An implementation of InstructionDecoderCollection for AllInstructions,
667///    including:
668///    - parse_instruction method to decode instructions
669///    - get_type method to retrieve the instruction type
670///
671/// # Note
672///
673/// Ensure that all necessary types (e.g., DecodedInstruction,
674/// InstructionDecoderCollection) are in scope where this macro is used.
675///
676/// Deprecated: Prefer `instruction_decoder_collection_fast!`, which dispatches
677/// by `program_id` using a `match` and is more efficient.
678#[deprecated(
679    note = "Use `instruction_decoder_collection_fast!` for faster dispatch by program_id."
680)]
681#[proc_macro]
682pub fn instruction_decoder_collection(input: TokenStream) -> TokenStream {
683    let input = parse_macro_input!(input as InstructionMacroInput);
684
685    let instructions_enum_name = input.instructions_enum_name;
686    let instruction_types_enum_name = input.instruction_types_enum_name;
687    let programs_enum_name = input.programs_enum_name;
688    let entries = input.entries;
689
690    let mut instruction_variants = Vec::new();
691    let mut instruction_type_variants = Vec::new();
692    let mut program_variants = Vec::new();
693    let mut parse_instruction_arms = Vec::new();
694    let mut get_type_arms = Vec::new();
695
696    for entry in entries {
697        let program_variant = entry.program_variant;
698        let decoder_expr = entry.decoder_expr;
699        let instruction_type = entry.instruction_type;
700
701        let instruction_enum_ident = &instruction_type
702            .path
703            .segments
704            .last()
705            .expect("segment")
706            .ident;
707        let instruction_type_ident = format_ident!("{}Type", instruction_enum_ident);
708
709        instruction_variants.push(quote! {
710            #program_variant(#instruction_enum_ident)
711        });
712        instruction_type_variants.push(quote! {
713            #program_variant(#instruction_type_ident)
714        });
715        program_variants.push(quote! {
716            #program_variant
717        });
718
719        parse_instruction_arms.push(quote! {
720            if let Some(decoded_instruction) = #decoder_expr.decode_instruction(&instruction) {
721                return Some(carbon_core::instruction::DecodedInstruction {
722                    program_id: instruction.program_id,
723                    accounts: instruction.accounts.clone(),
724                    data: #instructions_enum_name::#program_variant(decoded_instruction.data),
725                });
726            }
727        });
728
729        get_type_arms.push(quote! {
730            #instructions_enum_name::#program_variant(instruction) => {
731                #instruction_types_enum_name::#program_variant(instruction.get_instruction_type())
732            }
733        });
734    }
735
736    let expanded = quote! {
737        #[derive(Debug, Clone, std::hash::Hash, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
738        pub enum #instructions_enum_name {
739            #(#instruction_variants),*
740        }
741
742        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
743        pub enum #instruction_types_enum_name {
744            #(#instruction_type_variants),*
745        }
746
747        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
748        pub enum #programs_enum_name {
749            #(#program_variants),*
750        }
751
752        impl carbon_core::collection::InstructionDecoderCollection for #instructions_enum_name {
753            type InstructionType = #instruction_types_enum_name;
754
755            fn parse_instruction(
756                instruction: &solana_instruction::Instruction
757            ) -> Option<carbon_core::instruction::DecodedInstruction<Self>> {
758                #(#parse_instruction_arms)*
759                None
760            }
761
762            fn get_type(&self) -> Self::InstructionType {
763                match self {
764                    #(#get_type_arms),*
765                }
766            }
767        }
768    };
769
770    TokenStream::from(expanded)
771}
772
773/// Similar to `instruction_decoder_collection!` but dispatches by
774/// `program_id` via `match` for faster decoding.
775///
776/// Syntax
777///
778/// ```ignore
779/// instruction_decoder_collection_fast!(
780///     AllInstructionsEnum,
781///     AllInstructionTypesEnum,
782///     AllProgramsEnum,
783///     // 4-part (preferred): Variant => ProgramIdPath => DecoderExpr => InstructionTypePath
784///     Pumpfun => carbon_pumpfun_decoder::PROGRAM_ID => carbon_pumpfun_decoder::PumpfunDecoder => carbon_pumpfun_decoder::instructions::PumpfunInstruction,
785///     // 3-part (legacy): falls back to slow sequential decode
786///     PumpSwap => carbon_pump_swap_decoder::PumpSwapDecoder => carbon_pump_swap_decoder::instructions::PumpSwapInstruction,
787/// );
788/// ```
789#[proc_macro]
790pub fn instruction_decoder_collection_fast(input: TokenStream) -> TokenStream {
791    let input = parse_macro_input!(input as InstructionMacroInput);
792
793    let instructions_enum_name = input.instructions_enum_name;
794    let instruction_types_enum_name = input.instruction_types_enum_name;
795    let programs_enum_name = input.programs_enum_name;
796    let entries = input.entries;
797
798    let mut instruction_variants = Vec::new();
799    let mut instruction_type_variants = Vec::new();
800    let mut program_variants = Vec::new();
801    let mut parse_instruction_match_arms = Vec::new();
802    let mut fallback_decode_blocks = Vec::new();
803    let mut get_type_arms = Vec::new();
804
805    for entry in entries {
806        let program_variant = entry.program_variant;
807        let decoder_expr = entry.decoder_expr;
808        let instruction_type = entry.instruction_type;
809
810        let instruction_enum_ident = &instruction_type
811            .path
812            .segments
813            .last()
814            .expect("segment")
815            .ident;
816        let instruction_type_ident = format_ident!("{}Type", instruction_enum_ident);
817
818        // Resolve the program id path for dispatch. Prefer explicitly provided
819        // path if available; otherwise, fall back to inferring `<crate>::PROGRAM_ID`
820        // from the first segment of the instruction type path for backward
821        // compatibility with older 3-part syntax.
822        let explicit_program_id_path = entry.program_id_path;
823
824        instruction_variants.push(quote! {
825            #program_variant(#instruction_enum_ident)
826        });
827        instruction_type_variants.push(quote! {
828            #program_variant(#instruction_type_ident)
829        });
830        program_variants.push(quote! {
831            #program_variant
832        });
833
834        if let Some(program_id_path) = explicit_program_id_path {
835            parse_instruction_match_arms.push(quote! {
836                #program_id_path => {
837                    if let Some(decoded_instruction) = #decoder_expr.decode_instruction(&instruction) {
838                        Some(carbon_core::instruction::DecodedInstruction {
839                            program_id: instruction.program_id,
840                            accounts: instruction.accounts.clone(),
841                            data: #instructions_enum_name::#program_variant(decoded_instruction.data),
842                        })
843                    } else {
844                        None
845                    }
846                }
847            });
848        } else {
849            // No program id path: include in slow-path fallback.
850            fallback_decode_blocks.push(quote! {
851                if let Some(decoded_instruction) = #decoder_expr.decode_instruction(&instruction) {
852                    return Some(carbon_core::instruction::DecodedInstruction {
853                        program_id: instruction.program_id,
854                        accounts: instruction.accounts.clone(),
855                        data: #instructions_enum_name::#program_variant(decoded_instruction.data),
856                    });
857                }
858            });
859        }
860
861        get_type_arms.push(quote! {
862            #instructions_enum_name::#program_variant(instruction) => {
863                #instruction_types_enum_name::#program_variant(instruction.get_instruction_type())
864            }
865        });
866    }
867
868    let expanded = quote! {
869        #[derive(Debug, Clone, std::hash::Hash, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
870        pub enum #instructions_enum_name {
871            #(#instruction_variants),*
872        }
873
874        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
875        pub enum #instruction_types_enum_name {
876            #(#instruction_type_variants),*
877        }
878
879        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
880        pub enum #programs_enum_name {
881            #(#program_variants),*
882        }
883
884        impl carbon_core::collection::InstructionDecoderCollection for #instructions_enum_name {
885            type InstructionType = #instruction_types_enum_name;
886
887            fn parse_instruction(
888                instruction: &solana_instruction::Instruction
889            ) -> Option<carbon_core::instruction::DecodedInstruction<Self>> {
890                match instruction.program_id {
891                    #(#parse_instruction_match_arms),*
892                    _ => {
893                        #(#fallback_decode_blocks)*
894                        None
895                    }
896                }
897            }
898
899            fn get_type(&self) -> Self::InstructionType {
900                match self {
901                    #(#get_type_arms),*
902                }
903            }
904        }
905    };
906
907    TokenStream::from(expanded)
908}
909
910/// Derives a corresponding `InstructionType` enum for a given enum.
911///
912/// This procedural macro generates an `InstructionType` enum that mirrors the
913/// variants of the specified input enum. The `InstructionType` enum contains
914/// only the variant names, without any associated data. This is particularly
915/// useful for implementations that require a simplified representation of
916/// instruction types, such as in `InstructionDecoderCollection`.
917///
918/// # Syntax
919///
920/// To use this macro, annotate your enum with `#[derive(InstructionType)]`.
921/// This will automatically create an `InstructionType` enum with the same
922/// variant names as your original enum, suffixed with `Type`. Additionally,
923/// a `get_instruction_type` method will be implemented on the original enum,
924/// returning the corresponding `InstructionType` variant for each instance.
925///
926/// ```ignore
927/// #[derive(InstructionType)]
928/// enum MyEnum {
929///     VariantOne,
930///     VariantTwo(u32),
931///     VariantThree { data: String },
932/// }
933/// ```
934///
935/// The derived `InstructionType` enum will look like:
936///
937/// ```rust
938/// #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
939/// pub enum MyEnumType {
940///     VariantOne,
941///     VariantTwo,
942///     VariantThree,
943/// }
944/// ```
945///
946/// # Example
947///
948/// ```rust
949/// use carbon_proc_macros::InstructionType;
950///
951/// #[derive(InstructionType)]
952/// enum Instructions {
953///     NoData,
954///     WithData(u64),
955///     NamedData { field: String },
956/// }
957///
958/// let inst = Instructions::WithData(42);
959/// let inst_type = inst.get_instruction_type();
960///
961/// assert_eq!(inst_type, InstructionsType::WithData);
962/// ```
963///
964/// # Parameters
965///
966/// - `input`: A `TokenStream` representing the input enum, which is parsed to
967///   extract variant names and generate the `InstructionType` enum. Each
968///   variant is processed without any associated data.
969///
970/// # Return
971///
972/// Returns a `TokenStream` containing the expanded code for the generated
973/// `InstructionType` enum and the implementation of the `get_instruction_type`
974/// method on the original enum.
975///
976/// # Notes
977///
978/// - This macro will only derive an `InstructionType` enum for the input enum.
979///   It does not modify or remove any data associated with the original enum
980///   variants.
981/// - The generated `InstructionType` enum derives `Debug`, `Clone`,
982///   `PartialEq`, `Eq`, and `serde::Serialize`, making it suitable for use in
983///   serialization contexts as well as comparison and debugging.
984#[proc_macro_derive(InstructionType)]
985pub fn instruction_type_derive(input: TokenStream) -> TokenStream {
986    let input = parse_macro_input!(input as ItemEnum);
987
988    let enum_name = &input.ident;
989    let instruction_type_name = format_ident!("{}Type", enum_name);
990
991    let variants = input.variants.iter().map(|v| {
992        let variant_ident = &v.ident;
993        quote! {
994            #variant_ident
995        }
996    });
997
998    let instruction_type_enum = quote! {
999        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
1000        pub enum #instruction_type_name {
1001            #(#variants),*
1002        }
1003    };
1004
1005    let get_instruction_type_arms = input.variants.iter().map(|v| {
1006        let variant_ident = &v.ident;
1007        if let syn::Fields::Unit = v.fields {
1008            quote! {
1009                Self::#variant_ident => #instruction_type_name::#variant_ident,
1010            }
1011        } else if let syn::Fields::Unnamed(_) = v.fields {
1012            quote! {
1013                Self::#variant_ident(..) => #instruction_type_name::#variant_ident,
1014            }
1015        } else if let syn::Fields::Named(_) = v.fields {
1016            quote! {
1017                Self::#variant_ident { .. } => #instruction_type_name::#variant_ident,
1018            }
1019        } else {
1020            quote! {}
1021        }
1022    });
1023
1024    let impl_get_instruction_type = quote! {
1025        impl #enum_name {
1026            pub fn get_instruction_type(&self) -> #instruction_type_name {
1027                match self {
1028                    #(#get_instruction_type_arms)*
1029                }
1030            }
1031        }
1032    };
1033
1034    let expanded = quote! {
1035        #instruction_type_enum
1036
1037        #impl_get_instruction_type
1038    };
1039
1040    TokenStream::from(expanded)
1041}