forge_macros/lib.rs
1use proc_macro::TokenStream;
2
3mod cron;
4mod enum_type;
5mod job;
6mod model;
7mod mutation;
8mod query;
9mod sql_extractor;
10mod workflow;
11
12/// Marks a struct as a FORGE model, generating schema metadata for TypeScript codegen.
13///
14/// # Example
15/// ```ignore
16/// #[forge::model]
17/// pub struct User {
18/// pub id: Uuid,
19/// pub email: String,
20/// pub name: String,
21/// pub created_at: DateTime<Utc>,
22/// }
23/// ```
24#[proc_macro_attribute]
25pub fn model(attr: TokenStream, item: TokenStream) -> TokenStream {
26 model::expand_model(attr, item)
27}
28
29/// Marks an enum for database storage as a PostgreSQL ENUM type.
30///
31/// # Example
32/// ```ignore
33/// #[forge::forge_enum]
34/// pub enum ProjectStatus {
35/// Draft,
36/// Active,
37/// Paused,
38/// Completed,
39/// }
40/// ```
41#[proc_macro_attribute]
42pub fn forge_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
43 enum_type::expand_enum(attr, item)
44}
45
46/// Marks a function as a query (read-only, cacheable, subscribable).
47///
48/// # Attributes
49/// - `cache = "5m"` - Cache TTL (duration like "30s", "5m", "1h")
50/// - `public` - No authentication required
51/// - `require_auth` - Require authentication
52/// - `timeout = 30` - Timeout in seconds
53/// - `tables = ["users", "projects"]` - Explicit table dependencies (for dynamic SQL)
54///
55/// # Table Dependency Extraction
56/// By default, table dependencies are automatically extracted from SQL strings
57/// in the function body at compile time. This enables accurate reactive
58/// subscription invalidation for queries that join multiple tables.
59///
60/// For dynamic SQL (e.g., table names built at runtime), use the `tables`
61/// attribute to explicitly specify dependencies.
62///
63/// # Example
64/// ```ignore
65/// #[forge::query]
66/// pub async fn get_user(ctx: &QueryContext, user_id: Uuid) -> Result<User> {
67/// // Tables automatically extracted from SQL
68/// }
69///
70/// #[forge::query(cache = "5m", require_auth)]
71/// pub async fn get_profile(ctx: &QueryContext) -> Result<Profile> {
72/// let user_id = ctx.require_user_id()?;
73/// // ...
74/// }
75///
76/// #[forge::query(tables = ["users", "audit_log"])]
77/// pub async fn dynamic_query(ctx: &QueryContext, table: String) -> Result<Vec<Row>> {
78/// // Explicit tables for dynamic SQL
79/// }
80/// ```
81#[proc_macro_attribute]
82pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream {
83 query::expand_query(attr, item)
84}
85
86/// Marks a function as a mutation (transactional write).
87///
88/// Mutations run within a database transaction. All changes commit together or roll back on error.
89///
90/// # Attributes
91/// - `require_auth` - Require authentication
92/// - `require_role("admin")` - Require specific role
93/// - `timeout = 30` - Timeout in seconds
94///
95/// # Example
96/// ```ignore
97/// #[forge::mutation]
98/// pub async fn create_project(ctx: &MutationContext, input: CreateProjectInput) -> Result<Project> {
99/// let user_id = ctx.require_user_id()?;
100/// // ...
101/// }
102/// ```
103#[proc_macro_attribute]
104pub fn mutation(attr: TokenStream, item: TokenStream) -> TokenStream {
105 mutation::expand_mutation(attr, item)
106}
107
108/// Marks a function as a background job.
109///
110/// Jobs are durable background tasks that survive server restarts and automatically retry on failure.
111///
112/// # Attributes
113/// - `timeout = "30m"` - Job timeout
114/// - `priority = "normal"` - background, low, normal, high, critical
115/// - `max_attempts = 3` - Maximum retry attempts
116/// - `worker_capability = "general"` - Required worker capability
117///
118/// # Example
119/// ```ignore
120/// #[forge::job]
121/// #[timeout = "30m"]
122/// #[priority = "high"]
123/// pub async fn send_welcome_email(ctx: &JobContext, input: SendEmailInput) -> Result<()> {
124/// // ...
125/// }
126/// ```
127#[proc_macro_attribute]
128pub fn job(attr: TokenStream, item: TokenStream) -> TokenStream {
129 job::job_impl(attr, item)
130}
131
132/// Marks a function as a scheduled cron task.
133///
134/// Cron jobs run on a schedule, exactly once per scheduled time across the cluster.
135///
136/// # Attributes
137/// - `timezone = "UTC"` - Timezone for the schedule
138/// - `catch_up` - Run missed executions after downtime
139/// - `timeout = "1h"` - Execution timeout
140///
141/// # Example
142/// ```ignore
143/// #[forge::cron("0 0 * * *")]
144/// #[timezone = "UTC"]
145/// #[catch_up]
146/// pub async fn daily_cleanup(ctx: &CronContext) -> Result<()> {
147/// // ...
148/// }
149/// ```
150#[proc_macro_attribute]
151pub fn cron(attr: TokenStream, item: TokenStream) -> TokenStream {
152 cron::cron_impl(attr, item)
153}
154
155/// Marks a function as a durable workflow.
156///
157/// Workflows are multi-step processes that survive restarts and handle failures with compensation.
158///
159/// # Attributes
160/// - `version = 1` - Workflow version (increment for breaking changes)
161/// - `timeout = "24h"` - Maximum execution time
162///
163/// # Example
164/// ```ignore
165/// #[forge::workflow]
166/// #[version = 1]
167/// pub async fn user_onboarding(ctx: &WorkflowContext, input: OnboardingInput) -> Result<OnboardingResult> {
168/// let user = ctx.step("create_user", || async { /* ... */ }).await?;
169/// ctx.step("send_welcome", || async { /* ... */ }).await;
170/// Ok(OnboardingResult { user })
171/// }
172/// ```
173#[proc_macro_attribute]
174pub fn workflow(attr: TokenStream, item: TokenStream) -> TokenStream {
175 workflow::workflow_impl(attr, item)
176}