diff --git a/crates/cgp-macro-lib/src/check_components/derive.rs b/crates/cgp-macro-lib/src/check_components/derive.rs index bc1bbb16..28b70a2e 100644 --- a/crates/cgp-macro-lib/src/check_components/derive.rs +++ b/crates/cgp-macro-lib/src/check_components/derive.rs @@ -4,6 +4,7 @@ use syn::token::Comma; use syn::{ItemImpl, ItemTrait, Type, parse2}; use crate::check_components::override_span; +use crate::delegate_components::merge_generics; use crate::parse::{CheckComponents, CheckEntry}; pub fn derive_check_components(spec: &CheckComponents) -> syn::Result<(ItemTrait, Vec)> { @@ -27,6 +28,7 @@ pub fn derive_check_components(spec: &CheckComponents) -> syn::Result<(ItemTrait component_type, component_params, span, + generics: check_generics, } in spec.check_entries.entries.iter() { // Override the span of the context type so that any unsatisfied constraint @@ -35,8 +37,10 @@ pub fn derive_check_components(spec: &CheckComponents) -> syn::Result<(ItemTrait let component_param = component_params.as_ref().unwrap_or(&unit); + let generics = merge_generics(&check_generics.generics, &impl_generics.generics); + let item_impl: ItemImpl = parse2(quote! { - impl #impl_generics + impl #generics #trait_name < #component_type, #component_param > for #context_type #where_clause diff --git a/crates/cgp-macro-lib/src/delegate_components/mod.rs b/crates/cgp-macro-lib/src/delegate_components/mod.rs index 6a725623..828d2726 100644 --- a/crates/cgp-macro-lib/src/delegate_components/mod.rs +++ b/crates/cgp-macro-lib/src/delegate_components/mod.rs @@ -4,3 +4,4 @@ mod merge_generics; pub use define_struct::*; pub use impl_delegate::*; +pub use merge_generics::*; diff --git a/crates/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs b/crates/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs index 7a8fcd07..6f635434 100644 --- a/crates/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs +++ b/crates/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs @@ -30,12 +30,14 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result>(), None => vec![CheckEntry { component_type: component_type.clone(), component_params: None, span, + generics: ImplGenerics::default(), }], } }) diff --git a/crates/cgp-macro-lib/src/parse/check_components.rs b/crates/cgp-macro-lib/src/parse/check_components.rs index 887b3b46..cbfdee97 100644 --- a/crates/cgp-macro-lib/src/parse/check_components.rs +++ b/crates/cgp-macro-lib/src/parse/check_components.rs @@ -29,6 +29,7 @@ pub struct CheckEntry { pub component_type: Type, pub component_params: Option, pub span: Span, + pub generics: ImplGenerics, } struct ParseCheckEntries { @@ -155,14 +156,15 @@ impl Parse for ParseCheckEntries { vec![component_type] }; - let component_params: Vec = if input.peek(Colon) { + let component_params: Vec = if input.peek(Colon) { let _: Colon = input.parse()?; if input.peek(Bracket) { let content; bracketed!(content in input); - let types: Punctuated = Punctuated::parse_terminated(&content)?; + let types: Punctuated = + Punctuated::parse_terminated(&content)?; types.into_iter().collect() } else { vec![input.parse()?] @@ -181,21 +183,26 @@ impl Parse for ParseCheckEntries { component_type: component_type.clone(), component_params: None, span: component_type.span(), + generics: ImplGenerics::default(), }) } else { let component_params_count = component_params.len(); for component_param in component_params.iter() { + let component_param_type = &component_param.ty; + let component_param_generics = &component_param.generics; + let span = if component_types_count >= component_params_count { component_type.span() } else { - component_param.span() + component_param_type.span() }; entries.push(CheckEntry { component_type: component_type.clone(), - component_params: Some(component_param.clone()), + component_params: Some(component_param_type.clone()), span, + generics: component_param_generics.clone(), }) } } @@ -204,3 +211,22 @@ impl Parse for ParseCheckEntries { Ok(Self { entries }) } } + +pub struct TypeWithGenerics { + pub ty: Type, + pub generics: ImplGenerics, +} + +impl Parse for TypeWithGenerics { + fn parse(input: ParseStream) -> syn::Result { + let generics = if input.peek(Lt) { + input.parse()? + } else { + ImplGenerics::default() + }; + + let ty = input.parse()?; + + Ok(Self { ty, generics }) + } +} diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index a34c4d47..28cfcae3 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -8,6 +8,7 @@ use syn::punctuated::Punctuated; use syn::token::{At, Bracket, Colon, Comma, Gt, Lt, RArrow, Semi}; use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; +use crate::delegate_components::merge_generics; use crate::parse::{ComponentPaths, ImplGenerics, SimpleType, TypeGenerics}; pub struct DelegateComponents { @@ -193,26 +194,50 @@ impl Parse for DelegateEntry { let components_body; bracketed!(components_body in input); components_body.parse_terminated(DelegateKey::parse, Token![,])? - } else if input.peek(At) { - let _: At = input.parse()?; + } else { + let impl_generics: Option = if input.peek(Lt) { + Some(input.parse()?) + } else { + None + }; - let path: ComponentPaths = input.parse()?; + if input.peek(At) { + let _: At = input.parse()?; - let mut keys = Punctuated::new(); + let path: ComponentPaths = input.parse()?; - for path in path.paths { - let key = DelegateKey { - ty: path.path_type, - generics: path.generics, - }; + let mut keys = Punctuated::new(); - keys.push(key); - } + for path in path.paths { + let generics = if let Some(impl_generics) = &impl_generics { + ImplGenerics { + generics: merge_generics( + &impl_generics.generics, + &path.generics.generics, + ), + } + } else { + path.generics + }; - keys - } else { - let component: DelegateKey = input.parse()?; - Punctuated::from_iter(iter::once(component)) + let key = DelegateKey { + ty: path.path_type, + generics, + }; + + keys.push(key); + } + + keys + } else { + let mut key: DelegateKey = input.parse()?; + + if let Some(impl_generics) = impl_generics { + key.generics = impl_generics; + } + + Punctuated::from_iter(iter::once(key)) + } }; let mode = input.parse()?; diff --git a/crates/cgp-tests/tests/namespace_tests/mod.rs b/crates/cgp-tests/tests/namespace_tests/mod.rs index 149e8657..5bb49d53 100644 --- a/crates/cgp-tests/tests/namespace_tests/mod.rs +++ b/crates/cgp-tests/tests/namespace_tests/mod.rs @@ -1,3 +1,4 @@ +pub mod multi_param; pub mod namespace_macro; pub mod open; pub mod redirect; diff --git a/crates/cgp-tests/tests/namespace_tests/multi_param.rs b/crates/cgp-tests/tests/namespace_tests/multi_param.rs new file mode 100644 index 00000000..3397ef40 --- /dev/null +++ b/crates/cgp-tests/tests/namespace_tests/multi_param.rs @@ -0,0 +1,58 @@ +use cgp::prelude::*; + +#[cgp_component(FooProvider)] +#[use_namespace(@app.FooProviderComponent)] +pub trait Foo<'a, T, U> { + fn foo(&self, first: &'a T, second: U); +} + +#[cgp_impl(new DummyFoo)] +impl<'a, T, U> FooProvider<'a, T, U> { + fn foo(&self, _first: &'a T, _second: U) {} +} + +pub struct AppA; + +delegate_components! { + AppA { + open FooProviderComponent; + + @FooProviderComponent.String.u32: + DummyFoo, + @FooProviderComponent.bool.T: + DummyFoo, + } +} + +check_components! { + AppA { + FooProviderComponent: [ + <'a> (Life<'a>, String, u32), + <'a> (Life<'a>, bool, String), + ], + FooProviderComponent: + <'a> (Life<'a>, bool, bool), + } +} + +pub struct AppB; + +delegate_components! { + AppB { + namespace default; + + @app.FooProviderComponent.String.u64: + DummyFoo, + @app.FooProviderComponent.bool. T: + DummyFoo, + } +} + +check_components! { + <'a> AppB { + FooProviderComponent: [ + (Life<'a>, String, u64), + (Life<'a>, bool, String), + ], + } +}