Skip to main content

golem_rust_macro/
lib.rs

1// Copyright 2024-2026 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// The proc-macro entry points must be excluded from the test build because
16// test-r generates a non-macro entry point. Their absence makes production
17// implementation paths appear unused only while compiling the test harness.
18#![cfg_attr(test, allow(dead_code, unused_imports))]
19
20use proc_macro::TokenStream;
21use proc_macro_crate::{FoundCrate, crate_name};
22use proc_macro2::Span;
23
24use crate::transaction::golem_operation_impl;
25
26mod agentic;
27mod rpc_client_common;
28mod tool;
29mod transaction;
30
31#[cfg(test)]
32test_r::enable!();
33
34#[cfg(not(test))]
35#[proc_macro_derive(MultimodalSchema)]
36pub fn derive_multimodal(input: TokenStream) -> TokenStream {
37    agentic::derive_multimodal(input)
38}
39
40#[cfg(not(test))]
41#[proc_macro_derive(ConfigSchema, attributes(config_schema))]
42pub fn derive_config_schema(input: TokenStream) -> TokenStream {
43    let golem_rust_crate_ident = get_golem_rust_crate_ident();
44
45    agentic::derive_config_schema(input, &golem_rust_crate_ident)
46}
47
48#[cfg(not(test))]
49#[proc_macro_derive(AllowedLanguages, attributes(code))]
50pub fn derive_allowed_languages(input: TokenStream) -> TokenStream {
51    let golem_rust_crate_ident = get_golem_rust_crate_ident();
52
53    agentic::derive_allowed_languages(input, &golem_rust_crate_ident)
54}
55
56#[cfg(not(test))]
57#[proc_macro_derive(AllowedMimeTypes, attributes(mime_type))]
58pub fn derive_allowed_mimetypes(input: TokenStream) -> TokenStream {
59    let golem_rust_crate_ident = get_golem_rust_crate_ident();
60
61    agentic::derive_allowed_mime_types(input, &golem_rust_crate_ident)
62}
63
64#[cfg(not(test))]
65#[proc_macro_attribute]
66pub fn description(_attr: TokenStream, item: TokenStream) -> TokenStream {
67    item
68}
69
70#[cfg(not(test))]
71#[proc_macro_attribute]
72pub fn prompt(_attr: TokenStream, item: TokenStream) -> TokenStream {
73    item
74}
75
76#[cfg(not(test))]
77#[proc_macro_attribute]
78pub fn endpoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
79    item
80}
81
82#[cfg(not(test))]
83#[proc_macro_attribute]
84pub fn read_only(_attr: TokenStream, item: TokenStream) -> TokenStream {
85    item
86}
87
88/// Defines a function as an `Operation` that can be used in transactions
89#[cfg(not(test))]
90#[proc_macro_attribute]
91pub fn golem_operation(attr: TokenStream, item: TokenStream) -> TokenStream {
92    golem_operation_impl(attr, item)
93}
94
95#[cfg(not(test))]
96#[proc_macro_attribute]
97pub fn agent_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
98    agentic::agent_definition_impl(attr, item)
99}
100
101#[cfg(not(test))]
102#[proc_macro_attribute]
103pub fn agent_implementation(attr: TokenStream, item: TokenStream) -> TokenStream {
104    agentic::agent_implementation_impl(attr, item)
105}
106
107/// Registers an implementation of the SDK's `HttpRouter` trait.
108#[cfg(not(test))]
109#[proc_macro_attribute]
110pub fn http_router(attr: TokenStream, item: TokenStream) -> TokenStream {
111    agentic::http_router::expand(attr.into(), item.into())
112        .unwrap_or_else(|error| error.to_compile_error())
113        .into()
114}
115
116#[cfg(not(test))]
117#[proc_macro_attribute]
118pub fn agent_client(attr: TokenStream, item: TokenStream) -> TokenStream {
119    agentic::agent_client_impl(attr, item, &get_golem_rust_crate_ident())
120}
121
122#[cfg(not(test))]
123#[proc_macro_attribute]
124pub fn tool_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
125    tool::tool_definition_impl(attr, item, &get_golem_rust_crate_ident())
126}
127
128#[cfg(not(test))]
129#[proc_macro_attribute]
130pub fn tool_implementation(attr: TokenStream, item: TokenStream) -> TokenStream {
131    tool::tool_implementation_impl(attr, item, &get_golem_rust_crate_ident())
132}
133
134#[doc(hidden)]
135#[cfg(not(test))]
136#[proc_macro_attribute]
137pub fn native_tool_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
138    tool::native::native_tool_definition_impl(attr, item, &get_native_tool_crate_ident())
139}
140
141#[doc(hidden)]
142#[cfg(not(test))]
143#[proc_macro_attribute]
144pub fn native_tool_implementation(attr: TokenStream, item: TokenStream) -> TokenStream {
145    tool::native::native_tool_implementation_impl(attr, item, &get_native_tool_crate_ident())
146}
147
148fn get_native_tool_crate_ident() -> syn::Ident {
149    match crate_name("golem-native-tool") {
150        Ok(FoundCrate::Itself) => syn::Ident::new("crate", Span::call_site()),
151        Ok(FoundCrate::Name(name)) => syn::Ident::new(&name, Span::call_site()),
152        Err(_) => syn::Ident::new("golem_native_tool", Span::call_site()),
153    }
154}
155
156#[cfg(not(test))]
157#[proc_macro_attribute]
158pub fn tool_middleware(attr: TokenStream, item: TokenStream) -> TokenStream {
159    tool::tool_middleware_impl(attr, item, &get_golem_rust_crate_ident())
160}
161
162#[cfg(not(test))]
163#[proc_macro_attribute]
164pub fn universal_tool_middleware(attr: TokenStream, item: TokenStream) -> TokenStream {
165    tool::universal_tool_middleware_impl(attr, item, &get_golem_rust_crate_ident())
166}
167
168#[cfg(not(test))]
169#[proc_macro_derive(ToolError, attributes(tool_error, example))]
170pub fn derive_tool_error(input: TokenStream) -> TokenStream {
171    let guest = crate_name("golem-rust").is_ok() || crate_name("golem-native-tool").is_err();
172    tool::derive_tool_error_impl(input, &get_tool_schema_crate_ident(), guest)
173}
174
175fn get_tool_schema_crate_ident() -> syn::Ident {
176    if crate_name("golem-rust").is_ok() {
177        get_golem_rust_crate_ident()
178    } else {
179        match crate_name("golem-native-tool") {
180            Ok(FoundCrate::Itself) => syn::Ident::new("crate", Span::call_site()),
181            Ok(FoundCrate::Name(name)) => syn::Ident::new(&name, Span::call_site()),
182            Err(_) => syn::Ident::new("golem_rust", Span::call_site()),
183        }
184    }
185}
186
187#[doc(hidden)]
188#[cfg(not(test))]
189#[proc_macro]
190pub fn __golem_emit_tool_middleware_leaf(input: TokenStream) -> TokenStream {
191    match tool::middleware_surface::emit_tool_middleware_leaf(input.into()) {
192        Ok(tokens) => tokens.into(),
193        Err(error) => error.to_compile_error().into(),
194    }
195}
196
197// Helper attributes consumed by `#[tool_definition]`. A correct use sits on a
198// method inside a `#[tool_definition]` trait, where the outer macro strips it
199// before it ever reaches the compiler. Registering them as real proc-macro
200// attributes keeps them recognized in any position; if one is ever actually
201// expanded it means it was misplaced (outside a tool trait, or ordered before
202// `#[tool_definition]`), so it fails loudly instead of being silently ignored.
203fn misplaced_tool_helper_attr(name: &str) -> TokenStream {
204    syn::Error::new(
205        Span::call_site(),
206        format!("#[{name}] may only be used on methods inside a #[tool_definition] trait"),
207    )
208    .to_compile_error()
209    .into()
210}
211
212#[cfg(not(test))]
213#[proc_macro_attribute]
214pub fn arg(_attr: TokenStream, _item: TokenStream) -> TokenStream {
215    misplaced_tool_helper_attr("arg")
216}
217
218#[cfg(not(test))]
219#[proc_macro_attribute]
220pub fn command(_attr: TokenStream, _item: TokenStream) -> TokenStream {
221    misplaced_tool_helper_attr("command")
222}
223
224#[cfg(not(test))]
225#[proc_macro_attribute]
226pub fn constraint(_attr: TokenStream, _item: TokenStream) -> TokenStream {
227    misplaced_tool_helper_attr("constraint")
228}
229
230#[cfg(not(test))]
231#[proc_macro_attribute]
232pub fn result(_attr: TokenStream, _item: TokenStream) -> TokenStream {
233    misplaced_tool_helper_attr("result")
234}
235
236// get the identifier of golem_rust crate to use for referencing the `golem-rust` crate
237// within the macros. This handles the case where the crate is renamed in Cargo.toml
238// or when the macro is used within the `golem-rust` crate itself.
239fn get_golem_rust_crate_ident() -> syn::Ident {
240    match crate_name("golem-rust") {
241        Ok(FoundCrate::Itself) => syn::Ident::new("crate", Span::call_site()),
242        Ok(FoundCrate::Name(name)) => syn::Ident::new(&name, Span::call_site()),
243        Err(_) => syn::Ident::new("golem_rust", Span::call_site()),
244    }
245}