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::*,
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 the `instruction_decoder_collection!` macro.
332///
333/// The `InstructionMacroInput` struct holds the essential elements required
334/// to generate instruction decoding logic within the
335/// `instruction_decoder_collection!` macro. It includes the names of the enums
336/// for instructions, instruction types, and programs, along with a collection
337/// of `InstructionEntry` mappings that define the relationships between program
338/// variants, decoder expressions, and instruction types.
339///
340/// # Fields
341///
342/// - `instructions_enum_name`: The identifier for the enum representing the
343///   instructions. This enum contains the primary instruction variants to be
344///   used within the macro.
345/// - `instruction_types_enum_name`: The identifier for the enum representing
346///   the various types of instructions. This enum categorizes instructions by
347///   their specific types.
348/// - `programs_enum_name`: The identifier for the enum representing the
349///   programs. This enum is used to identify different programs and their
350///   corresponding variants in the macro.
351/// - `entries`: A vector of `InstructionEntry` items, each of which maps a
352///   program variant to a decoder expression and an instruction type, defining
353///   how each instruction should be processed.
354///
355/// # Example
356///
357/// ```ignore
358/// use syn::Ident;
359/// use syn::parse_quote;
360///
361/// let instructions_enum_name: Ident = parse_quote!(InstructionsEnum);
362/// let instruction_types_enum_name: Ident = parse_quote!(InstructionTypesEnum);
363/// let programs_enum_name: Ident = parse_quote!(ProgramsEnum);
364/// let entries = vec![
365///     InstructionEntry {
366///         program_variant: parse_quote!(MyProgram),
367///         decoder_expr: parse_quote!(my_decoder),
368///         instruction_type: parse_quote!(MyInstructionType),
369///     },
370/// ];
371///
372/// let input = InstructionMacroInput {
373///     instructions_enum_name,
374///     instruction_types_enum_name,
375///     programs_enum_name,
376///     entries,
377/// };
378/// ```
379///
380/// # Usage
381///
382/// The `InstructionMacroInput` struct is primarily used within procedural
383/// macros for parsing and storing elements required for generating complex
384/// decoding logic. Each field serves a specific role in defining how
385/// instructions are categorized, decoded, and mapped to programs.
386///
387/// # Notes
388///
389/// - Ensure that all identifiers correspond to valid enums in your macro
390///   context, as these will be referenced directly when generating code.
391/// - The `entries` vector should contain an `InstructionEntry` for each mapping
392///   you wish to include. Each entry specifies a program variant and the logic
393///   to decode its instructions.
394struct InstructionMacroInput {
395    instructions_enum_name: Ident,
396    instruction_types_enum_name: Ident,
397    programs_enum_name: Ident,
398    entries: Vec<InstructionEntry>,
399}
400
401/// Represents a mapping between a program variant, its decoder expression, and
402/// an instruction type.
403///
404/// The `InstructionEntry` struct is used to define individual mappings within
405/// the `instruction_decoder_collection!` macro. Each entry specifies a unique
406/// program variant, decoder for its instructions, and the
407/// resulting instruction type. This structure enables the macro to understand
408/// and process different program instructions efficiently.
409///
410/// # Fields
411///
412/// - `program_variant`: An `Ident` representing the variant of the program
413///   enum. This is used to match against specific programs within the macro.
414/// - `decoder_expr`: An expression (`syn::Expr`) that defines the decoding
415///   logic for this program variant.
416/// - `instruction_type`: A `TypePath` that specifies the type of instruction
417///   resulting from the decoding process. This type should correspond to one of
418///   the variants in the instruction types enum.
419///
420/// # Example
421///
422/// ```ignore
423///
424/// let program_variant: Ident = parse_quote!(MyProgram);
425/// let decoder_expr: Expr = parse_quote!(MyDecoder);
426/// let instruction_type: TypePath = parse_quote!(MyInstructionType);
427///
428/// let entry = InstructionEntry {
429///     program_variant,
430///     decoder_expr,
431///     instruction_type,
432/// };
433/// ```
434///
435/// # Usage
436///
437/// The `InstructionEntry` struct is used as part of a vector within the
438/// `InstructionMacroInput` struct. Each entry allows the macro to handle
439/// multiple programs and their associated instruction types in a modular
440/// and scalable manner. By specifying each program's decoding logic and
441/// instruction type, the macro can dynamically adapt to different program
442/// requirements.
443///
444/// # Notes
445///
446/// - Ensure that `decoder_expr` correctly implements the decoding functionality
447///   expected by the associated `instruction_type`. Misalignment between the
448///   decoder expression and the expected instruction type can lead to runtime
449///   errors.
450/// - This struct is typically not used standalone but as part of a collection
451///   that defines multiple program-instruction mappings for procedural macros.
452struct InstructionEntry {
453    program_variant: Ident,
454    decoder_expr: syn::Expr,
455    instruction_type: TypePath,
456}
457
458/// Parses input for the `instruction_decoder_collection!` macro.
459///
460/// This implementation of the `Parse` trait is responsible for parsing the
461/// input provided to the `instruction_decoder_collection!` macro. It expects a
462/// comma-separated sequence of identifiers followed by a series of
463/// `InstructionEntry` items, which define mappings between program variants,
464/// decoder expressions, and instruction types. These entries are collected into
465/// an `InstructionMacroInput` struct, which can then be used to generate
466/// instruction decoding logic.
467///
468/// # Syntax
469///
470/// The input format for the macro should follow this structure:
471///
472/// ```ignore
473/// instruction_decoder_collection!(
474///     InstructionsEnum, InstructionTypesEnum, ProgramsEnum,
475///     ProgramVariant => decoder_expr => InstructionType,
476///     ProgramVariant => decoder_expr => InstructionType,
477///     ...
478/// );
479/// ```
480///
481/// - `InstructionsEnum`: Identifier for the enum representing instruction names
482///   with data.
483/// - `InstructionTypesEnum`: Identifier for the enum representing types of
484///   instructions.
485/// - `ProgramsEnum`: Identifier for the enum representing program types.
486/// - Each `InstructionEntry` consists of a program variant, a decoder
487///   expression, and an instruction type, separated by `=>` and followed by a
488///   comma.
489///
490/// # Example
491///
492/// ```ignore
493///
494/// let input = parse_quote! {
495///     MyInstructionsEnum, MyInstructionTypesEnum, MyProgramsEnum,
496///     MyProgram => my_decoder => MyInstruction,
497///     AnotherProgram => another_decoder => AnotherInstruction,
498/// };
499///
500/// let parsed_input: InstructionMacroInput = syn::parse2(input)
501///     .expect("Failed to parse macro input");
502/// ```
503///
504/// # Parameters
505///
506/// - `input`: A `ParseStream` representing the macro input, expected to
507///   contain:
508///   - An enum name for instructions
509///   - An enum name for instruction types
510///   - An enum name for program types
511///   - A series of `InstructionEntry` mappings for program variants and
512///     instructions.
513///
514/// # Return
515///
516/// Returns a `syn::Result<Self>`, which will be an `InstructionMacroInput`
517/// containing the parsed components if successful. On failure, returns a
518/// `syn::Error` indicating the specific parsing issue.
519///
520/// # Notes
521///
522/// - The macro requires the input format to be strictly adhered to, with commas
523///   separating the enum identifiers and each `InstructionEntry` mapping.
524///   Ensure that all mappings include `=>` separators between program variants,
525///   decoder expressions, and instruction types.
526/// - This parsing process is typically used within a procedural macro and may
527///   be subject to Rust's macro hygiene and parsing rules.
528///
529/// # Errors
530///
531/// An error will be returned if:
532/// - An identifier or component is missing or improperly formatted
533/// - The input stream does not conform to the expected comma-separated format
534impl Parse for InstructionMacroInput {
535    fn parse(input: ParseStream) -> syn::Result<Self> {
536        let instructions_enum_name: Ident = input.parse()?;
537        input.parse::<Token![,]>()?;
538        let instruction_types_enum_name: Ident = input.parse()?;
539        input.parse::<Token![,]>()?;
540        let programs_enum_name: Ident = input.parse()?;
541        input.parse::<Token![,]>()?;
542
543        let mut entries = Vec::new();
544
545        while !input.is_empty() {
546            let program_variant: Ident = input.parse()?;
547            input.parse::<Token![=>]>()?;
548            let decoder_expr: syn::Expr = input.parse()?;
549            input.parse::<Token![=>]>()?;
550            let instruction_type: TypePath = input.parse()?;
551
552            entries.push(InstructionEntry {
553                program_variant,
554                decoder_expr,
555                instruction_type,
556            });
557
558            if input.peek(Token![,]) {
559                input.parse::<Token![,]>()?;
560            }
561        }
562
563        Ok(InstructionMacroInput {
564            instructions_enum_name,
565            instruction_types_enum_name,
566            programs_enum_name,
567            entries,
568        })
569    }
570}
571
572/// Generates a collection of instruction decoders and associated enums.
573///
574/// This macro creates a set of enums and implementations to handle decoding
575/// of instructions for multiple Solana programs. It generates:
576/// 1. An enum for all instructions
577/// 2. An enum for all instruction types
578/// 3. An enum for all programs
579/// 4. An implementation of InstructionDecoderCollection trait
580///
581/// # Syntax
582///
583/// The macro takes the following arguments:
584/// 1. Name for the all-encompassing instructions enum
585/// 2. Name for the all-encompassing instruction types enum
586/// 3. Name for the programs enum
587/// 4. One or more entries, each consisting of:
588///    - Program variant name
589///    - Decoder expression
590///    - Instruction enum for the program
591///
592/// # Example
593///
594/// ```ignore
595/// instruction_decoder_collection!(
596///     AllInstructions, AllInstructionTypes, AllPrograms,
597///     JupSwap => JupiterDecoder => JupiterInstruction,
598///     MeteoraSwap => MeteoraDecoder => MeteoraInstruction
599/// );
600/// ```
601///
602///
603/// This example will generate:
604/// - AllInstructions enum with variants JupSwap(JupiterInstruction) and
605///   MeteoraSwap(MeteoraInstruction)
606/// - AllInstructionTypes enum with variants JupSwap(JupiterInstructionType) and
607///   MeteoraSwap(MeteoraInstructionType)
608/// - AllPrograms enum with variants JupSwap and MeteoraSwap
609/// - An implementation of InstructionDecoderCollection for AllInstructions
610///
611/// # Generated Code
612///
613/// The macro generates the following:
614/// 1. An enum AllInstructions containing variants for each program's
615///    instructions
616/// 2. An enum AllInstructionTypes containing variants for each program's
617///    instruction types
618/// 3. An enum AllPrograms listing all programs
619/// 4. An implementation of InstructionDecoderCollection for AllInstructions,
620///    including:
621///    - parse_instruction method to decode instructions
622///    - get_type method to retrieve the instruction type
623///
624/// # Note
625///
626/// Ensure that all necessary types (e.g., DecodedInstruction,
627/// InstructionDecoderCollection) are in scope where this macro is used.
628#[proc_macro]
629pub fn instruction_decoder_collection(input: TokenStream) -> TokenStream {
630    let input = parse_macro_input!(input as InstructionMacroInput);
631
632    let instructions_enum_name = input.instructions_enum_name;
633    let instruction_types_enum_name = input.instruction_types_enum_name;
634    let programs_enum_name = input.programs_enum_name;
635    let entries = input.entries;
636
637    let mut instruction_variants = Vec::new();
638    let mut instruction_type_variants = Vec::new();
639    let mut program_variants = Vec::new();
640    let mut parse_instruction_arms = Vec::new();
641    let mut get_type_arms = Vec::new();
642
643    for entry in entries {
644        let program_variant = entry.program_variant;
645        let decoder_expr = entry.decoder_expr;
646        let instruction_type = entry.instruction_type;
647
648        let instruction_enum_ident = &instruction_type
649            .path
650            .segments
651            .last()
652            .expect("segment")
653            .ident;
654        let instruction_type_ident = format_ident!("{}Type", instruction_enum_ident);
655
656        instruction_variants.push(quote! {
657            #program_variant(#instruction_enum_ident)
658        });
659        instruction_type_variants.push(quote! {
660            #program_variant(#instruction_type_ident)
661        });
662        program_variants.push(quote! {
663            #program_variant
664        });
665
666        parse_instruction_arms.push(quote! {
667            if let Some(decoded_instruction) = #decoder_expr.decode_instruction(&instruction) {
668                return Some(carbon_core::instruction::DecodedInstruction {
669                    program_id: instruction.program_id,
670                    accounts: instruction.accounts.clone(),
671                    data: #instructions_enum_name::#program_variant(decoded_instruction.data),
672                });
673            }
674        });
675
676        get_type_arms.push(quote! {
677            #instructions_enum_name::#program_variant(instruction) => {
678                #instruction_types_enum_name::#program_variant(instruction.get_instruction_type())
679            }
680        });
681    }
682
683    let expanded = quote! {
684        #[derive(Debug, Clone, std::hash::Hash, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
685        pub enum #instructions_enum_name {
686            #(#instruction_variants),*
687        }
688
689        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
690        pub enum #instruction_types_enum_name {
691            #(#instruction_type_variants),*
692        }
693
694        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
695        pub enum #programs_enum_name {
696            #(#program_variants),*
697        }
698
699        impl carbon_core::collection::InstructionDecoderCollection for #instructions_enum_name {
700            type InstructionType = #instruction_types_enum_name;
701
702            fn parse_instruction(
703                instruction: &solana_instruction::Instruction
704            ) -> Option<carbon_core::instruction::DecodedInstruction<Self>> {
705                #(#parse_instruction_arms)*
706                None
707            }
708
709            fn get_type(&self) -> Self::InstructionType {
710                match self {
711                    #(#get_type_arms),*
712                }
713            }
714        }
715    };
716
717    TokenStream::from(expanded)
718}
719
720/// Derives a corresponding `InstructionType` enum for a given enum.
721///
722/// This procedural macro generates an `InstructionType` enum that mirrors the
723/// variants of the specified input enum. The `InstructionType` enum contains
724/// only the variant names, without any associated data. This is particularly
725/// useful for implementations that require a simplified representation of
726/// instruction types, such as in `InstructionDecoderCollection`.
727///
728/// # Syntax
729///
730/// To use this macro, annotate your enum with `#[derive(InstructionType)]`.
731/// This will automatically create an `InstructionType` enum with the same
732/// variant names as your original enum, suffixed with `Type`. Additionally,
733/// a `get_instruction_type` method will be implemented on the original enum,
734/// returning the corresponding `InstructionType` variant for each instance.
735///
736/// ```ignore
737/// #[derive(InstructionType)]
738/// enum MyEnum {
739///     VariantOne,
740///     VariantTwo(u32),
741///     VariantThree { data: String },
742/// }
743/// ```
744///
745/// The derived `InstructionType` enum will look like:
746///
747/// ```rust
748/// #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
749/// pub enum MyEnumType {
750///     VariantOne,
751///     VariantTwo,
752///     VariantThree,
753/// }
754/// ```
755///
756/// # Example
757///
758/// ```rust
759/// use carbon_proc_macros::InstructionType;
760///
761/// #[derive(InstructionType)]
762/// enum Instructions {
763///     NoData,
764///     WithData(u64),
765///     NamedData { field: String },
766/// }
767///
768/// let inst = Instructions::WithData(42);
769/// let inst_type = inst.get_instruction_type();
770///
771/// assert_eq!(inst_type, InstructionsType::WithData);
772/// ```
773///
774/// # Parameters
775///
776/// - `input`: A `TokenStream` representing the input enum, which is parsed to
777///   extract variant names and generate the `InstructionType` enum. Each
778///   variant is processed without any associated data.
779///
780/// # Return
781///
782/// Returns a `TokenStream` containing the expanded code for the generated
783/// `InstructionType` enum and the implementation of the `get_instruction_type`
784/// method on the original enum.
785///
786/// # Notes
787///
788/// - This macro will only derive an `InstructionType` enum for the input enum.
789///   It does not modify or remove any data associated with the original enum
790///   variants.
791/// - The generated `InstructionType` enum derives `Debug`, `Clone`,
792///   `PartialEq`, `Eq`, and `serde::Serialize`, making it suitable for use in
793///   serialization contexts as well as comparison and debugging.
794#[proc_macro_derive(InstructionType)]
795pub fn instruction_type_derive(input: TokenStream) -> TokenStream {
796    let input = parse_macro_input!(input as ItemEnum);
797
798    let enum_name = &input.ident;
799    let instruction_type_name = format_ident!("{}Type", enum_name);
800
801    let variants = input.variants.iter().map(|v| {
802        let variant_ident = &v.ident;
803        quote! {
804            #variant_ident
805        }
806    });
807
808    let instruction_type_enum = quote! {
809        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
810        pub enum #instruction_type_name {
811            #(#variants),*
812        }
813    };
814
815    let get_instruction_type_arms = input.variants.iter().map(|v| {
816        let variant_ident = &v.ident;
817        if let syn::Fields::Unit = v.fields {
818            quote! {
819                Self::#variant_ident => #instruction_type_name::#variant_ident,
820            }
821        } else if let syn::Fields::Unnamed(_) = v.fields {
822            quote! {
823                Self::#variant_ident(..) => #instruction_type_name::#variant_ident,
824            }
825        } else if let syn::Fields::Named(_) = v.fields {
826            quote! {
827                Self::#variant_ident { .. } => #instruction_type_name::#variant_ident,
828            }
829        } else {
830            quote! {}
831        }
832    });
833
834    let impl_get_instruction_type = quote! {
835        impl #enum_name {
836            pub fn get_instruction_type(&self) -> #instruction_type_name {
837                match self {
838                    #(#get_instruction_type_arms)*
839                }
840            }
841        }
842    };
843
844    let expanded = quote! {
845        #instruction_type_enum
846
847        #impl_get_instruction_type
848    };
849
850    TokenStream::from(expanded)
851}