diff --git a/crates/cgp-extra-macro-lib/src/entrypoints/cgp_dispatch.rs b/crates/cgp-extra-macro-lib/src/entrypoints/cgp_dispatch.rs index 5f5d7cde..a830dbc4 100644 --- a/crates/cgp-extra-macro-lib/src/entrypoints/cgp_dispatch.rs +++ b/crates/cgp-extra-macro-lib/src/entrypoints/cgp_dispatch.rs @@ -6,8 +6,8 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::{ - parse2, FnArg, Ident, ImplItem, ImplItemFn, ItemTrait, Lifetime, Pat, PatIdent, ReturnType, - TraitItemFn, Type, Visibility, + parse2, FnArg, GenericParam, Ident, ImplItem, ImplItemFn, ItemTrait, Lifetime, Pat, PatIdent, + ReturnType, TraitItemFn, Type, Visibility, }; use crate::utils::to_camel_case_str; @@ -71,6 +71,18 @@ fn derive_blanket_impl(item_trait: &ItemTrait) -> syn::Result { method_ident.span(), ); + for generic_param in signature.generics.params.iter() { + match generic_param { + GenericParam::Lifetime(_) => {} + _ => { + return Err(syn::Error::new( + generic_param.span(), + "Dispatch trait methods cannot contain non-lifetime generic parameters due to the lack of quantified constraints in Rust", + )); + } + } + } + let mut args = signature.inputs.iter_mut(); let receiver = if let Some(FnArg::Receiver(receiver)) = args.next() { @@ -281,6 +293,7 @@ fn derive_method_computer( generics .params .extend(signature.generics.params.iter().cloned()); + if let Some(method_where_clause) = &signature.generics.where_clause { generics .make_where_clause() @@ -347,16 +360,17 @@ fn derive_method_computer( for (i, arg) in args.enumerate() { if let FnArg::Typed(pat_type) = arg { - let mut arg_type = pat_type.ty.as_ref().clone(); - if let Type::Reference(arg_type) = &mut arg_type { + arg_idents.push(Ident::new(&format!("arg_{}", i), pat_type.span())); + + let arg_type = pat_type.ty.as_mut(); + if let Type::Reference(arg_type) = arg_type { if arg_type.lifetime.is_none() { use_extra_life = true; arg_type.lifetime = Some(extra_life.clone()); } } - arg_idents.push(Ident::new(&format!("arg_{}", i), pat_type.span())); - arg_types.push(pat_type.ty.as_ref().clone()); + arg_types.push(arg_type); } else { return Err(syn::Error::new( arg.span(), @@ -399,6 +413,22 @@ fn derive_method_computer( method_ident.span(), ); + let method_generics = { + let method_generics = method + .sig + .generics + .params + .iter() + .filter(|param| !matches!(param, syn::GenericParam::Lifetime(_))) + .collect::>(); + + if method_generics.is_empty() { + TokenStream::new() + } else { + quote! { ::< #method_generics > } + } + }; + let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(quote! { @@ -409,7 +439,7 @@ fn derive_method_computer( ) #return_type #where_clause { - #context_ident. #method_ident( #arg_idents ) #dot_await + #context_ident. #method_ident #method_generics ( #arg_idents ) #dot_await } }) } diff --git a/crates/cgp-handler/src/providers/return_input.rs b/crates/cgp-handler/src/providers/return_input.rs index e59f4b39..ab3407fa 100644 --- a/crates/cgp-handler/src/providers/return_input.rs +++ b/crates/cgp-handler/src/providers/return_input.rs @@ -1,7 +1,8 @@ use cgp_core::prelude::*; use crate::{ - Computer, ComputerComponent, Handler, HandlerComponent, TryComputer, TryComputerComponent, + AsyncComputer, AsyncComputerComponent, Computer, ComputerComponent, Handler, HandlerComponent, + TryComputer, TryComputerComponent, }; pub struct ReturnInput; @@ -31,6 +32,19 @@ where } } +#[cgp_provider] +impl AsyncComputer for ReturnInput { + type Output = Input; + + async fn compute_async( + _context: &Context, + _code: PhantomData, + input: Input, + ) -> Self::Output { + input + } +} + #[cgp_provider] impl Handler for ReturnInput where diff --git a/crates/cgp-tests/src/tests/mod.rs b/crates/cgp-tests/src/tests/mod.rs index db2deb4d..7b7a9b46 100644 --- a/crates/cgp-tests/src/tests/mod.rs +++ b/crates/cgp-tests/src/tests/mod.rs @@ -8,7 +8,6 @@ pub mod delegate_and_check_components; pub mod delegate_components; pub mod extractor; pub mod getter; -pub mod handler; pub mod has_field; pub mod has_fields; pub mod monad; diff --git a/crates/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs b/crates/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs new file mode 100644 index 00000000..d6987e8e --- /dev/null +++ b/crates/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs @@ -0,0 +1,42 @@ +use core::fmt::Display; + +use cgp::prelude::*; +use futures::executor::block_on; + +use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; + +#[cgp_dispatch] +#[async_trait] +pub trait CanCall { + async fn call_a(&self, _a: u64, _b: &T) -> String; + + fn call_b(self, _a: u64, b: &mut T) -> &str; +} + +impl CanCall for Foo { + async fn call_a(&self, _a: u64, b: &T) -> String { + format!("foo-{}", b) + } + + fn call_b(self, _a: u64, _b: &mut T) -> &str { + "foo" + } +} + +impl CanCall for Bar { + async fn call_a(&self, _a: u64, _b: &T) -> String { + "bar".to_owned() + } + + fn call_b(self, _a: u64, _b: &mut T) -> &str { + "bar" + } +} + +pub trait CheckCanCallFooBar: CanCall {} +impl CheckCanCallFooBar for FooBar {} + +#[test] +fn test_call_self_only() { + assert_eq!(block_on(FooBar::Foo(Foo).call_a(42, &"extra")), "foo-extra"); +} diff --git a/crates/cgp-tests/tests/dispatcher_macro_tests/generics.rs b/crates/cgp-tests/tests/dispatcher_macro_tests/generics.rs new file mode 100644 index 00000000..616d2297 --- /dev/null +++ b/crates/cgp-tests/tests/dispatcher_macro_tests/generics.rs @@ -0,0 +1,40 @@ +use core::fmt::Display; + +use cgp::prelude::*; + +use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; + +#[cgp_dispatch] +pub trait CanCall { + fn call_a(&self, _a: u64, _b: &T) -> String; + + fn call_b(self, _a: u64, b: &mut T) -> &str; +} + +impl CanCall for Foo { + fn call_a(&self, _a: u64, b: &T) -> String { + format!("foo-{}", b) + } + + fn call_b(self, _a: u64, _b: &mut T) -> &str { + "foo" + } +} + +impl CanCall for Bar { + fn call_a(&self, _a: u64, _b: &T) -> String { + "bar".to_owned() + } + + fn call_b(self, _a: u64, _b: &mut T) -> &str { + "bar" + } +} + +pub trait CheckCanCallFooBar: CanCall {} +impl CheckCanCallFooBar for FooBar {} + +#[test] +fn test_call_self_only() { + assert_eq!(FooBar::Foo(Foo).call_a(42, &"extra"), "foo-extra"); +} diff --git a/crates/cgp-tests/tests/dispatcher_macro_tests/mod.rs b/crates/cgp-tests/tests/dispatcher_macro_tests/mod.rs index 624414a9..ff3c7293 100644 --- a/crates/cgp-tests/tests/dispatcher_macro_tests/mod.rs +++ b/crates/cgp-tests/tests/dispatcher_macro_tests/mod.rs @@ -1,12 +1,15 @@ +pub mod async_generics; pub mod async_multi_args; pub mod async_multi_args_owned_self; pub mod async_multi_args_ref; pub mod async_self_mut_only; pub mod async_self_only; pub mod async_self_ref_only; +pub mod generics; pub mod multi_args; pub mod multi_args_owned_self; pub mod multi_args_ref; +pub mod multi_methods; pub mod self_mut_only; pub mod self_only; pub mod self_ref_only; diff --git a/crates/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs b/crates/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs index d697e20e..06e60838 100644 --- a/crates/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs +++ b/crates/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs @@ -4,7 +4,7 @@ use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; #[cgp_dispatch] pub trait CanCall { - fn call<'a>(self, _a: &'a mut u64, _b: bool) -> &'a str; + fn call(self, _a: &mut u64, _b: bool) -> &str; } impl CanCall for Foo { diff --git a/crates/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs b/crates/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs new file mode 100644 index 00000000..7b8c2728 --- /dev/null +++ b/crates/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs @@ -0,0 +1,48 @@ +use cgp::prelude::*; + +use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; + +#[cgp_dispatch] +pub trait CanCall { + fn call_a(&self, _a: u64, _b: bool) -> &str; + + fn call_b(&mut self, _a: u64, _b: bool) -> u64; + + fn call_c(self, _a: &u64, _b: bool) -> &str; +} + +impl CanCall for Foo { + fn call_a(&self, _a: u64, _b: bool) -> &str { + "foo" + } + + fn call_b(&mut self, _a: u64, _b: bool) -> u64 { + 123 + } + + fn call_c(self, _a: &u64, _b: bool) -> &str { + "foo" + } +} + +impl CanCall for Bar { + fn call_a(&self, _a: u64, _b: bool) -> &str { + "bar" + } + + fn call_b(&mut self, _a: u64, _b: bool) -> u64 { + 456 + } + + fn call_c(self, _a: &u64, _b: bool) -> &str { + "bar" + } +} + +pub trait CheckCanCallFooBar: CanCall {} +impl CheckCanCallFooBar for FooBar {} + +#[test] +fn test_call_self_only() { + assert_eq!(FooBar::Foo(Foo).call_a(42, true), "foo"); +} diff --git a/crates/cgp-tests/tests/handler.rs b/crates/cgp-tests/tests/handler.rs new file mode 100644 index 00000000..8a61d337 --- /dev/null +++ b/crates/cgp-tests/tests/handler.rs @@ -0,0 +1 @@ +pub mod handler_tests; diff --git a/crates/cgp-tests/src/tests/handler/computer_macro.rs b/crates/cgp-tests/tests/handler_tests/computer_macro.rs similarity index 96% rename from crates/cgp-tests/src/tests/handler/computer_macro.rs rename to crates/cgp-tests/tests/handler_tests/computer_macro.rs index 69bcc95e..e4a427c1 100644 --- a/crates/cgp-tests/src/tests/handler/computer_macro.rs +++ b/crates/cgp-tests/tests/handler_tests/computer_macro.rs @@ -120,3 +120,8 @@ fn test_computer_ref() { Ok("1".to_owned()) ); } + +#[cgp_computer] +pub fn add_generic>(a: T, b: T) -> T { + a + b +} diff --git a/crates/cgp-tests/src/tests/handler/handler_macro.rs b/crates/cgp-tests/tests/handler_tests/handler_macro.rs similarity index 100% rename from crates/cgp-tests/src/tests/handler/handler_macro.rs rename to crates/cgp-tests/tests/handler_tests/handler_macro.rs diff --git a/crates/cgp-tests/src/tests/handler/mod.rs b/crates/cgp-tests/tests/handler_tests/mod.rs similarity index 100% rename from crates/cgp-tests/src/tests/handler/mod.rs rename to crates/cgp-tests/tests/handler_tests/mod.rs diff --git a/crates/cgp-tests/src/tests/handler/pipe.rs b/crates/cgp-tests/tests/handler_tests/pipe.rs similarity index 100% rename from crates/cgp-tests/src/tests/handler/pipe.rs rename to crates/cgp-tests/tests/handler_tests/pipe.rs diff --git a/crates/cgp-tests/src/tests/handler/producer_macro.rs b/crates/cgp-tests/tests/handler_tests/producer_macro.rs similarity index 100% rename from crates/cgp-tests/src/tests/handler/producer_macro.rs rename to crates/cgp-tests/tests/handler_tests/producer_macro.rs