boson_macros/lib.rs
1//! Proc macros for Boson background work.
2//!
3//! This crate exposes [`task`], an attribute macro that turns an async function into a Boson
4//! task with:
5//! - a typed params struct (`<FnName>Params`),
6//! - a typed enqueue handle (`<TaskName>::send_with`),
7//! - and link-time registration (see
8//! [Mode 1](https://docs.rs/uf-boson/latest/boson/index.html#mode-1--embedded-one-binary) /
9//! [Mode 2](https://docs.rs/uf-boson/latest/boson/index.html#mode-2--remote-worker-two-binaries)
10//! on the [`boson`](https://docs.rs/uf-boson) crate for worker vs enqueue-host boot).
11//!
12//! # Define a task
13//!
14//! ```ignore
15//! use boson_core::ExecutionContext;
16//!
17//! #[boson::task(name = "notify_user")]
18//! pub async fn notify_user(
19//! ctx: Box<dyn ExecutionContext>,
20//! user_id: String,
21//! message: String,
22//! ) -> boson_core::Result<()> {
23//! tracing::info!(actor = ctx.label(), %user_id, %message);
24//! Ok(())
25//! }
26//! ```
27//!
28//! The first parameter must be `Box<dyn ExecutionContext>`. How that context is built from
29//! `actor_json` is configured when the worker boots — see [`ExecutionContext`](https://docs.rs/boson-core/latest/boson_core/trait.ExecutionContext.html)
30//! and [`BosonBuilder`](https://docs.rs/boson-runtime/latest/boson_runtime/struct.BosonBuilder.html).
31//!
32//! # Enqueue work
33//!
34//! After **any process** that will call `send_with` has called
35//! [`configure`](https://docs.rs/boson-runtime/latest/boson_runtime/fn.configure.html) (Mode 1
36//! embedded **or** Mode 2 enqueue-only host):
37//!
38//! ```ignore
39//! let job_id = NotifyUser::send_with(
40//! serde_json::json!({"System": {"operation": "notify"}}),
41//! NotifyUserParams {
42//! user_id: "user:123".into(),
43//! message: "Welcome!".into(),
44//! },
45//! ).await?;
46//! ```
47//!
48//! Pass the same `actor_json` shape you would use with [`Boson::enqueue`](https://docs.rs/boson-runtime/latest/boson_runtime/struct.Boson.html#method.enqueue).
49//! The worker that **runs** the job must have discovered the task via
50//! [`auto_registry`](https://docs.rs/boson-runtime/latest/boson_runtime/struct.BosonBuilder.html#method.auto_registry)
51//! (and linked the crate that defines it).
52//!
53//! # Project setup (once per crate)
54//!
55//! - **Depend** — add this crate plus `quark`, `boson-runtime`, `boson-core`, `serde`, and
56//! `serde_json` to the crate that owns the handler (see [README](../README.md)).
57//! - **Link** — when the handler lives in a library crate, make that crate a dependency of the
58//! **worker** binary so inventory entries are linked (for example `use my_worker as _;` in `main`).
59//!
60//! # Boot once (not per task)
61//!
62//! Worker / enqueue-host boot (`BosonBuilder`, `auto_registry`, `configure`, identity factory) is
63//! **not** repeated for each new task. See the [`boson`](https://docs.rs/uf-boson) crate
64//! [Getting started](https://docs.rs/uf-boson/latest/boson/index.html#getting-started) and the
65//! [`task_macro` example](https://github.com/unified-field-dev/boson/blob/main/boson/examples/task_macro.rs).
66//!
67//! # Generated items
68//!
69//! For a task function `notify_user` with task name `"notify_user"`, the macro generates:
70//!
71//! | Item | Purpose |
72//! |------|---------|
73//! | `NotifyUserParams` | Serde payload struct for task arguments |
74//! | `NotifyUser` | Handle type with `send_with(actor_json, params)` |
75//! | `__notify_user_impl` | Original function body (internal) |
76//!
77//! Registration for worker dispatch is emitted at compile time; boot-time collection is described
78//! on [`TaskRegistry`](https://docs.rs/boson-runtime/latest/boson_runtime/struct.TaskRegistry.html) and in the [`boson`](https://docs.rs/uf-boson) crate
79//! [Mode 1](https://docs.rs/uf-boson/latest/boson/index.html#mode-1--embedded-one-binary) /
80//! [Mode 2](https://docs.rs/uf-boson/latest/boson/index.html#mode-2--remote-worker-two-binaries) sections.
81//!
82//! Task handle names are derived from the **task name** (dots become underscores, `PascalCase`).
83//! Params struct names are derived from the **function name**.
84//!
85//! # Macro attributes
86//!
87//! `name` is required and must be the first attribute. Additional policy fields seed the initial
88//! [`TaskConfig`](https://docs.rs/boson-core/latest/boson_core/struct.TaskConfig.html) on first enqueue (runtime admin may override later):
89//!
90//! | Attribute | Default | Meaning |
91//! |-----------|---------|---------|
92//! | `name` | *(required)* | Registry key and enqueue target |
93//! | `priority` | `1` | Default priority (lower = higher priority) |
94//! | `pool` | `"global"` | Worker pool name |
95//! | `idempotency_mode` | *(inherit)* | `"lwt"` or `"none"` (override runtime default) |
96//! | `max_attempts` | `3` | Default retry max attempts |
97//! | `base_delay_ms` | `1000` | Retry base delay in milliseconds |
98//! | `backoff_multiplier` | `2.0` | Retry backoff multiplier |
99//! | `max_delay_ms` | `30000` | Retry max delay cap in milliseconds |
100//! | `max_in_flight` | `100` | Default max in-flight jobs (`0` = unlimited) |
101//! | `max_enqueue_per_second` | `50` | Default enqueue rate limit (`0` = unlimited) |
102//!
103//! ```ignore
104//! #[boson::task(
105//! name = "process_order",
106//! priority = 10,
107//! pool = "checkout",
108//! max_in_flight = 200,
109//! )]
110//! async fn process_order(ctx: Box<dyn ExecutionContext>, order_id: String) -> boson_core::Result<()> {
111//! Ok(())
112//! }
113//! ```
114//!
115//! # Contract
116//!
117//! The annotated function must:
118//! - be `async`
119//! - accept `Box<dyn ExecutionContext>` as the first argument
120//! - return `Result<()>` (for example `boson_core::Result<()>`)
121//! - include `name = "..."` as the first macro attribute
122//!
123//! Free functions only — methods with `&self` are rejected at compile time.
124
125use proc_macro::TokenStream;
126
127mod task;
128mod task_attrs;
129mod task_expand;
130mod task_validate;
131
132/// Marks an async function as a Boson task.
133///
134/// Generates a params struct, a typed enqueue handle, and a registration entry for worker dispatch.
135/// See the [crate-level documentation](self) for define/enqueue examples, policy attributes, and
136/// worker boot (cross-link to the [`boson`](https://docs.rs/uf-boson) crate).
137///
138/// # Contract
139///
140/// - Function must be `async`.
141/// - First parameter must be `Box<dyn ExecutionContext>`.
142/// - Return type must be `Result<()>` (typically `boson_core::Result<()>`).
143/// - `name` attribute is required (must be first).
144///
145/// # Policy attributes
146///
147/// Optional: `priority`, `pool`, `idempotency_mode` (`"lwt"` / `"none"`), `max_attempts`, `base_delay_ms`, `backoff_multiplier`,
148/// `max_delay_ms`, `max_in_flight`, `max_enqueue_per_second`. See crate docs for defaults.
149#[proc_macro_attribute]
150pub fn task(attr: TokenStream, item: TokenStream) -> TokenStream {
151 task::task_impl(attr, item)
152}