Skip to main content

hyperstack_macros/
lib.rs

1//! # hyperstack-macros
2//!
3//! Procedural macros for defining HyperStack streams.
4//!
5//! This crate provides the `#[hyperstack]` attribute macro that transforms
6//! annotated Rust structs into full streaming pipeline specifications, including:
7//!
8//! - State struct generation with field accessors
9//! - Handler creation functions for event processing
10//! - IDL/Proto parser integration for Solana programs
11//! - Automatic AST serialization for deployment
12//!
13//! ## Module Usage (IDL-based)
14//!
15//! ```rust,ignore
16//! use hyperstack_macros::{hyperstack, Stream};
17//!
18//! #[hyperstack(idl = "idl.json")]
19//! pub mod my_stream {
20//!     #[entity(name = "MyEntity")]
21//!     #[derive(Stream)]
22//!     struct Entity {
23//!         #[map(from = "MyAccount", field = "value")]
24//!         pub value: u64,
25//!     }
26//! }
27//! ```
28//!
29//! ## Supported Attributes
30//!
31//! - `#[map(...)]` - Map from account fields
32//! - `#[from_instruction(...)]` - Map from instruction fields
33//! - `#[event(...)]` - Capture instruction events
34//! - `#[snapshot(...)]` - Capture entire source data
35//! - `#[aggregate(...)]` - Aggregate field values
36//! - `#[computed(...)]` - Computed fields from other fields
37//! - `#[derive_from(...)]` - Derive values from instructions
38
39// Public modules - AST types needed for SDK generation
40pub(crate) mod ast;
41mod diagnostic;
42pub(crate) mod event_type_helpers;
43
44// Internal modules - not exposed publicly
45mod codegen;
46mod idl_codegen;
47mod idl_parser_gen;
48mod idl_vixen_gen;
49mod parse;
50mod proto_codegen;
51mod stream_spec;
52mod utils;
53mod validation;
54
55use proc_macro::TokenStream;
56use std::collections::HashMap;
57use syn::{ItemMod, ItemStruct};
58
59// Use the stream_spec module functions
60use stream_spec::{process_module, process_struct_with_context};
61
62/// Process a `#[hyperstack(...)]` attribute.
63///
64/// This macro can be applied to:
65/// - A module containing entity structs
66/// - A single struct (legacy usage)
67///
68/// ## Module Usage (IDL-based)
69///
70/// ```rust,ignore
71/// #[hyperstack(idl = "idl.json")]
72/// pub mod my_stream {
73///     #[entity(name = "MyEntity")]
74///     struct Entity {
75///         // fields with mapping attributes
76///     }
77/// }
78/// ```
79///
80/// ## Proto-based Usage
81///
82/// ```rust,ignore
83/// #[hyperstack(proto = ["events.proto"])]
84/// pub mod my_stream {
85///     // entity structs
86/// }
87/// ```
88#[proc_macro_attribute]
89pub fn hyperstack(attr: TokenStream, item: TokenStream) -> TokenStream {
90    expand_hyperstack(attr, item)
91        .unwrap_or_else(syn::Error::into_compile_error)
92        .into()
93}
94
95fn expand_hyperstack(
96    attr: TokenStream,
97    item: TokenStream,
98) -> syn::Result<proc_macro2::TokenStream> {
99    let mod_err = match syn::parse::<ItemMod>(item.clone()) {
100        Ok(module) => return process_module(module, attr),
101        Err(e) => e,
102    };
103
104    let input = syn::parse::<ItemStruct>(item).map_err(|struct_err| {
105        // If neither parse succeeds, prefer the module error since most usages
106        // of #[hyperstack] are on modules.
107        let mut combined = mod_err;
108        combined.combine(struct_err);
109        combined
110    })?;
111
112    let config = parse::parse_stream_spec_attribute(attr)?;
113    if !config.proto_files.is_empty() || !config.idl_files.is_empty() || config.skip_decoders {
114        return Err(syn::Error::new(
115            input.ident.span(),
116            "#[hyperstack(...)] arguments are only supported on modules",
117        ));
118    }
119
120    process_struct_with_context(input, HashMap::new(), false)
121}
122
123/// Derive macro for `Stream`.
124///
125/// This is a marker derive that enables the following attributes on struct fields:
126/// - `#[map(...)]` - Map from account fields
127/// - `#[from_instruction(...)]` - Map from instruction fields
128/// - `#[event(...)]` - Capture instruction events
129/// - `#[snapshot(...)]` - Capture entire source
130/// - `#[aggregate(...)]` - Aggregate field values
131/// - `#[computed(...)]` - Computed fields from other fields
132/// - `#[derive_from(...)]` - Derive values from instructions
133/// - `#[resolve(...)]` - Resolve external data (token metadata via DAS API or data from URLs)
134#[proc_macro_derive(
135    Stream,
136    attributes(
137        map,
138        from_instruction,
139        event,
140        snapshot,
141        aggregate,
142        computed,
143        derive_from,
144        resolve
145    )
146)]
147pub fn stream_derive(_input: TokenStream) -> TokenStream {
148    TokenStream::new()
149}