Skip to main content

cognis_macros/
lib.rs

1//! Proc macros for the Cognis framework.
2//!
3//! # Macros
4//!
5//! - [`tool`] — `#[cognis::tool]` attribute that generates a
6//!   `cognis_core::tools::BaseTool` implementation from an `async fn` (or
7//!   from an `impl` block containing one). Uses [`schemars`] under the hood
8//!   for parameter schema generation.
9//! - [`GraphState`] — derive macro for graph state schemas with per-field
10//!   reducers (see attributes `#[reducer(append|last_value|add|merge)]`).
11//!
12//! # JSON Schema generation
13//!
14//! For `#[derive(JsonSchema)]` on your own structs/enums, use the re-export
15//! at `cognis_core::JsonSchema` (which is `schemars::JsonSchema`). The legacy
16//! hand-rolled `JsonSchema` / `ToolSchema` derives in this crate were removed
17//! in favor of the upstream `schemars` crate, which supports recursive types,
18//! `$ref`/`definitions`, doc-comment → `description`, and richer attribute
19//! syntax.
20//!
21//! ```ignore
22//! use cognis_core::JsonSchema;
23//! use serde::{Deserialize, Serialize};
24//!
25//! #[derive(JsonSchema, Serialize, Deserialize)]
26//! struct SearchFilter {
27//!     /// Minimum relevance score
28//!     min_score: f64,
29//!     /// Categories to include
30//!     categories: Vec<String>,
31//! }
32//!
33//! let schema = serde_json::to_value(schemars::schema_for!(SearchFilter)).unwrap();
34//! ```
35
36mod graph_state;
37mod graph_state_v2;
38mod schema_attr;
39mod tool_attr;
40mod tools_impl_attr;
41
42use proc_macro::TokenStream;
43use proc_macro2::TokenStream as TokenStream2;
44use syn::{parse_macro_input, DeriveInput};
45
46// ---------------------------------------------------------------------------
47// #[derive(GraphState)]
48// ---------------------------------------------------------------------------
49
50/// Derive macro for generating graph state schemas with per-field reducers.
51///
52/// # Attributes
53///
54/// - `#[reducer(append)]` — append arrays/values
55/// - `#[reducer(last_value)]` — overwrite with latest (default)
56/// - `#[reducer(add)]` — add numeric values
57/// - `#[reducer(merge)]` — deep-merge JSON objects
58#[proc_macro_derive(GraphState, attributes(reducer))]
59pub fn derive_graph_state(input: TokenStream) -> TokenStream {
60    let input = parse_macro_input!(input as DeriveInput);
61    graph_state::derive_graph_state(input).into()
62}
63
64// ---------------------------------------------------------------------------
65// #[cognis::tool] attribute macro
66// ---------------------------------------------------------------------------
67
68/// `#[cognis::tool]` — attribute macro that generates a
69/// [`cognis_core::tools::BaseTool`] implementation from an `async fn`.
70///
71/// Applies to either a standalone `async fn` or an `impl` block that
72/// contains exactly one `async fn` (the stateful form, where the tool
73/// struct holds configuration such as HTTP clients or API keys).
74///
75/// # Arguments
76///
77/// - `name = "..."` — overrides the tool name (defaults to the fn name).
78/// - `description = "..."` — overrides the fn's doc-comment description.
79/// - `return_direct = true|false` — passes through to `BaseTool::return_direct`.
80///
81/// # Field validators
82///
83/// `#[schema(range(...))]`, `#[schema(length(...))]`, `#[schema(pattern(...))]`,
84/// `#[schema(enum_values(...))]`, and `#[schema(format(...))]` on fn arguments
85/// emit matching runtime checks. Range/length/pattern are also translated into
86/// `#[schemars(...)]` attributes on the generated args struct so they appear
87/// in the JSON Schema produced via `schemars::schema_for!`. Enum-values and
88/// format are merged into the schema during `args_schema()` post-processing.
89///
90/// See `cognis_core::tools::validation` for the runtime helpers the
91/// generated code invokes.
92#[proc_macro_attribute]
93pub fn tool(attr: TokenStream, item: TokenStream) -> TokenStream {
94    let args = parse_macro_input!(attr as tool_attr::ToolArgs);
95    let item_ts: TokenStream2 = item.into();
96    match tool_attr::expand(args, item_ts) {
97        Ok(ts) => ts.into(),
98        Err(e) => e.to_compile_error().into(),
99    }
100}
101
102/// `#[derive(GraphStateV2)]` — v2-shape state derive that emits a typed
103/// sibling `<Name>Update` struct and an `impl GraphState for <Name>`. Use
104/// in v2 code via the re-export `cognis_core::GraphState` (the rename
105/// happens in cognis-core's lib.rs in Plan #2).
106#[proc_macro_derive(GraphStateV2, attributes(reducer, graph_state))]
107pub fn derive_graph_state_v2(input: TokenStream) -> TokenStream {
108    let input = parse_macro_input!(input as DeriveInput);
109    graph_state_v2::derive_graph_state_v2(input).into()
110}
111
112/// `#[tools_impl]` — outer attribute that scans an `impl` block for inner
113/// `#[tool]`-marked async methods and generates one [`cognis_core::tools::BaseTool`]
114/// wrapper per method, plus an `into_tools()` collector method on the user's
115/// struct.
116///
117/// # Arguments
118///
119/// - `crate_path = "..."` — override the framework crate (default: `"cognis_core"`).
120///
121/// Inner `#[tool]` markers on methods follow the same syntax as the standalone
122/// `#[tool]` attribute (name, description, return_direct, crate_path).
123#[proc_macro_attribute]
124pub fn tools_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
125    let args = parse_macro_input!(attr as tools_impl_attr::ToolsImplArgs);
126    let item_ts: TokenStream2 = item.into();
127    match tools_impl_attr::expand(args, item_ts) {
128        Ok(ts) => ts.into(),
129        Err(e) => e.to_compile_error().into(),
130    }
131}