From 384c81d40926b7b4377ffe15736ba51b48691512 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 27 Jun 2026 13:12:38 +0200 Subject: [PATCH 01/10] Refactor namespace macros --- .../src/components/async_computer.rs | 2 + .../cgp-handler/src/components/computer.rs | 2 + .../cgp-handler/src/components/handler.rs | 2 + .../cgp-handler/src/components/produce.rs | 1 + .../cgp-handler/src/components/try_compute.rs | 2 + .../src/types/namespace/table.rs | 55 +++++++++++-------- .../namespace_macro/extended_namespace.rs | 2 +- 7 files changed, 41 insertions(+), 25 deletions(-) diff --git a/crates/extra/cgp-handler/src/components/async_computer.rs b/crates/extra/cgp-handler/src/components/async_computer.rs index 3e58a4f4..09b28e16 100644 --- a/crates/extra/cgp-handler/src/components/async_computer.rs +++ b/crates/extra/cgp-handler/src/components/async_computer.rs @@ -13,6 +13,7 @@ use crate::UseInputDelegate; UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanComputeAsync { type Output; @@ -27,6 +28,7 @@ pub trait CanComputeAsync { UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanComputeAsyncRef { type Output; diff --git a/crates/extra/cgp-handler/src/components/computer.rs b/crates/extra/cgp-handler/src/components/computer.rs index 8d971f9f..084c9d99 100644 --- a/crates/extra/cgp-handler/src/components/computer.rs +++ b/crates/extra/cgp-handler/src/components/computer.rs @@ -12,6 +12,7 @@ use crate::UseInputDelegate; UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanCompute { type Output; @@ -25,6 +26,7 @@ pub trait CanCompute { UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanComputeRef { type Output; diff --git a/crates/extra/cgp-handler/src/components/handler.rs b/crates/extra/cgp-handler/src/components/handler.rs index 098815e9..b9aa52bf 100644 --- a/crates/extra/cgp-handler/src/components/handler.rs +++ b/crates/extra/cgp-handler/src/components/handler.rs @@ -13,6 +13,7 @@ use crate::UseInputDelegate; UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanHandle: HasErrorType { type Output; @@ -31,6 +32,7 @@ pub trait CanHandle: HasErrorType { UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanHandleRef: HasErrorType { type Output; diff --git a/crates/extra/cgp-handler/src/components/produce.rs b/crates/extra/cgp-handler/src/components/produce.rs index 4dc88db1..3f9df06f 100644 --- a/crates/extra/cgp-handler/src/components/produce.rs +++ b/crates/extra/cgp-handler/src/components/produce.rs @@ -7,6 +7,7 @@ use cgp::prelude::*; provider: Producer, derive_delegate: UseDelegate, }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanProduce { type Output; diff --git a/crates/extra/cgp-handler/src/components/try_compute.rs b/crates/extra/cgp-handler/src/components/try_compute.rs index 75b970b6..3e0daa98 100644 --- a/crates/extra/cgp-handler/src/components/try_compute.rs +++ b/crates/extra/cgp-handler/src/components/try_compute.rs @@ -12,6 +12,7 @@ use crate::UseInputDelegate; UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanTryCompute: HasErrorType { type Output; @@ -29,6 +30,7 @@ pub trait CanTryCompute: HasErrorType { UseInputDelegate, ], }] +#[prefix(@cgp.extra.handler in DefaultNamespace)] pub trait CanTryComputeRef: HasErrorType { type Output; diff --git a/crates/macros/cgp-macro-core/src/types/namespace/table.rs b/crates/macros/cgp-macro-core/src/types/namespace/table.rs index 534dd652..7a1f4580 100644 --- a/crates/macros/cgp-macro-core/src/types/namespace/table.rs +++ b/crates/macros/cgp-macro-core/src/types/namespace/table.rs @@ -1,14 +1,14 @@ use syn::parse::{Parse, ParseStream}; use syn::token::Colon; -use syn::{Error, Ident, ItemImpl, ItemStruct, ItemTrait, Type, braced}; +use syn::{Ident, ItemImpl, ItemStruct, ItemTrait, Type, braced}; -use crate::parse_internal; +use crate::functions::parse_internal; use crate::traits::ParseOptionalKeyword; use crate::types::delegate_component::{ DelegateEntries, EvalDelegateEntries, EvalDelegateEntry, EvalForEntry, }; use crate::types::generics::ImplGenerics; -use crate::types::ident::{IdentWithTypeGenerics, PathWithTypeArgs}; +use crate::types::ident::{IdentWithTypeArgs, PathWithTypeArgs}; use crate::types::keyword::Keyword; use crate::types::keywords::New; use crate::types::namespace::{EvaluatedNamespaceTable, InheritNamespaceStatement}; @@ -16,7 +16,7 @@ use crate::types::namespace::{EvaluatedNamespaceTable, InheritNamespaceStatement pub struct NamespaceTable { pub impl_generics: ImplGenerics, pub new: Option>, - pub namespace: IdentWithTypeGenerics, + pub namespace: IdentWithTypeArgs, pub parent_namespace: Option<(Colon, PathWithTypeArgs)>, pub entries: DelegateEntries, } @@ -55,11 +55,11 @@ impl Parse for NamespaceTable { impl NamespaceTable { pub fn build_namespace_trait(&self) -> syn::Result { - let namespace_ident = &self.namespace.ident; - let mut namespace_generics = self.namespace.type_generics.clone(); - namespace_generics.params.push(parse_internal!(__Table__)); + let mut namespace = self.namespace.clone(); + + namespace.type_args.args.push(parse_internal!(__Table__)); - let namespace_trait: Type = parse_internal!( #namespace_ident #namespace_generics ); + let namespace_trait: Type = parse_internal!( #namespace ); Ok(namespace_trait) } @@ -102,21 +102,12 @@ impl NamespaceTable { Ok(item_impls) } - pub fn build_parent_namespace_impl(&self) -> syn::Result> { - let Some((_, parent_namespace)) = &self.parent_namespace else { - return Ok(None); - }; - + pub fn build_namespace_struct(&self) -> syn::Result> { if self.new.is_none() { - return Err(Error::new( - parent_namespace.ident().span(), - "parent namespace can only be specified with `new` namespaces", - )); + return Ok(None); } - let namespace_ident = self.namespace.ident.clone(); - - let table_type: Type = parse_internal!(__Table__); + let namespace_ident = &self.namespace.ident; let namespace_struct_ident = Ident::new( &format!("__{}Components", namespace_ident), @@ -127,6 +118,23 @@ impl NamespaceTable { pub struct #namespace_struct_ident; }; + Ok(Some(namespace_struct)) + } + + pub fn build_parent_namespace_impl(&self) -> syn::Result> { + let Some((_, parent_namespace)) = &self.parent_namespace else { + return Ok(None); + }; + + let namespace_ident = self.namespace.ident.clone(); + + let table_type: Type = parse_internal!(__Table__); + + let namespace_struct_ident = Ident::new( + &format!("__{}Components", namespace_ident), + namespace_ident.span(), + ); + let for_entry = InheritNamespaceStatement { namespace: parent_namespace.clone(), local_table_ident: namespace_struct_ident, @@ -142,17 +150,16 @@ impl NamespaceTable { let item_impl = evaluated_entry.build_namespace_impl(&namespace_trait, &generics)?; - Ok(Some((namespace_struct, item_impl))) + Ok(Some(item_impl)) } pub fn eval(&self) -> syn::Result { - let mut item_struct = None; let item_trait = self.build_item_trait()?; let mut item_impls = self.build_item_impls()?; + let item_struct = self.build_namespace_struct()?; - if let Some((namespace_struct, item_impl)) = self.build_parent_namespace_impl()? { + if let Some(item_impl) = self.build_parent_namespace_impl()? { item_impls.insert(0, item_impl); - item_struct = Some(namespace_struct); } Ok(EvaluatedNamespaceTable { diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs index 7d067728..8c4f84cc 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs @@ -19,7 +19,7 @@ snapshot_delegate_components! { ErrorWrapperComponent, }: RaiseFrom, - TryComputerComponent: + @cgp.extra.handler.TryComputerComponent: Foo, } } From f9f8b9ab0d06f99339edeb36e2e307874f664845 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 27 Jun 2026 13:49:10 +0200 Subject: [PATCH 02/10] Add grouped paths --- .../src/types/path/path_head.rs | 39 ++++++++++++++++--- crates/macros/cgp-macro-lib/src/parse/path.rs | 18 ++++++++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/path/path_head.rs b/crates/macros/cgp-macro-core/src/types/path/path_head.rs index a0ce9ea6..bf4abe8f 100644 --- a/crates/macros/cgp-macro-core/src/types/path/path_head.rs +++ b/crates/macros/cgp-macro-core/src/types/path/path_head.rs @@ -1,7 +1,7 @@ -use syn::braced; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::token::{Brace, Comma, Dot}; +use syn::token::{Brace, Bracket, Comma, Dot}; +use syn::{braced, bracketed}; use crate::types::generics::ImplGenerics; use crate::types::path::{PathElement, UniPath}; @@ -9,7 +9,8 @@ use crate::types::path::{PathElement, UniPath}; #[derive(Debug, Clone)] pub enum PathHead { Type(ImplGenerics, Box, Box), - Group(Punctuated), + Nested(Punctuated), + Group(Punctuated, Box), End, } @@ -29,10 +30,24 @@ impl PathHead { out_paths } - Self::Group(path_heads) => path_heads + Self::Nested(path_heads) => path_heads .iter() .flat_map(|path| path.into_paths()) .collect(), + Self::Group(path_elements, tail) => { + let tail_paths = tail.into_paths(); + let mut out_paths = Vec::new(); + + for path_element in path_elements { + for (tail_generics, tail_path) in &tail_paths { + let mut path = tail_path.clone(); + path.elements.insert(0, path_element.clone()); + out_paths.push((tail_generics.clone(), path)); + } + } + + out_paths + } Self::End => { vec![(ImplGenerics::default(), UniPath::default())] } @@ -50,7 +65,21 @@ impl Parse for PathHead { let group = Punctuated::parse_terminated(&body)?; - Ok(Self::Group(group)) + Ok(Self::Nested(group)) + } else if input.peek(Bracket) { + let body; + bracketed!(body in input); + + let group = Punctuated::parse_terminated(&body)?; + + let rest_path = if input.peek(Dot) { + let _: Dot = input.parse()?; + Box::new(Self::parse(input)?) + } else { + Box::new(Self::End) + }; + + Ok(Self::Group(group, rest_path)) } else { let generics = input.parse()?; diff --git a/crates/macros/cgp-macro-lib/src/parse/path.rs b/crates/macros/cgp-macro-lib/src/parse/path.rs index ccf5d4d5..7655092a 100644 --- a/crates/macros/cgp-macro-lib/src/parse/path.rs +++ b/crates/macros/cgp-macro-lib/src/parse/path.rs @@ -34,6 +34,7 @@ impl Parse for ComponentPaths { } } +#[derive(Clone)] pub struct ComponentPath { pub path_type: Path, pub generics: ImplGenerics, @@ -46,7 +47,22 @@ pub fn path_head_to_prefix(path_head: &PathHead) -> Vec paths.iter().flat_map(path_head_to_prefix).collect(), + PathHead::Group(path_elements, rest) => { + let rest_types = path_head_to_prefix(rest); + let mut out = Vec::new(); + + for path_element in path_elements { + let paths = prepend_path( + path_element.to_token_stream(), + Default::default(), + rest_types.clone(), + ); + out.extend(paths); + } + + out + } + PathHead::Nested(paths) => paths.iter().flat_map(path_head_to_prefix).collect(), PathHead::End => { vec![ComponentPath { path_type: quote! { __Wildcard__ }, From 913f4a2f07fc7615eb22ba642c801588290c6cb0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 27 Jun 2026 13:57:53 +0200 Subject: [PATCH 03/10] Support generics in path element groups --- .../cgp-macro-core/src/types/path/mod.rs | 2 ++ .../types/path/path_element_with_generics.rs | 18 +++++++++++++ .../src/types/path/path_head.rs | 26 +++++++++++-------- crates/macros/cgp-macro-lib/src/parse/path.rs | 12 ++++++--- 4 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 crates/macros/cgp-macro-core/src/types/path/path_element_with_generics.rs diff --git a/crates/macros/cgp-macro-core/src/types/path/mod.rs b/crates/macros/cgp-macro-core/src/types/path/mod.rs index 26f81c79..b41628e3 100644 --- a/crates/macros/cgp-macro-core/src/types/path/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/path/mod.rs @@ -1,4 +1,5 @@ mod path_element; +mod path_element_with_generics; mod path_head; mod path_head_or_type; mod prefix; @@ -6,6 +7,7 @@ mod unipath; mod unipath_or_type; pub use path_element::*; +pub use path_element_with_generics::*; pub use path_head::*; pub use path_head_or_type::*; pub use prefix::*; diff --git a/crates/macros/cgp-macro-core/src/types/path/path_element_with_generics.rs b/crates/macros/cgp-macro-core/src/types/path/path_element_with_generics.rs new file mode 100644 index 00000000..88a1546d --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/path/path_element_with_generics.rs @@ -0,0 +1,18 @@ +use syn::parse::{Parse, ParseStream}; + +use crate::types::generics::ImplGenerics; +use crate::types::path::PathElement; + +#[derive(Debug, Clone)] +pub struct PathElementWithGenerics { + pub generics: ImplGenerics, + pub element: PathElement, +} + +impl Parse for PathElementWithGenerics { + fn parse(input: ParseStream) -> syn::Result { + let generics = input.parse()?; + let element = input.parse()?; + Ok(Self { generics, element }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/path/path_head.rs b/crates/macros/cgp-macro-core/src/types/path/path_head.rs index bf4abe8f..ac01e705 100644 --- a/crates/macros/cgp-macro-core/src/types/path/path_head.rs +++ b/crates/macros/cgp-macro-core/src/types/path/path_head.rs @@ -4,27 +4,30 @@ use syn::token::{Brace, Bracket, Comma, Dot}; use syn::{braced, bracketed}; use crate::types::generics::ImplGenerics; -use crate::types::path::{PathElement, UniPath}; +use crate::types::path::{PathElementWithGenerics, UniPath}; #[derive(Debug, Clone)] pub enum PathHead { - Type(ImplGenerics, Box, Box), + Type(Box, Box), Nested(Punctuated), - Group(Punctuated, Box), + Group(Punctuated, Box), End, } impl PathHead { pub fn into_paths(&self) -> Vec<(ImplGenerics, UniPath)> { match self { - Self::Type(generics, path_element, tail) => { + Self::Type(path_element, tail) => { + let generics = &path_element.generics; + let element = &path_element.element; + let tail_paths = tail.into_paths(); let mut out_paths = Vec::new(); for (tail_generics, mut tail_path) in tail_paths { let mut generics = generics.clone(); generics.params.extend(tail_generics.params.iter().cloned()); - tail_path.elements.insert(0, path_element.as_ref().clone()); + tail_path.elements.insert(0, element.clone()); out_paths.push((generics, tail_path)) } @@ -40,9 +43,12 @@ impl PathHead { for path_element in path_elements { for (tail_generics, tail_path) in &tail_paths { + let mut generics = path_element.generics.clone(); + generics.params.extend(tail_generics.params.iter().cloned()); + let mut path = tail_path.clone(); - path.elements.insert(0, path_element.clone()); - out_paths.push((tail_generics.clone(), path)); + path.elements.insert(0, path_element.element.clone()); + out_paths.push((generics, path)); } } @@ -81,9 +87,7 @@ impl Parse for PathHead { Ok(Self::Group(group, rest_path)) } else { - let generics = input.parse()?; - - let path_type: PathElement = input.parse()?; + let path_element = input.parse()?; let rest_path = if input.peek(Dot) { let _: Dot = input.parse()?; @@ -92,7 +96,7 @@ impl Parse for PathHead { Box::new(Self::End) }; - Ok(Self::Type(generics, Box::new(path_type), rest_path)) + Ok(Self::Type(Box::new(path_element), rest_path)) } } } diff --git a/crates/macros/cgp-macro-lib/src/parse/path.rs b/crates/macros/cgp-macro-lib/src/parse/path.rs index 7655092a..0f2a921f 100644 --- a/crates/macros/cgp-macro-lib/src/parse/path.rs +++ b/crates/macros/cgp-macro-lib/src/parse/path.rs @@ -42,10 +42,14 @@ pub struct ComponentPath { pub fn path_head_to_prefix(path_head: &PathHead) -> Vec> { match path_head { - PathHead::Type(generics, path_type, rest) => { + PathHead::Type(path_element, rest) => { let rest_types = path_head_to_prefix(rest); - prepend_path(path_type.to_token_stream(), generics.clone(), rest_types) + prepend_path( + path_element.element.to_token_stream(), + path_element.generics.clone(), + rest_types, + ) } PathHead::Group(path_elements, rest) => { let rest_types = path_head_to_prefix(rest); @@ -53,8 +57,8 @@ pub fn path_head_to_prefix(path_head: &PathHead) -> Vec Date: Sat, 27 Jun 2026 14:38:02 +0200 Subject: [PATCH 04/10] Add grouped path tests --- crates/macros/cgp-macro-core/src/exports.rs | 2 + .../src/functions/is_provider_params.rs | 3 +- .../types/cgp_provider/provider_impl_args.rs | 3 +- .../src/types/check_components/table.rs | 3 +- .../cgp-macro-core/src/types/empty_struct.rs | 4 +- .../cgp-tests/src/namespaces/default_impls.rs | 1 + .../cgp-tests/tests/namespace_tests/group.rs | 195 ++++++++++++++++++ .../cgp-tests/tests/namespace_tests/mod.rs | 1 + .../tests/namespace_tests/multi_param.rs | 139 +------------ .../namespace_tests/namespace_macro/basic.rs | 2 +- .../namespace_macro/extended_namespace.rs | 85 +++++++- .../namespace_macro/multi_namespace.rs | 3 +- .../namespace_macro/symbol_path.rs | 2 +- .../namespace_macro/type_path.rs | 2 +- .../cgp-tests/tests/namespace_tests/open.rs | 1 - 15 files changed, 303 insertions(+), 143 deletions(-) create mode 100644 crates/tests/cgp-tests/tests/namespace_tests/group.rs diff --git a/crates/macros/cgp-macro-core/src/exports.rs b/crates/macros/cgp-macro-core/src/exports.rs index 212d2a55..1bd2ae8e 100644 --- a/crates/macros/cgp-macro-core/src/exports.rs +++ b/crates/macros/cgp-macro-core/src/exports.rs @@ -11,7 +11,9 @@ export_constructs! { RedirectLookup, DelegateComponent, IsProviderFor, + CanUseComponent, HasField, HasFieldMut, UseContext, + Life, } diff --git a/crates/macros/cgp-macro-core/src/functions/is_provider_params.rs b/crates/macros/cgp-macro-core/src/functions/is_provider_params.rs index a23dd8a1..1eeaaa13 100644 --- a/crates/macros/cgp-macro-core/src/functions/is_provider_params.rs +++ b/crates/macros/cgp-macro-core/src/functions/is_provider_params.rs @@ -2,6 +2,7 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{GenericParam, Generics, Type}; +use crate::exports::Life; use crate::parse_internal; use crate::types::generics::TypeGenerics; @@ -18,7 +19,7 @@ pub fn parse_is_provider_params(generics: &Generics) -> syn::Result { let life = &life_param.lifetime; - parse_internal! { Life<#life> } + parse_internal! { #Life<#life> } } GenericParam::Const(_) => { unimplemented!("const generic parameters are not yet supported in CGP traits") diff --git a/crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs b/crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs index 116c28a0..f8ed7f2b 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs @@ -5,6 +5,7 @@ use syn::spanned::Spanned; use syn::token::Comma; use syn::{Error, Lifetime, Type}; +use crate::exports::Life; use crate::types::ident::{TypeArg, TypeArgs}; pub struct ProviderImplArgs { @@ -68,7 +69,7 @@ impl ToTokens for ProviderImplArg { ty.to_tokens(tokens); } ProviderImplArg::Life(life) => { - tokens.extend(quote!(Life<#life>)); + tokens.extend(quote!(#Life<#life>)); } } } diff --git a/crates/macros/cgp-macro-core/src/types/check_components/table.rs b/crates/macros/cgp-macro-core/src/types/check_components/table.rs index a8dad6d4..16ea61f8 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/table.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/table.rs @@ -6,6 +6,7 @@ use syn::spanned::Spanned; use syn::token::{Comma, Lt, Pound, Where}; use syn::{Attribute, Ident, Item, ItemImpl, ItemTrait, Type, WhereClause, braced, parse2}; +use crate::exports::CanUseComponent; use crate::functions::merge_generics; use crate::parse_internal; use crate::types::check_components::{CheckEntries, EvaluatedCheckEntry, TypeWithGenerics}; @@ -46,7 +47,7 @@ impl CheckComponentsTable { } } else { parse_internal! { - trait #trait_name <__Component__, __Params__: ?Sized>: CanUseComponent<__Component__, __Params__> {} + trait #trait_name <__Component__, __Params__: ?Sized>: #CanUseComponent<__Component__, __Params__> {} } }; diff --git a/crates/macros/cgp-macro-core/src/types/empty_struct.rs b/crates/macros/cgp-macro-core/src/types/empty_struct.rs index 587cda32..caaf6aa1 100644 --- a/crates/macros/cgp-macro-core/src/types/empty_struct.rs +++ b/crates/macros/cgp-macro-core/src/types/empty_struct.rs @@ -3,6 +3,8 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{GenericParam, Generics, Ident, ItemStruct, Type, parse_quote}; +use crate::exports::Life; + pub struct EmptyStruct { pub ident: Ident, pub generics: Generics, @@ -41,7 +43,7 @@ impl ToTokens for EmptyStruct { life_param.bounds.clear(); let lifetime = &life_param.lifetime; - phantom_params.push(parse_quote!( Life<#lifetime> )); + phantom_params.push(parse_quote!( #Life<#lifetime> )); } _ => {} } diff --git a/crates/tests/cgp-tests/src/namespaces/default_impls.rs b/crates/tests/cgp-tests/src/namespaces/default_impls.rs index e6363ea7..d4cd123e 100644 --- a/crates/tests/cgp-tests/src/namespaces/default_impls.rs +++ b/crates/tests/cgp-tests/src/namespaces/default_impls.rs @@ -162,6 +162,7 @@ snapshot_cgp_namespace! { expand_default_show_components(output) { insta::assert_snapshot!(output, @" + pub struct __DefaultShowComponentsComponents; pub trait DefaultShowComponents<__Table__> { type Delegate; } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/group.rs b/crates/tests/cgp-tests/tests/namespace_tests/group.rs new file mode 100644 index 00000000..fa3157b1 --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespace_tests/group.rs @@ -0,0 +1,195 @@ +use cgp::prelude::{DefaultNamespace, cgp_component, cgp_impl, check_components}; +use cgp_macro_test_util::snapshot_delegate_components; + +#[cgp_component(FooProvider)] +#[prefix(@app in DefaultNamespace)] +pub trait Foo { + fn foo(&self, value: &T); +} + +#[cgp_component(BarProvider)] +#[prefix(@app in DefaultNamespace)] +pub trait Bar { + fn bar(&self, value: &T); +} + +pub struct DummyImpl; + +#[cgp_impl(DummyImpl)] +impl FooProvider { + fn foo(&self, _value: &T) {} +} + +#[cgp_impl(DummyImpl)] +impl BarProvider { + fn bar(&self, _value: &T) {} +} + +pub struct App; + +snapshot_delegate_components! { + delegate_components! { + App { + namespace DefaultNamespace; + + @app.[FooProviderComponent, BarProviderComponent].[u64, String]: + DummyImpl, + } + } + + expand_delegate_components(output) { + insta::assert_snapshot!(output, @" + impl<__Key__, __Value__> DelegateComponent<__Key__> for App + where + __Key__: DefaultNamespace, + { + type Delegate = __Value__; + } + impl< + __Key__, + __Value__, + __Context__, + __Params__, + > IsProviderFor<__Key__, __Context__, __Params__> for App + where + __Key__: DefaultNamespace, + __Value__: IsProviderFor<__Key__, __Context__, __Params__>, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + > for App { + type Delegate = DummyImpl; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + > for App + where + DummyImpl: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + > for App { + type Delegate = DummyImpl; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + > for App + where + DummyImpl: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + > for App { + type Delegate = DummyImpl; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + > for App + where + DummyImpl: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + > for App { + type Delegate = DummyImpl; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + > for App + where + DummyImpl: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>, + >, + __Context__, + __Params__, + >, + {} + ") + } +} + +check_components! { + App { + [FooProviderComponent, BarProviderComponent]: + [u64, String] + } +} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/mod.rs b/crates/tests/cgp-tests/tests/namespace_tests/mod.rs index 4745538f..60b2bc01 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/mod.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/mod.rs @@ -1,3 +1,4 @@ +pub mod group; pub mod multi_param; pub mod namespace; pub mod namespace_macro; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs b/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs index 436d082e..b89dd87d 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs @@ -1,138 +1,15 @@ use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_check_components, - snapshot_delegate_components, -}; +use cgp_macro_test_util::{snapshot_check_components, snapshot_delegate_components}; -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - #[prefix(@app in DefaultNamespace)] - pub trait Foo<'a, T, U> { - fn foo(&self, first: &'a T, second: U); - } - - expand_multi_param_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo<'a, T, U> { - fn foo(&self, first: &'a T, second: U); - } - impl<'a, __Context__, T, U> Foo<'a, T, U> for __Context__ - where - __Context__: FooProvider<'a, __Context__, T, U>, - { - fn foo(&self, first: &'a T, second: U) { - __Context__::foo(self, first, second) - } - } - pub trait FooProvider< - 'a, - __Context__, - T, - U, - >: IsProviderFor, T, U)> { - fn foo(__context__: &__Context__, first: &'a T, second: U); - } - impl<'a, __Provider__, __Context__, T, U> FooProvider<'a, __Context__, T, U> - for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, T, U)>, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<'a, __Context__, T, U>, - { - fn foo(__context__: &__Context__, first: &'a T, second: U) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__, first, second) - } - } - pub struct FooProviderComponent; - impl<'a, __Context__, T, U> FooProvider<'a, __Context__, T, U> for UseContext - where - __Context__: Foo<'a, T, U>, - { - fn foo(__context__: &__Context__, first: &'a T, second: U) { - __Context__::foo(__context__, first, second) - } - } - impl< - 'a, - __Context__, - T, - U, - > IsProviderFor, T, U)> for UseContext - where - __Context__: Foo<'a, T, U>, - {} - impl<'a, __Context__, T, U, __Components__, __Path__> FooProvider<'a, __Context__, T, U> - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: FooProvider<'a, __Context__, T, U>, - { - fn foo(__context__: &__Context__, first: &'a T, second: U) { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate::foo(__context__, first, second) - } - } - impl< - 'a, - __Context__, - T, - U, - __Components__, - __Path__, - > IsProviderFor, T, U)> - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: FooProvider<'a, __Context__, T, U>, - {} - impl<__Components__> DefaultNamespace<__Components__> for FooProviderComponent { - type Delegate = RedirectLookup< - __Components__, - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons, - >, - >; - } - ") - } +#[cgp_component(FooProvider)] +#[prefix(@app in DefaultNamespace)] +pub trait Foo<'a, T, U> { + fn foo(&self, first: &'a T, second: U); } -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl<'a, T, U> FooProvider<'a, T, U> { - fn foo(&self, _first: &'a T, _second: U) {} - } - - expand_multi_param_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<'a, __Context__, T, U> FooProvider<'a, __Context__, T, U> for DummyFoo { - fn foo(__context__: &__Context__, _first: &'a T, _second: U) {} - } - impl< - 'a, - __Context__, - T, - U, - > IsProviderFor, T, U)> for DummyFoo {} - pub struct DummyFoo; - ") - } +#[cgp_impl(new DummyFoo)] +impl<'a, T, U> FooProvider<'a, T, U> { + fn foo(&self, _first: &'a T, _second: U) {} } pub struct AppA; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs index 55f4813e..72f2f17c 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs @@ -1,4 +1,3 @@ -use cgp::prelude::*; use cgp_macro_test_util::{ snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, snapshot_delegate_components, @@ -92,6 +91,7 @@ snapshot_cgp_namespace! { expand_basic_my_namespace(output) { insta::assert_snapshot!(output, @" + pub struct __MyNamespaceComponents; pub trait MyNamespace<__Table__> { type Delegate; } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs index 8c4f84cc..9195e8c5 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs @@ -172,15 +172,94 @@ snapshot_delegate_components! { __Params__, >, {} - impl DelegateComponent for App { + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, + PathCons< + Symbol<5, Chars<'e', Chars<'x', Chars<'t', Chars<'r', Chars<'a', Nil>>>>>>, + PathCons< + Symbol< + 7, + Chars< + 'h', + Chars< + 'a', + Chars< + 'n', + Chars<'d', Chars<'l', Chars<'e', Chars<'r', Nil>>>>, + >, + >, + >, + >, + PathCons, + >, + >, + >, + > for App { type Delegate = Foo; } impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, + PathCons< + Symbol<5, Chars<'e', Chars<'x', Chars<'t', Chars<'r', Chars<'a', Nil>>>>>>, + PathCons< + Symbol< + 7, + Chars< + 'h', + Chars< + 'a', + Chars< + 'n', + Chars<'d', Chars<'l', Chars<'e', Chars<'r', Nil>>>>, + >, + >, + >, + >, + PathCons, + >, + >, + >, __Context__, __Params__, - > IsProviderFor for App + > for App where - Foo: IsProviderFor, + Foo: IsProviderFor< + PathCons< + Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, + PathCons< + Symbol< + 5, + Chars<'e', Chars<'x', Chars<'t', Chars<'r', Chars<'a', Nil>>>>>, + >, + PathCons< + Symbol< + 7, + Chars< + 'h', + Chars< + 'a', + Chars< + 'n', + Chars<'d', Chars<'l', Chars<'e', Chars<'r', Nil>>>>, + >, + >, + >, + >, + PathCons, + >, + >, + >, + __Context__, + __Params__, + >, {} ") } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs index 77d4e9ac..8e4904c3 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs @@ -1,4 +1,3 @@ -use cgp::prelude::*; use cgp_macro_test_util::{ snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, snapshot_delegate_components, @@ -94,6 +93,7 @@ snapshot_cgp_namespace! { expand_multi_ns_my_namespace(output) { insta::assert_snapshot!(output, @" + pub struct __MyNamespaceComponents; pub trait MyNamespace<__Table__> { type Delegate; } @@ -117,6 +117,7 @@ snapshot_cgp_namespace! { expand_multi_ns_other_namespace(output) { insta::assert_snapshot!(output, @" + pub struct __OtherNamespaceComponents; pub trait OtherNamespace<__Table__> { type Delegate; } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs index c398d71a..604d8464 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs @@ -1,4 +1,3 @@ -use cgp::prelude::*; use cgp_macro_test_util::{ snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, snapshot_delegate_components, @@ -92,6 +91,7 @@ snapshot_cgp_namespace! { expand_symbol_path_my_namespace(output) { insta::assert_snapshot!(output, @" + pub struct __MyNamespaceComponents; pub trait MyNamespace<__Table__> { type Delegate; } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs index 7704cc92..d432fcf5 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs @@ -1,4 +1,3 @@ -use cgp::prelude::*; use cgp_macro_test_util::{ snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, snapshot_delegate_components, @@ -94,6 +93,7 @@ snapshot_cgp_namespace! { expand_type_path_my_namespace(output) { insta::assert_snapshot!(output, @" + pub struct __MyNamespaceComponents; pub trait MyNamespace<__Table__> { type Delegate; } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/open.rs b/crates/tests/cgp-tests/tests/namespace_tests/open.rs index b7b08be2..61ffd096 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/open.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/open.rs @@ -1,4 +1,3 @@ -use cgp::prelude::*; use cgp_macro_test_util::{ snapshot_cgp_component, snapshot_cgp_impl, snapshot_check_components, snapshot_delegate_components, From 8248d9b8540fb5ce09d44a1a03f50b2261a04f8a Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 10:44:00 +0200 Subject: [PATCH 05/10] Add `Path!` macro and `IsDelegateKeyIn` trait --- crates/core/cgp-component/src/lib.rs | 8 +++----- .../core/cgp-component/src/traits/delegate_component.rs | 6 +++--- .../core/cgp-component/src/traits/is_delegate_key_in.rs | 5 +++++ crates/core/cgp-component/src/traits/mod.rs | 2 ++ crates/macros/cgp-macro-lib/src/entrypoints/mod.rs | 2 ++ crates/macros/cgp-macro-lib/src/entrypoints/path.rs | 9 +++++++++ crates/macros/cgp-macro/src/lib.rs | 8 ++++++++ 7 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 crates/core/cgp-component/src/traits/is_delegate_key_in.rs create mode 100644 crates/macros/cgp-macro-lib/src/entrypoints/path.rs diff --git a/crates/core/cgp-component/src/lib.rs b/crates/core/cgp-component/src/lib.rs index 9680af2e..b63ed46a 100644 --- a/crates/core/cgp-component/src/lib.rs +++ b/crates/core/cgp-component/src/lib.rs @@ -11,8 +11,6 @@ mod namespaces; mod providers; mod traits; -pub use namespaces::{DefaultImpls1, DefaultImpls2, DefaultNamespace}; -pub use providers::{ - RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, WithProvider, -}; -pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; +pub use namespaces::*; +pub use providers::*; +pub use traits::*; diff --git a/crates/core/cgp-component/src/traits/delegate_component.rs b/crates/core/cgp-component/src/traits/delegate_component.rs index 42334c35..fca84a03 100644 --- a/crates/core/cgp-component/src/traits/delegate_component.rs +++ b/crates/core/cgp-component/src/traits/delegate_component.rs @@ -40,9 +40,9 @@ ``` */ #[diagnostic::on_unimplemented( - message = "{Self} does not contain any DelegateComponent entry for {Name}", - note = "You might want to implement the provider trait for {Name} on {Self}" + message = "{Self} does not contain any DelegateComponent entry for {Key}", + note = "You might want to implement the provider trait for {Key} on {Self}" )] -pub trait DelegateComponent { +pub trait DelegateComponent { type Delegate; } diff --git a/crates/core/cgp-component/src/traits/is_delegate_key_in.rs b/crates/core/cgp-component/src/traits/is_delegate_key_in.rs new file mode 100644 index 00000000..42f347e4 --- /dev/null +++ b/crates/core/cgp-component/src/traits/is_delegate_key_in.rs @@ -0,0 +1,5 @@ +use crate::DelegateComponent; + +pub trait IsDelegateKeyIn {} + +impl IsDelegateKeyIn
for Key where Table: DelegateComponent {} diff --git a/crates/core/cgp-component/src/traits/mod.rs b/crates/core/cgp-component/src/traits/mod.rs index 1e5f1a04..d51a635d 100644 --- a/crates/core/cgp-component/src/traits/mod.rs +++ b/crates/core/cgp-component/src/traits/mod.rs @@ -1,7 +1,9 @@ mod can_use_component; mod delegate_component; +mod is_delegate_key_in; mod is_provider; pub use can_use_component::*; pub use delegate_component::DelegateComponent; +pub use is_delegate_key_in::*; pub use is_provider::*; diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/mod.rs b/crates/macros/cgp-macro-lib/src/entrypoints/mod.rs index ee117d9c..f3036c36 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/mod.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/mod.rs @@ -20,6 +20,7 @@ mod derive_build_field; mod derive_extract_field; mod derive_from_variant; mod derive_has_fields; +mod path; mod re_export_imports; mod replace_with; @@ -45,5 +46,6 @@ pub use derive_build_field::*; pub use derive_extract_field::*; pub use derive_from_variant::*; pub use derive_has_fields::*; +pub use path::*; pub use re_export_imports::*; pub use replace_with::*; diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/path.rs b/crates/macros/cgp-macro-lib/src/entrypoints/path.rs new file mode 100644 index 00000000..c69a3272 --- /dev/null +++ b/crates/macros/cgp-macro-lib/src/entrypoints/path.rs @@ -0,0 +1,9 @@ +use cgp_macro_core::types::path::UniPath; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse2; + +pub fn path(body: TokenStream) -> syn::Result { + let unipath: UniPath = parse2(body)?; + Ok(unipath.to_token_stream()) +} diff --git a/crates/macros/cgp-macro/src/lib.rs b/crates/macros/cgp-macro/src/lib.rs index 72ad7cfd..78ad0548 100644 --- a/crates/macros/cgp-macro/src/lib.rs +++ b/crates/macros/cgp-macro/src/lib.rs @@ -972,6 +972,14 @@ pub fn Sum(body: TokenStream) -> TokenStream { cgp_macro_lib::make_sum_type(body.into()).into() } +#[proc_macro] +#[allow(non_snake_case)] +pub fn Path(body: TokenStream) -> TokenStream { + cgp_macro_lib::path(body.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + #[proc_macro] pub fn product(body: TokenStream) -> TokenStream { cgp_macro_lib::make_product_expr(body.into()).into() From 9e8f26a4e5854ec8b5b094bb18309ebbd57bbcff Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 11:00:09 +0200 Subject: [PATCH 06/10] Make `derive_delegate` as normal attribute --- .../src/providers/use_delegate.rs | 6 ++---- .../cgp-error/src/traits/can_raise_error.rs | 6 ++---- .../cgp-error/src/traits/can_wrap_error.rs | 6 ++---- crates/core/cgp-type/src/traits/has_type.rs | 6 ++---- .../src/components/async_computer.rs | 20 ++++++------------- .../cgp-handler/src/components/computer.rs | 20 ++++++------------- .../cgp-handler/src/components/handler.rs | 20 ++++++------------- .../cgp-handler/src/components/produce.rs | 6 ++---- .../cgp-handler/src/components/try_compute.rs | 20 ++++++------------- crates/extra/cgp-run/src/lib.rs | 12 ++++------- .../attributes/cgp_component_attributes.rs | 13 +++++++++++- .../src/types/cgp_component/evaluated/item.rs | 2 +- .../src/types/cgp_component/item.rs | 7 ++++++- crates/macros/cgp-macro-test-util/README.md | 2 +- .../src/tests/use_delegate/getter.rs | 20 ++++++------------- 15 files changed, 64 insertions(+), 102 deletions(-) diff --git a/crates/core/cgp-component/src/providers/use_delegate.rs b/crates/core/cgp-component/src/providers/use_delegate.rs index bde0ef43..0dbf76bc 100644 --- a/crates/core/cgp-component/src/providers/use_delegate.rs +++ b/crates/core/cgp-component/src/providers/use_delegate.rs @@ -31,10 +31,8 @@ use core::marker::PhantomData; Given the following component definition: ```rust,ignore - #[cgp_component { - provider: ErrorRaiser, - derive_delegate: UseDelegate, - }] + #[cgp_component(ErrorRaiser)] + #[derive_delegate(UseDelegate)] pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } diff --git a/crates/core/cgp-error/src/traits/can_raise_error.rs b/crates/core/cgp-error/src/traits/can_raise_error.rs index 446fa844..0717eaa4 100644 --- a/crates/core/cgp-error/src/traits/can_raise_error.rs +++ b/crates/core/cgp-error/src/traits/can_raise_error.rs @@ -7,11 +7,9 @@ use crate::traits::has_error_type::HasErrorType; The `CanRaiseError` trait is used to raise any concrete error type into an abstract error provided by [`HasErrorType`]. */ -#[cgp_component { - provider: ErrorRaiser, - derive_delegate: UseDelegate, -}] +#[cgp_component(ErrorRaiser)] #[prefix(@cgp.core.error in DefaultNamespace)] +#[derive_delegate(UseDelegate)] pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } diff --git a/crates/core/cgp-error/src/traits/can_wrap_error.rs b/crates/core/cgp-error/src/traits/can_wrap_error.rs index 3e4d6e5f..745f6bef 100644 --- a/crates/core/cgp-error/src/traits/can_wrap_error.rs +++ b/crates/core/cgp-error/src/traits/can_wrap_error.rs @@ -3,11 +3,9 @@ use cgp_macro::cgp_component; use crate::traits::HasErrorType; -#[cgp_component { - provider: ErrorWrapper, - derive_delegate: UseDelegate, -}] +#[cgp_component(ErrorWrapper)] #[prefix(@cgp.core.error in DefaultNamespace)] +#[derive_delegate(UseDelegate)] pub trait CanWrapError: HasErrorType { fn wrap_error(error: Self::Error, detail: Detail) -> Self::Error; } diff --git a/crates/core/cgp-type/src/traits/has_type.rs b/crates/core/cgp-type/src/traits/has_type.rs index 9563218f..24070b4e 100644 --- a/crates/core/cgp-type/src/traits/has_type.rs +++ b/crates/core/cgp-type/src/traits/has_type.rs @@ -1,10 +1,8 @@ use cgp::macro_prelude::*; use cgp_macro::cgp_component; -#[cgp_component { - provider: TypeProvider, - derive_delegate: UseDelegate, -}] +#[cgp_component(TypeProvider)] +#[derive_delegate(UseDelegate)] pub trait HasType { type Type; } diff --git a/crates/extra/cgp-handler/src/components/async_computer.rs b/crates/extra/cgp-handler/src/components/async_computer.rs index 09b28e16..71badf60 100644 --- a/crates/extra/cgp-handler/src/components/async_computer.rs +++ b/crates/extra/cgp-handler/src/components/async_computer.rs @@ -6,14 +6,10 @@ use cgp::prelude::*; use crate::UseInputDelegate; #[async_trait] -#[cgp_component { - provider: AsyncComputer, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(AsyncComputer)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanComputeAsync { type Output; @@ -21,14 +17,10 @@ pub trait CanComputeAsync { } #[async_trait] -#[cgp_component { - provider: AsyncComputerRef, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(AsyncComputerRef)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanComputeAsyncRef { type Output; diff --git a/crates/extra/cgp-handler/src/components/computer.rs b/crates/extra/cgp-handler/src/components/computer.rs index 084c9d99..f533c9ff 100644 --- a/crates/extra/cgp-handler/src/components/computer.rs +++ b/crates/extra/cgp-handler/src/components/computer.rs @@ -5,28 +5,20 @@ use cgp::prelude::*; use crate::UseInputDelegate; -#[cgp_component { - provider: Computer, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(Computer)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanCompute { type Output; fn compute(&self, _code: PhantomData, input: Input) -> Self::Output; } -#[cgp_component { - provider: ComputerRef, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(ComputerRef)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanComputeRef { type Output; diff --git a/crates/extra/cgp-handler/src/components/handler.rs b/crates/extra/cgp-handler/src/components/handler.rs index b9aa52bf..02ff9676 100644 --- a/crates/extra/cgp-handler/src/components/handler.rs +++ b/crates/extra/cgp-handler/src/components/handler.rs @@ -6,14 +6,10 @@ use cgp::prelude::*; use crate::UseInputDelegate; #[async_trait] -#[cgp_component { - provider: Handler, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(Handler)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanHandle: HasErrorType { type Output; @@ -25,14 +21,10 @@ pub trait CanHandle: HasErrorType { } #[async_trait] -#[cgp_component { - provider: HandlerRef, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(HandlerRef)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanHandleRef: HasErrorType { type Output; diff --git a/crates/extra/cgp-handler/src/components/produce.rs b/crates/extra/cgp-handler/src/components/produce.rs index 3f9df06f..1cf21536 100644 --- a/crates/extra/cgp-handler/src/components/produce.rs +++ b/crates/extra/cgp-handler/src/components/produce.rs @@ -3,11 +3,9 @@ use core::marker::PhantomData; use cgp::component::UseDelegate; use cgp::prelude::*; -#[cgp_component { - provider: Producer, - derive_delegate: UseDelegate, -}] +#[cgp_component(Producer)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] pub trait CanProduce { type Output; diff --git a/crates/extra/cgp-handler/src/components/try_compute.rs b/crates/extra/cgp-handler/src/components/try_compute.rs index 3e0daa98..4e0a8304 100644 --- a/crates/extra/cgp-handler/src/components/try_compute.rs +++ b/crates/extra/cgp-handler/src/components/try_compute.rs @@ -5,14 +5,10 @@ use cgp::prelude::*; use crate::UseInputDelegate; -#[cgp_component { - provider: TryComputer, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(TryComputer)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanTryCompute: HasErrorType { type Output; @@ -23,14 +19,10 @@ pub trait CanTryCompute: HasErrorType { ) -> Result; } -#[cgp_component { - provider: TryComputerRef, - derive_delegate: [ - UseDelegate, - UseInputDelegate, - ], -}] +#[cgp_component(TryComputerRef)] #[prefix(@cgp.extra.handler in DefaultNamespace)] +#[derive_delegate(UseDelegate)] +#[derive_delegate(UseInputDelegate)] pub trait CanTryComputeRef: HasErrorType { type Output; diff --git a/crates/extra/cgp-run/src/lib.rs b/crates/extra/cgp-run/src/lib.rs index 26856aa8..6330faf4 100644 --- a/crates/extra/cgp-run/src/lib.rs +++ b/crates/extra/cgp-run/src/lib.rs @@ -5,20 +5,16 @@ use core::marker::PhantomData; use cgp::prelude::*; -#[cgp_component { - provider: Runner, - derive_delegate: UseDelegate, -}] +#[cgp_component(Runner)] #[async_trait] +#[derive_delegate(UseDelegate)] pub trait CanRun: HasErrorType { async fn run(&self, _code: PhantomData) -> Result<(), Self::Error>; } -#[cgp_component { - provider: SendRunner, - derive_delegate: UseDelegate, -}] +#[cgp_component(SendRunner)] #[async_trait] +#[derive_delegate(UseDelegate)] pub trait CanSendRun: HasErrorType { fn send_run( &self, diff --git a/crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs b/crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs index eba8cca2..ddf61e5f 100644 --- a/crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs @@ -5,13 +5,17 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Attribute, ItemTrait, TypeParamBound}; -use crate::types::attributes::{PrefixAttribute, UseTypeAttribute, UseTypeAttributes}; +use crate::types::attributes::{ + DeriveDelegateAttribute, DeriveDelegateAttributes, PrefixAttribute, UseTypeAttribute, + UseTypeAttributes, +}; #[derive(Default, Clone)] pub struct CgpComponentAttributes { pub extend: Vec, pub use_type: UseTypeAttributes, pub prefixes: Vec, + pub derive_delegate_attributes: DeriveDelegateAttributes, } impl CgpComponentAttributes { @@ -57,6 +61,13 @@ impl CgpComponentAttributes { } else if ident == "prefix" { let namespace_specs = attribute.parse_args_with(PrefixAttribute::parse)?; parsed_attributes.prefixes.push(namespace_specs); + } else if ident == "derive_delegate" { + let derive_delegate_attribute = + attribute.parse_args_with(DeriveDelegateAttribute::parse)?; + parsed_attributes + .derive_delegate_attributes + .attributes + .push(derive_delegate_attribute); } else { attributes.push(attribute); } diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/item.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/item.rs index c9c5ac6c..b79c216f 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/item.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/item.rs @@ -60,7 +60,7 @@ impl EvaluatedCgpComponent { let component_type = self.args.component_name.to_type(); let mut provider_impls = ItemProviderImpls::default(); - for delegate_attribute in &self.args.derive_delegate_attributes.attributes { + for delegate_attribute in &self.attributes.derive_delegate_attributes.attributes { let item_impl = delegate_attribute.to_provider_impl(provider_trait)?; provider_impls.items.push(ItemProviderImpl { component_type: component_type.clone(), diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs index b5aad5a5..7f44ea57 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs @@ -10,7 +10,12 @@ pub struct ItemCgpComponent { impl ItemCgpComponent { pub fn preprocess(&self) -> syn::Result { - let (attributes, item_trait) = CgpComponentAttributes::preprocess(&self.item_trait)?; + let (mut attributes, item_trait) = CgpComponentAttributes::preprocess(&self.item_trait)?; + + attributes + .derive_delegate_attributes + .attributes + .extend(self.args.derive_delegate_attributes.attributes.clone()); Ok(PreprocessedCgpComponent { args: self.args.clone(), diff --git a/crates/macros/cgp-macro-test-util/README.md b/crates/macros/cgp-macro-test-util/README.md index 800346c4..d007527a 100644 --- a/crates/macros/cgp-macro-test-util/README.md +++ b/crates/macros/cgp-macro-test-util/README.md @@ -285,7 +285,7 @@ snapshot_cgp_type! { Both the default form `#[cgp_type]` and the custom provider name forms `#[cgp_type(ScalarTypeProvider)]` and -`#[cgp_type { provider: ..., derive_delegate: ... }]` are accepted, mirroring the +`#[cgp_type { provider: ... }]` are accepted, mirroring the real macro. In addition to the usual `#[cgp_component]` output, the snapshot captures the extra `UseType` / `WithProvider` providers that `#[cgp_type]` generates. diff --git a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs b/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs index d40017b1..9a67e143 100644 --- a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs +++ b/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs @@ -6,13 +6,9 @@ use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_cgp_type}; pub struct UseDelegate2(pub PhantomData); snapshot_cgp_type! { - #[cgp_type { - provider: FooTypeProviderAt, - derive_delegate: [ - UseDelegate, - UseDelegate2<(I, J)>, - ], - }] + #[cgp_type(FooTypeProviderAt)] + #[derive_delegate(UseDelegate)] + #[derive_delegate(UseDelegate2<(I, J)>)] pub trait HasFooTypeAt { type Foo; } @@ -185,13 +181,9 @@ snapshot_cgp_type! { } snapshot_cgp_getter! { - #[cgp_getter { - provider: FooGetterAt, - derive_delegate: [ - UseDelegate, - UseDelegate2<(I, J)>, - ], - }] + #[cgp_getter(FooGetterAt)] + #[derive_delegate(UseDelegate)] + #[derive_delegate(UseDelegate2<(I, J)>)] pub trait HasFooAt: HasFooTypeAt { fn foo_at(&self, _tag: PhantomData<(I, J)>) -> &Self::Foo; } From 640c9c0fb382b3965ed8a4073efe7549a35d0476 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 11:01:31 +0200 Subject: [PATCH 07/10] Remove `derive_delegate` in cgp_component args --- .../src/types/cgp_component/args/component_args.rs | 5 ----- .../cgp-macro-core/src/types/cgp_component/args/raw.rs | 8 -------- .../macros/cgp-macro-core/src/types/cgp_component/item.rs | 7 +------ 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/args/component_args.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/args/component_args.rs index ad9f763b..1d4d2381 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/args/component_args.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/args/component_args.rs @@ -2,7 +2,6 @@ use proc_macro2::Span; use syn::parse::Parse; use syn::{Error, Ident}; -use crate::types::attributes::DeriveDelegateAttributes; use crate::types::cgp_component::CgpComponentRawArgs; use crate::types::ident::IdentWithTypeGenerics; @@ -11,7 +10,6 @@ pub struct CgpComponentArgs { pub context_ident: Ident, pub provider_ident: Ident, pub component_name: IdentWithTypeGenerics, - pub derive_delegate_attributes: DeriveDelegateAttributes, } impl Parse for CgpComponentArgs { @@ -41,13 +39,10 @@ impl TryFrom for CgpComponentArgs { )) }); - let derive_delegate_attributes = raw_args.derive_delegate_attributes.unwrap_or_default(); - Ok(Self { context_ident, provider_ident, component_name, - derive_delegate_attributes, }) } } diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/args/raw.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/args/raw.rs index 1a87f01b..29f00589 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/args/raw.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/args/raw.rs @@ -2,7 +2,6 @@ use syn::parse::{End, Parse, ParseStream}; use syn::token::{Colon, Comma}; use syn::{Error, Ident}; -use crate::types::attributes::DeriveDelegateAttributes; use crate::types::ident::IdentWithTypeGenerics; #[derive(Default)] @@ -10,7 +9,6 @@ pub struct CgpComponentRawArgs { pub context_ident: Option, pub provider_ident: Option, pub component_name: Option, - pub derive_delegate_attributes: Option, } impl Parse for CgpComponentRawArgs { @@ -53,12 +51,6 @@ impl Parse for CgpComponentRawArgs { args.provider_ident = Some(input.parse()?); } - "derive_delegate" => { - if args.derive_delegate_attributes.is_some() { - return Err(Error::new(key.span(), "duplicate key is not allowed")); - } - args.derive_delegate_attributes = Some(input.parse()?); - } _ => { return Err(Error::new(key.span(), format!("unknown key {key}"))); } diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs index 7f44ea57..b5aad5a5 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs @@ -10,12 +10,7 @@ pub struct ItemCgpComponent { impl ItemCgpComponent { pub fn preprocess(&self) -> syn::Result { - let (mut attributes, item_trait) = CgpComponentAttributes::preprocess(&self.item_trait)?; - - attributes - .derive_delegate_attributes - .attributes - .extend(self.args.derive_delegate_attributes.attributes.clone()); + let (attributes, item_trait) = CgpComponentAttributes::preprocess(&self.item_trait)?; Ok(PreprocessedCgpComponent { args: self.args.clone(), From 65f99960eecc532febb526424e83dff8896e6395 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 12:57:39 +0200 Subject: [PATCH 08/10] Remove `Parse` impl for `DeriveDelegateAttributes` --- .../attributes/derive_delegate/attributes.rs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/attributes/derive_delegate/attributes.rs b/crates/macros/cgp-macro-core/src/types/attributes/derive_delegate/attributes.rs index edf121ad..d4bf6908 100644 --- a/crates/macros/cgp-macro-core/src/types/attributes/derive_delegate/attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/attributes/derive_delegate/attributes.rs @@ -1,30 +1,6 @@ -use syn::bracketed; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::token::{Bracket, Comma}; - use crate::types::attributes::DeriveDelegateAttribute; #[derive(Default, Clone)] pub struct DeriveDelegateAttributes { pub attributes: Vec, } - -impl Parse for DeriveDelegateAttributes { - fn parse(input: ParseStream) -> syn::Result { - if input.peek(Bracket) { - let body; - bracketed!(body in input); - - let attributes = >::parse_terminated(&body)?; - Ok(Self { - attributes: Vec::from_iter(attributes), - }) - } else { - let spec = input.parse()?; - Ok(Self { - attributes: vec![spec], - }) - } - } -} From 31958085c70656830fad6fd5f78f810c9183c6e1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 15:53:05 +0200 Subject: [PATCH 09/10] Remove `IsDelegateKeyIn` --- crates/core/cgp-component/src/traits/is_delegate_key_in.rs | 5 ----- crates/core/cgp-component/src/traits/mod.rs | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 crates/core/cgp-component/src/traits/is_delegate_key_in.rs diff --git a/crates/core/cgp-component/src/traits/is_delegate_key_in.rs b/crates/core/cgp-component/src/traits/is_delegate_key_in.rs deleted file mode 100644 index 42f347e4..00000000 --- a/crates/core/cgp-component/src/traits/is_delegate_key_in.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::DelegateComponent; - -pub trait IsDelegateKeyIn
{} - -impl IsDelegateKeyIn
for Key where Table: DelegateComponent {} diff --git a/crates/core/cgp-component/src/traits/mod.rs b/crates/core/cgp-component/src/traits/mod.rs index d51a635d..1e5f1a04 100644 --- a/crates/core/cgp-component/src/traits/mod.rs +++ b/crates/core/cgp-component/src/traits/mod.rs @@ -1,9 +1,7 @@ mod can_use_component; mod delegate_component; -mod is_delegate_key_in; mod is_provider; pub use can_use_component::*; pub use delegate_component::DelegateComponent; -pub use is_delegate_key_in::*; pub use is_provider::*; From 0197aa27bf7c0ceae89ba65ca62f795433621206 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 28 Jun 2026 16:06:40 +0200 Subject: [PATCH 10/10] Minor docs update --- crates/core/cgp-component/src/providers/use_delegate.rs | 2 +- crates/macros/cgp-macro/src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/core/cgp-component/src/providers/use_delegate.rs b/crates/core/cgp-component/src/providers/use_delegate.rs index 0dbf76bc..5963b4d2 100644 --- a/crates/core/cgp-component/src/providers/use_delegate.rs +++ b/crates/core/cgp-component/src/providers/use_delegate.rs @@ -20,7 +20,7 @@ use core::marker::PhantomData; that overlaps on the generic parameters. The implementation of `UseDelegate` can be automatically generated through - the `derive_delegate` entry in `#[cgp_component]`. It is also possible to + the `#[derive_delegate]` attribute in `#[cgp_component]`. It is also possible to implement the dispatcher pattern on types other than `UseDelegate`, especially when there are multiple generic parameters that could be dispatched differently. We mainly use `UseDelegate` as the default dispatcher, so that users don't need diff --git a/crates/macros/cgp-macro/src/lib.rs b/crates/macros/cgp-macro/src/lib.rs index 78ad0548..61cfb6d0 100644 --- a/crates/macros/cgp-macro/src/lib.rs +++ b/crates/macros/cgp-macro/src/lib.rs @@ -25,10 +25,6 @@ use proc_macro::TokenStream; - `context` - the identifier used for the generic context type. If not provided, the default identifier `Context` would be used. - - `derive_delegate` - a list of generic dispatcher wrappers to derive the - `UseDelegate` pattern on, with the matching generic parameters specified in - the generic argument of the wrapper. Refer to `UseDelegate` for more details. - ## Extension Macros There are two other macros that extends `#[cgp_component]` that can be used for