carbon_macros/
schemas.rs

1//! # Schema Module
2//!
3//! The `schema` module provides macros for constructing and organizing
4//! transaction schemas in a hierarchical manner. These macros are designed to
5//! simplify the creation of complex transaction structures by allowing inline
6//! specification of schema elements, which represent instructions
7//! within transactions.
8//!
9//! ## Overview
10//!
11//! This module includes two primary macros:
12//! - **`schema!`**: The main macro for constructing a `TransactionSchema`,
13//!   which is a hierarchical schema representation.
14//! - **`schema_inner!`**: A helper macro, used internally by `schema!` for
15//!   recursive schema node construction.
16//!
17//! Together, these macros enable you to define schema nodes in a flexible and
18//! intuitive manner, allowing for combinations of `Any` and `Instruction` nodes
19//! with optional nested instructions.
20//!
21//! ## Key Macros
22//!
23//! ### `schema!`
24//!
25//! The `schema!` macro is the primary entry point for constructing a
26//! `TransactionSchema`. It parses provided tokens into a `TransactionSchema`
27//! object, allowing inline definition of various schema elements in a tree-like
28//! structure. This macro supports keywords like `any` to create branches that
29//! can match multiple instruction types.
30//!
31//! #### Example
32//!
33//! ```ignore
34//! use your_crate::schema;
35//!
36//! let transaction_schema = schema![
37//!     any,
38//!     [
39//!         AllInstructionTypes::JupSwap(JupiterInstructionType::SwapEvent),
40//!         "jup_swap_event",
41//!         []
42//!     ],
43//!     any,
44//! ];
45//! ```
46//!
47//! This example defines a schema with an `any` branch, an `Instruction` node
48//! with nested instructions, and another `any` branch, creating a flexible
49//! transaction structure. In practical terms, this means that the schema
50//! represents a transaction that has a Jupiter Swap Event instruction anywhere
51//! within the transaction.
52//!
53//! ### `schema_inner!`
54//!
55//! This macro is used internally by `schema!` to build out individual
56//! `SchemaNode` elements. It supports three main syntax patterns: `any`, single
57//! `Instruction` nodes, and nested `Instruction` nodes. Users typically don’t
58//! need to interact with this macro directly, as it’s invoked by `schema!`
59//! to handle recursive node construction.
60//!
61//! #### Supported Syntax Patterns
62//!
63//! 1. **`any`**: Adds an `Any` node, which can match any instruction type,
64//!    multiple times.
65//! 2. **`[$ix_type:expr, $name:expr]`**: Adds an `Instruction` node without
66//!    nested instructions.
67//! 3. **`[$ix_type:expr, $name:expr, [$($inner:tt)*]]`**: Adds an `Instruction`
68//!    node with nested inner instructions.
69//!
70//! ## Notes
71//!
72//! - The `schema!` macro relies on `schema_inner!` for recursive parsing and
73//!   node creation.
74//! - When using the `schema!` macro, ensure that all instruction types and
75//!   identifiers correspond to valid values expected by the `TransactionSchema`
76//!   to avoid compilation errors.
77//!
78//! ## Crate Dependencies
79//!
80//! This module relies on components like `SchemaNode` and
81//! `InstructionSchemaNode` to build the schema tree. Make sure these types are
82//! defined and accessible within the scope of the module’s usage.
83
84/// Constructs a `TransactionSchema` from provided tokens.
85///
86/// The `schema!` macro facilitates the creation of a `TransactionSchema` by
87/// parsing the provided tokens and assembling the structure. This macro
88/// simplifies schema construction by allowing inline specification of schema
89/// elements, including instruction types and associated names.
90///
91/// # Syntax
92///
93/// The `schema!` macro uses a flexible token-based syntax, enabling you to
94/// define schema nodes inline. Nodes are defined in a hierarchical manner, with
95/// keywords like `any` indicating schema branches, followed by instruction
96/// types, string identifiers, and additional attributes or sub-nodes as needed.
97///
98/// # Example
99///
100/// ```ignore
101/// use carbon_macros::schema;
102/// use carbon_macros::schema_inner;
103///
104/// let transaction_schema = schema![
105///     any
106///     [
107///         AllInstructionTypes::JupSwap(JupiterInstructionType::SwapEvent),
108///         "jup_swap_event",
109///         []
110///     ]
111///     any
112/// ];
113/// ```
114///
115/// # Parameters
116///
117/// - `$tt`: The token stream passed to the macro, which may include multiple
118///   schema nodes and attributes. These tokens are parsed to construct the
119///   schema tree, forming a `TransactionSchema` object with a root node
120///   containing the specified elements.
121///
122/// # Returns
123///
124/// This macro returns a `TransactionSchema` instance with the constructed
125/// schema tree based on the tokens provided. The schema is organized with nodes
126/// added to the root, allowing for complex, multi-layered transaction
127/// structures, which represent real transaction instructions, in order.
128///
129/// # Notes
130///
131/// - This macro requires that the inner macro `schema_inner!` is also defined,
132///   as it handles the recursive parsing and node addition to the schema.
133/// - Ensure that types and keywords used within the schema correspond to valid
134///   identifiers and instructions expected by `TransactionSchema`. Inconsistent
135///   tokens may lead to compilation errors or incorrect schema construction.
136#[macro_export]
137macro_rules! schema {
138    ($($tt:tt)*) => {{
139        let mut nodes = Vec::new();
140        schema_inner!(&mut nodes, $($tt)*);
141        carbon_core::schema::TransactionSchema { root: nodes }
142    }};
143}
144
145/// Recursively constructs schema nodes within the `schema!` macro.
146///
147/// The `schema_inner!` macro is utilized internally by the `schema!` macro to
148/// build a `TransactionSchema` structure by parsing tokens into individual
149/// `SchemaNode` elements. This macro supports multiple node types, including
150/// `Any` nodes and `Instruction` nodes with optional nested instructions. Each
151/// parsed node is appended to the provided vector, forming a hierarchical
152/// schema structure.
153///
154/// # Syntax
155///
156/// This macro supports three main syntax patterns:
157///
158/// 1. `any`: Adds an `Any` node to the schema, indicating a branch point that
159///    accepts multiple instructions of any type.
160/// 2. `[$ix_type:expr, $name:expr]`: Adds an `Instruction` node with the
161///    specified instruction type and name, without nested instructions.
162/// 3. `[$ix_type:expr, $name:expr, [$($inner:tt)*]]`: Adds an `Instruction`
163///    node with the specified instruction type and name, including inner
164///    instructions which are parsed recursively.
165///
166/// # Parameters
167///
168/// - `$nodes`: A mutable reference to a `Vec<SchemaNode>`. This vector
169///   accumulates the constructed schema nodes, which can include both `Any` and
170///   `Instruction` node variants with nested instructions.
171/// - `$ix_type`: The instruction type for the node, provided as an expression.
172///   This identifies the specific instruction type in the schema.
173/// - `$name`: A string literal representing the name of the instruction. This
174///   name is associated with the `Instruction` node for identification.
175///
176/// # Returns
177///
178/// This macro modifies the `Vec<SchemaNode>` passed as `$nodes`, adding each
179/// parsed `SchemaNode` to the vector. Nodes can be nested, with the structure
180/// reflecting any provided inner instructions.
181///
182/// # Notes
183///
184/// - This macro is intended for internal use within the `schema!` macro and is
185///   not typically called directly by users.
186/// - Ensure that each `$ix_type` corresponds to a valid instruction type and
187///   that `$name` is a string literal for compatibility with
188///   `InstructionSchemaNode`.
189/// - The recursive structure allows for complex, multi-level instruction
190///   schemas suitable for detailed transaction validation and processing.
191#[macro_export]
192macro_rules! schema_inner {
193    ($nodes:expr, ) => {};
194
195    ($nodes:expr, any $($rest:tt)*) => {
196        $nodes.push(carbon_core::schema::SchemaNode::Any);
197        schema_inner!($nodes, $($rest)*);
198    };
199
200    ($nodes:expr, [$ix_type:expr, $name:expr] $($rest:tt)*) => {
201        $nodes.push(carbon_core::schema::SchemaNode::Instruction(carbon_core::schema::InstructionSchemaNode {
202            ix_type: $ix_type,
203            name: $name.to_string(),
204            inner_instructions: Vec::new(),
205        }));
206        schema_inner!($nodes, $($rest)*);
207    };
208
209    ($nodes:expr, [$ix_type:expr, $name:expr, [$($inner:tt)*]] $($rest:tt)*) => {{
210        let mut inner_nodes = Vec::new();
211        schema_inner!(&mut inner_nodes, $($inner)*);
212        $nodes.push(carbon_core::schema::SchemaNode::Instruction(carbon_core::schema::InstructionSchemaNode {
213            ix_type: $ix_type,
214            name: $name.to_string(),
215            inner_instructions: inner_nodes,
216            }));
217        schema_inner!($nodes, $($rest)*);
218    }};
219}