fix(macros): respect local feature in #[prompt] macro — omit + Send bound (#803)
* refactor(prompt): update return type handling * fix(prompt): add omit send and test
This commit is contained in:
parent
c99903a67a
commit
6603c1ff15
1 changed files with 50 additions and 3 deletions
|
|
@ -20,6 +20,9 @@ pub struct PromptAttribute {
|
|||
pub icons: Option<Expr>,
|
||||
/// Optional metadata for the prompt
|
||||
pub meta: Option<Expr>,
|
||||
/// When true, the generated future will not require `Send`. Useful for `!Send` handlers
|
||||
/// (e.g. single-threaded database connections). Also enabled globally by the `local` crate feature.
|
||||
pub local: bool,
|
||||
}
|
||||
|
||||
pub struct ResolvedPromptAttribute {
|
||||
|
|
@ -78,6 +81,7 @@ pub fn prompt(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream>
|
|||
};
|
||||
let mut fn_item = syn::parse2::<ImplItemFn>(input.clone())?;
|
||||
let fn_ident = &fn_item.sig.ident;
|
||||
let omit_send = cfg!(feature = "local") || attribute.local;
|
||||
|
||||
let prompt_attr_fn_ident = format_ident!("{}_prompt_attr", fn_ident);
|
||||
|
||||
|
|
@ -123,7 +127,8 @@ pub fn prompt(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream>
|
|||
// Modify the input function for async support (same as tool macro)
|
||||
if fn_item.sig.asyncness.is_some() {
|
||||
// 1. remove asyncness from sig
|
||||
// 2. make return type: `futures::future::BoxFuture<'_, #ReturnType>`
|
||||
// 2. make return type: `std::pin::Pin<Box<dyn std::future::Future<Output = #ReturnType> + Send + '_>>`
|
||||
// (omit `+ Send` when the `local` crate feature is active or `#[prompt(local)]` is used)
|
||||
// 3. make body: { Box::pin(async move { #body }) }
|
||||
let new_output = syn::parse2::<ReturnType>({
|
||||
let mut lt = quote! { 'static };
|
||||
|
|
@ -138,10 +143,18 @@ pub fn prompt(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream>
|
|||
}
|
||||
match &fn_item.sig.output {
|
||||
syn::ReturnType::Default => {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
|
||||
if omit_send {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + #lt>> }
|
||||
} else {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
|
||||
}
|
||||
}
|
||||
syn::ReturnType::Type(_, ty) => {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
|
||||
if omit_send {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + #lt>> }
|
||||
} else {
|
||||
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
|
@ -226,4 +239,38 @@ mod test {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_prompt_default_send_behavior() -> syn::Result<()> {
|
||||
let attr = quote! {};
|
||||
let input = quote! {
|
||||
async fn test_prompt_default_send(&self) -> String {
|
||||
"ok".to_string()
|
||||
}
|
||||
};
|
||||
let result = prompt(attr, input)?;
|
||||
|
||||
let result_str = result.to_string();
|
||||
if cfg!(feature = "local") {
|
||||
assert!(!result_str.contains("+ Send +"));
|
||||
} else {
|
||||
assert!(result_str.contains("+ Send +"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_prompt_local_omits_send() -> syn::Result<()> {
|
||||
let attr = quote! { local };
|
||||
let input = quote! {
|
||||
async fn test_prompt_local_no_send(&self) -> String {
|
||||
"ok".to_string()
|
||||
}
|
||||
};
|
||||
let result = prompt(attr, input)?;
|
||||
|
||||
let result_str = result.to_string();
|
||||
assert!(!result_str.contains("+ Send +"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue