use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{DeriveInput, ItemStruct, Meta};
use crate::codegen::{generate_compat_methods, generate_safe_methods, generate_schema_impl};
use crate::helpers::{
extract_doc_from_attrs, extract_helper_meta, parse_struct_meta, parse_tool_meta_tokens,
};
pub(crate) fn expand_tool_for_struct(
args: TokenStream2,
mut s: ItemStruct,
) -> Result<TokenStream2, syn::Error> {
let meta = parse_tool_meta_tokens(&args);
let struct_name = &s.ident;
let helper_meta = extract_helper_meta(&s.attrs);
let name = if !meta.name.is_empty() {
meta.name.clone()
} else if !helper_meta.name.is_empty() {
helper_meta.name.clone()
} else {
crate::helpers::ident_to_snake_case(&s.ident.to_string())
};
let description = if !meta.description.is_empty() {
meta.description.clone()
} else if !helper_meta.description.is_empty() {
helper_meta.description.clone()
} else {
extract_doc_from_attrs(&s.attrs).unwrap_or_default()
};
s.attrs
.retain(|attr| !(attr.path().is_ident("tool") && matches!(&attr.meta, Meta::List(_))));
let schema_fn = generate_schema_impl(struct_name);
let compat_methods = generate_compat_methods(struct_name);
let safe_methods = generate_safe_methods(struct_name);
Ok(quote! {
#s
impl ::lellm_agent::ToolArgs for #struct_name {
const NAME: &'static str = #name;
const DESCRIPTION: &'static str = #description;
#schema_fn
}
#compat_methods
#safe_methods
})
}
pub(crate) fn generate_tool_for_struct(
input: &DeriveInput,
_data: &syn::DataStruct,
) -> TokenStream {
let struct_name = &input.ident;
let (name, description) = parse_struct_meta(&input.attrs, input);
let schema_fn = generate_schema_impl(struct_name);
let compat_methods = generate_compat_methods(struct_name);
let safe_methods = generate_safe_methods(struct_name);
let generated = quote! {
impl ::lellm_agent::ToolArgs for #struct_name {
const NAME: &'static str = #name;
const DESCRIPTION: &'static str = #description;
#schema_fn
}
#compat_methods
#safe_methods
};
TokenStream::from(generated)
}