From 64a17e21ec9b4dd8afbf8432ca1160083ff37834 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 3 Mar 2026 21:07:18 +0100 Subject: [PATCH 01/45] Experiment on UseNamespace and RedirectLookup --- crates/cgp-tests/tests/redirect.rs | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 crates/cgp-tests/tests/redirect.rs diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs new file mode 100644 index 00000000..29bc4409 --- /dev/null +++ b/crates/cgp-tests/tests/redirect.rs @@ -0,0 +1,55 @@ +use cgp::prelude::*; + +#[cgp_component(FooProvider)] +pub trait CanDoFoo { + fn foo(); +} + +pub struct UseNamespace(pub PhantomData); + +pub struct RedirectLookup(pub PhantomData<(Key, Components)>); + +#[cgp_impl(RedirectLookup)] +#[use_provider(Components::Delegate: FooProvider)] +impl FooProvider +where + Components: DelegateComponent, +{ + fn foo() { + Components::Delegate::foo(); + } +} + +delegate_components! { + UseNamespace { + FooProviderComponent: RedirectLookup, + } +} + +pub struct BarComponent; + +#[cgp_impl(new TestProvider)] +impl FooProvider { + fn foo() {} +} + +pub struct App; + +delegate_components! { + App { + FooProviderComponent: UseNamespace, + BarComponent: TestProvider, + } +} + +delegate_components! { + new InnerComponents { + BarComponent: TestProvider, + } +} + +check_components! { + App { + FooProviderComponent, + } +} From 88353cc2737f4f985e21257d7d35044b1649f5da Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 3 Mar 2026 21:27:39 +0100 Subject: [PATCH 02/45] More experiment --- crates/cgp-tests/tests/redirect.rs | 40 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index 29bc4409..605b019d 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -1,13 +1,17 @@ use cgp::prelude::*; +pub trait HasNamespace {} + +pub struct UseNamespace(pub PhantomData<(Path, Components)>); + +pub struct RedirectLookup(pub PhantomData<(Key, Components)>); + #[cgp_component(FooProvider)] pub trait CanDoFoo { fn foo(); } -pub struct UseNamespace(pub PhantomData); - -pub struct RedirectLookup(pub PhantomData<(Key, Components)>); +impl HasNamespace for FooProviderComponent {} #[cgp_impl(RedirectLookup)] #[use_provider(Components::Delegate: FooProvider)] @@ -21,13 +25,21 @@ where } delegate_components! { - UseNamespace { - FooProviderComponent: RedirectLookup, + UseNamespace<(), Components> { + FooProviderComponent: RedirectLookup<(BarComponent, BazComponent), Components>, + } +} + +delegate_components! { + UseNamespace { + FooProviderComponent: RedirectLookup, } } pub struct BarComponent; +pub struct BazComponent; + #[cgp_impl(new TestProvider)] impl FooProvider { fn foo() {} @@ -36,15 +48,19 @@ impl FooProvider { pub struct App; delegate_components! { + // #[use_namespace] App { - FooProviderComponent: UseNamespace, - BarComponent: TestProvider, - } -} + > Component: + UseNamespace<(), App>, -delegate_components! { - new InnerComponents { - BarComponent: TestProvider, + // open BarComponent; + // + // (BarComponent, Components): UseNamespace, + + // BazComponent: TestProvider, + + // @BarComponent::BazComponent: TestProvider, + (BarComponent, BazComponent): TestProvider, } } From eacefb0f3e711b5aa209e1680cbc6e319a47198b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 3 Mar 2026 21:58:57 +0100 Subject: [PATCH 03/45] Simplify hierarchical namspace lookup with product type --- crates/cgp-tests/tests/redirect.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index 605b019d..9e77b15f 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -25,14 +25,8 @@ where } delegate_components! { - UseNamespace<(), Components> { - FooProviderComponent: RedirectLookup<(BarComponent, BazComponent), Components>, - } -} - -delegate_components! { - UseNamespace { - FooProviderComponent: RedirectLookup, + UseNamespace { + FooProviderComponent: RedirectLookup, } } @@ -51,16 +45,16 @@ delegate_components! { // #[use_namespace] App { > Component: - UseNamespace<(), App>, + UseNamespace, - // open BarComponent; - // - // (BarComponent, Components): UseNamespace, + // @BarComponent::* : TestProvider, + // Cons: TestProvider, - // BazComponent: TestProvider, + // @BarComponent::BazComponent::* : TestProvider, + Cons>: TestProvider, - // @BarComponent::BazComponent: TestProvider, - (BarComponent, BazComponent): TestProvider, + // @BarComponent::BazComponent::FooProviderComponent : TestProvider, + // Product![BarComponent, BazComponent, FooProviderComponent]: TestProvider, } } From 9518c1fa2c76add869d9bcd144d05820c7652b78 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 3 Mar 2026 22:09:54 +0100 Subject: [PATCH 04/45] More experiment on separator syntax --- crates/cgp-tests/tests/redirect.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index 9e77b15f..a228671a 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -51,10 +51,13 @@ delegate_components! { // Cons: TestProvider, // @BarComponent::BazComponent::* : TestProvider, - Cons>: TestProvider, + // Cons>: TestProvider, // @BarComponent::BazComponent::FooProviderComponent : TestProvider, - // Product![BarComponent, BazComponent, FooProviderComponent]: TestProvider, + Product![BarComponent, BazComponent, FooProviderComponent]: TestProvider, + + // @*::BazComponent::*: TestProvider, + // Cons>: TestProvider, } } From ace78b0cf45f60ed7f865fb191755d96a04246b8 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 3 Mar 2026 22:11:00 +0100 Subject: [PATCH 05/45] Remove Path from UseNamespace --- crates/cgp-tests/tests/redirect.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index a228671a..21a5b36c 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -2,7 +2,7 @@ use cgp::prelude::*; pub trait HasNamespace {} -pub struct UseNamespace(pub PhantomData<(Path, Components)>); +pub struct UseNamespace(pub PhantomData); pub struct RedirectLookup(pub PhantomData<(Key, Components)>); @@ -25,7 +25,7 @@ where } delegate_components! { - UseNamespace { + UseNamespace { FooProviderComponent: RedirectLookup, } } @@ -45,7 +45,7 @@ delegate_components! { // #[use_namespace] App { > Component: - UseNamespace, + UseNamespace, // @BarComponent::* : TestProvider, // Cons: TestProvider, From 108c2e6ea5fe11710ac1f829a670ddd5b53bbfaf Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 8 Mar 2026 21:10:41 +0100 Subject: [PATCH 06/45] Experiment on extended namespace --- Cargo.lock | 1 + crates/cgp-component/src/lib.rs | 4 ++- crates/cgp-component/src/namespaces.rs | 5 +++ crates/cgp-component/src/types/mod.rs | 2 ++ .../src/types/redirect_lookup.rs | 3 ++ crates/cgp-core/src/prelude.rs | 4 +-- crates/cgp-error/Cargo.toml | 1 + crates/cgp-error/src/lib.rs | 2 ++ crates/cgp-error/src/paths.rs | 1 + .../cgp-error/src/traits/can_raise_error.rs | 16 ++++++++-- crates/cgp-error/src/traits/has_error_type.rs | 23 +++++++++++-- crates/cgp-field/src/traits/append_product.rs | 16 ++++++++++ crates/cgp-field/src/traits/mod.rs | 2 ++ crates/cgp-tests/src/lib.rs | 22 +++++++++++++ crates/cgp-tests/tests/namespace.rs | 32 +++++++++++++++++++ 15 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 crates/cgp-component/src/namespaces.rs create mode 100644 crates/cgp-component/src/types/redirect_lookup.rs create mode 100644 crates/cgp-error/src/paths.rs create mode 100644 crates/cgp-field/src/traits/append_product.rs create mode 100644 crates/cgp-tests/tests/namespace.rs diff --git a/Cargo.lock b/Cargo.lock index 14945d3c..bd3a639d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,7 @@ name = "cgp-error" version = "0.7.0" dependencies = [ "cgp-component", + "cgp-field", "cgp-macro", "cgp-type", ] diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 0785c757..83f1f906 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -5,8 +5,10 @@ CGP component implementation. */ +mod namespaces; mod traits; mod types; +pub use namespaces::{CoreComponents, DefaultNamespace}; pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; -pub use types::{UseContext, UseDelegate, UseFields, WithContext, WithProvider}; +pub use types::{RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider}; diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs new file mode 100644 index 00000000..59ef4cca --- /dev/null +++ b/crates/cgp-component/src/namespaces.rs @@ -0,0 +1,5 @@ +pub trait DefaultNamespace { + type Path; +} + +pub struct CoreComponents; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index e2d83e97..19b867e1 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -1,8 +1,10 @@ +mod redirect_lookup; mod use_context; mod use_delegate; mod use_fields; mod with_provider; +pub use redirect_lookup::RedirectLookup; pub use use_context::{UseContext, WithContext}; pub use use_delegate::UseDelegate; pub use use_fields::UseFields; diff --git a/crates/cgp-component/src/types/redirect_lookup.rs b/crates/cgp-component/src/types/redirect_lookup.rs new file mode 100644 index 00000000..dbc117e5 --- /dev/null +++ b/crates/cgp-component/src/types/redirect_lookup.rs @@ -0,0 +1,3 @@ +use core::marker::PhantomData; + +pub struct RedirectLookup(pub PhantomData<(Key, Components)>); diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 1476edb3..355aafb7 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - CanUseComponent, DelegateComponent, IsProviderFor, UseContext, UseDelegate, UseFields, - WithContext, WithProvider, + CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, UseContext, UseDelegate, + UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/Cargo.toml b/crates/cgp-error/Cargo.toml index c4fb9648..061de754 100644 --- a/crates/cgp-error/Cargo.toml +++ b/crates/cgp-error/Cargo.toml @@ -15,3 +15,4 @@ description = """ cgp-component = { workspace = true } cgp-macro = { workspace = true } cgp-type = { workspace = true } +cgp-field = { workspace = true } diff --git a/crates/cgp-error/src/lib.rs b/crates/cgp-error/src/lib.rs index 56a70f70..b30722f3 100644 --- a/crates/cgp-error/src/lib.rs +++ b/crates/cgp-error/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] mod contexts; +mod paths; mod traits; pub use contexts::*; +pub use paths::*; pub use traits::*; diff --git a/crates/cgp-error/src/paths.rs b/crates/cgp-error/src/paths.rs new file mode 100644 index 00000000..35968e7c --- /dev/null +++ b/crates/cgp-error/src/paths.rs @@ -0,0 +1 @@ +pub struct ErrorComponents; diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index be617d30..e8c467d5 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -1,5 +1,5 @@ -use cgp_component::{DelegateComponent, IsProviderFor, UseContext, UseDelegate}; -use cgp_macro::cgp_component; +use cgp_component::{DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate}; +use cgp_macro::{cgp_component, cgp_impl}; use crate::traits::has_error_type::HasErrorType; @@ -14,3 +14,15 @@ use crate::traits::has_error_type::HasErrorType; pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } + +#[cgp_impl(RedirectLookup)] +#[use_type(HasErrorType::Error)] +#[use_provider(Components::Delegate: ErrorRaiser)] +impl ErrorRaiser +where + Components: DelegateComponent, +{ + fn raise_error(error: E) -> Error { + Components::Delegate::raise_error(error) + } +} diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 32e8fe11..6ff1c8b6 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -1,9 +1,15 @@ use core::fmt::Debug; -use cgp_component::{DelegateComponent, IsProviderFor, UseContext, WithProvider}; -use cgp_macro::cgp_type; +use cgp_component::{ + CoreComponents, DefaultNamespace, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, + WithProvider, +}; +use cgp_field::types::*; +use cgp_macro::{Product, cgp_impl, cgp_type}; use cgp_type::{TypeProvider, UseType}; +use crate::ErrorComponents; + /** The `HasErrorType` trait provides an abstract error type that can be used by CGP components to decouple the code from any concrete error implementation. @@ -29,3 +35,16 @@ pub trait HasErrorType { } pub type ErrorOf = ::Error; + +impl DefaultNamespace for ErrorTypeProviderComponent { + type Path = Product![CoreComponents, ErrorComponents]; +} + +#[cgp_impl(RedirectLookup)] +#[use_provider(Components::Delegate: ErrorTypeProvider)] +impl ErrorTypeProvider +where + Components: DelegateComponent, +{ + type Error = >::Error; +} diff --git a/crates/cgp-field/src/traits/append_product.rs b/crates/cgp-field/src/traits/append_product.rs new file mode 100644 index 00000000..c7b465cf --- /dev/null +++ b/crates/cgp-field/src/traits/append_product.rs @@ -0,0 +1,16 @@ +use crate::types::{Cons, Nil}; + +pub trait AppendProduct { + type Output; +} + +impl AppendProduct for Nil { + type Output = Cons; +} + +impl AppendProduct for Cons +where + Tail: AppendProduct, +{ + type Output = Cons; +} diff --git a/crates/cgp-field/src/traits/mod.rs b/crates/cgp-field/src/traits/mod.rs index dfdec21b..3585c43b 100644 --- a/crates/cgp-field/src/traits/mod.rs +++ b/crates/cgp-field/src/traits/mod.rs @@ -1,3 +1,4 @@ +mod append_product; mod build_field; mod extract_field; mod from_fields; @@ -18,6 +19,7 @@ mod to_fields; mod transform_map; mod update_field; +pub use append_product::*; pub use build_field::*; pub use extract_field::*; pub use from_fields::*; diff --git a/crates/cgp-tests/src/lib.rs b/crates/cgp-tests/src/lib.rs index 192cb697..e64aa563 100644 --- a/crates/cgp-tests/src/lib.rs +++ b/crates/cgp-tests/src/lib.rs @@ -1,2 +1,24 @@ #[cfg(test)] pub mod tests; + +use cgp::core::component::CoreComponents; +use cgp::core::error::{ErrorComponents, ErrorRaiserComponent}; +use cgp::prelude::*; + +pub trait ExtendedNamespace { + type Path; +} + +pub struct ExtendedNamespaceComponents; + +impl ExtendedNamespace for Component +where + Component: DefaultNamespace + + DefaultNamespace, +{ + type Path = Path; +} + +impl ExtendedNamespace for ErrorRaiserComponent { + type Path = Product![CoreComponents, ErrorComponents]; +} diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs new file mode 100644 index 00000000..6a8364b0 --- /dev/null +++ b/crates/cgp-tests/tests/namespace.rs @@ -0,0 +1,32 @@ +use cgp::core::component::{CoreComponents, RedirectLookup}; +use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::core::field::traits::AppendProduct; +use cgp::extra::error::RaiseFrom; +use cgp::extra::handler::CanTryCompute; +use cgp::prelude::*; +use cgp_tests::ExtendedNamespace; + +pub struct App; + +delegate_components! { + App { + >> + Component: + RedirectLookup, + Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]: + UseType, + Product![CoreComponents, ErrorComponents, ErrorRaiserComponent]: + RaiseFrom, + TryComputerComponent: + Foo, + } +} + +#[cgp_computer] +fn foo(x: u64) -> Result { + Ok(x * 2) +} + +pub trait CheckApp: HasErrorType + CanRaiseError<&'static str> + CanTryCompute<(), u64> {} + +impl CheckApp for App {} From e0467bf30634b355617318f3cb0230ad2b4adcc9 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 8 Mar 2026 22:44:48 +0100 Subject: [PATCH 07/45] Experiment on overridding namespace path --- crates/cgp-error/src/traits/has_error_type.rs | 2 +- crates/cgp-tests/src/lib.rs | 12 ++++++++++-- crates/cgp-tests/tests/namespace.rs | 15 +++++++-------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 6ff1c8b6..b085efc6 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -37,7 +37,7 @@ pub trait HasErrorType { pub type ErrorOf = ::Error; impl DefaultNamespace for ErrorTypeProviderComponent { - type Path = Product![CoreComponents, ErrorComponents]; + type Path = Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]; } #[cgp_impl(RedirectLookup)] diff --git a/crates/cgp-tests/src/lib.rs b/crates/cgp-tests/src/lib.rs index e64aa563..33882cfe 100644 --- a/crates/cgp-tests/src/lib.rs +++ b/crates/cgp-tests/src/lib.rs @@ -2,7 +2,7 @@ pub mod tests; use cgp::core::component::CoreComponents; -use cgp::core::error::{ErrorComponents, ErrorRaiserComponent}; +use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::prelude::*; pub trait ExtendedNamespace { @@ -11,6 +11,8 @@ pub trait ExtendedNamespace { pub struct ExtendedNamespaceComponents; +pub struct MyErrorComponents; + impl ExtendedNamespace for Component where Component: DefaultNamespace @@ -20,5 +22,11 @@ where } impl ExtendedNamespace for ErrorRaiserComponent { - type Path = Product![CoreComponents, ErrorComponents]; + type Path = Product![MyErrorComponents, ErrorRaiserComponent]; +} + +impl ExtendedNamespace + for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] +{ + type Path = Product![MyErrorComponents, ErrorTypeProviderComponent]; } diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs index 6a8364b0..85c5c2b0 100644 --- a/crates/cgp-tests/tests/namespace.rs +++ b/crates/cgp-tests/tests/namespace.rs @@ -1,21 +1,20 @@ -use cgp::core::component::{CoreComponents, RedirectLookup}; -use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; -use cgp::core::field::traits::AppendProduct; +use cgp::core::component::RedirectLookup; +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; -use cgp_tests::ExtendedNamespace; +use cgp_tests::{ExtendedNamespace, MyErrorComponents}; pub struct App; delegate_components! { App { - >> + > Component: - RedirectLookup, - Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]: + RedirectLookup, + Product![MyErrorComponents, ErrorTypeProviderComponent]: UseType, - Product![CoreComponents, ErrorComponents, ErrorRaiserComponent]: + Product![MyErrorComponents, ErrorRaiserComponent]: RaiseFrom, TryComputerComponent: Foo, From bd4f5c9ac28f8502e482e3d2eea6159e4e9a7e09 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 10 Mar 2026 21:29:07 +0100 Subject: [PATCH 08/45] More experiments --- .../src/types/redirect_lookup.rs | 25 ++++++++++++++++ .../cgp-error/src/traits/can_raise_error.rs | 22 +++++++------- crates/cgp-error/src/traits/has_error_type.rs | 26 +++++++++++------ crates/cgp-tests/tests/namespace.rs | 29 ++++++++++++++++--- crates/cgp-tests/tests/redirect.rs | 8 ++--- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/crates/cgp-component/src/types/redirect_lookup.rs b/crates/cgp-component/src/types/redirect_lookup.rs index dbc117e5..a305aca6 100644 --- a/crates/cgp-component/src/types/redirect_lookup.rs +++ b/crates/cgp-component/src/types/redirect_lookup.rs @@ -1,3 +1,28 @@ use core::marker::PhantomData; +use crate::{DelegateComponent, IsProviderFor}; + pub struct RedirectLookup(pub PhantomData<(Key, Components)>); + +impl DelegateComponent for RedirectLookup +where + Components: DelegateComponent, +{ + type Delegate = Components::Delegate; +} + +impl IsProviderFor + for RedirectLookup +where + Components: DelegateComponent, + Components::Delegate: IsProviderFor, +{ +} + +// delegate_components! { +// , Component> +// RedirectLookup { +// Component: +// Components::Delegate, +// } +// } diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index e8c467d5..8f0397bb 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -15,14 +15,14 @@ pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } -#[cgp_impl(RedirectLookup)] -#[use_type(HasErrorType::Error)] -#[use_provider(Components::Delegate: ErrorRaiser)] -impl ErrorRaiser -where - Components: DelegateComponent, -{ - fn raise_error(error: E) -> Error { - Components::Delegate::raise_error(error) - } -} +// #[cgp_impl(RedirectLookup)] +// #[use_type(HasErrorType::Error)] +// #[use_provider(Components::Delegate: ErrorRaiser)] +// impl ErrorRaiser +// where +// Components: DelegateComponent, +// { +// fn raise_error(error: E) -> Error { +// Components::Delegate::raise_error(error) +// } +// } diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index b085efc6..9ac91dde 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -5,7 +5,7 @@ use cgp_component::{ WithProvider, }; use cgp_field::types::*; -use cgp_macro::{Product, cgp_impl, cgp_type}; +use cgp_macro::{Product, cgp_type, delegate_components}; use cgp_type::{TypeProvider, UseType}; use crate::ErrorComponents; @@ -40,11 +40,19 @@ impl DefaultNamespace for ErrorTypeProviderComponent { type Path = Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]; } -#[cgp_impl(RedirectLookup)] -#[use_provider(Components::Delegate: ErrorTypeProvider)] -impl ErrorTypeProvider -where - Components: DelegateComponent, -{ - type Error = >::Error; -} +// delegate_components! { +// > +// RedirectLookup { +// ErrorTypeProviderComponent: +// Components::Delegate, +// } +// } + +// #[cgp_impl(RedirectLookup)] +// #[use_provider(Components::Delegate: ErrorTypeProvider)] +// impl ErrorTypeProvider +// where +// Components: DelegateComponent, +// { +// type Error = >::Error; +// } diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs index 85c5c2b0..fb8c602f 100644 --- a/crates/cgp-tests/tests/namespace.rs +++ b/crates/cgp-tests/tests/namespace.rs @@ -7,13 +7,34 @@ use cgp_tests::{ExtendedNamespace, MyErrorComponents}; pub struct App; +impl DelegateComponent for App +where + Component: ExtendedNamespace, + App: DelegateComponent, +{ + // type Delegate = Delegate; + type Delegate = RedirectLookup; +} + +impl IsProviderFor for App +where + Component: ExtendedNamespace, + RedirectLookup: IsProviderFor, + App: DelegateComponent, +{ +} + delegate_components! { App { - > - Component: - RedirectLookup, - Product![MyErrorComponents, ErrorTypeProviderComponent]: + // > + // Component: + // RedirectLookup, + ErrorTypeProviderComponent: UseType, + // ErrorRaiserComponent: + // RaiseFrom, + // Product![MyErrorComponents, ErrorTypeProviderComponent]: + // UseType, Product![MyErrorComponents, ErrorRaiserComponent]: RaiseFrom, TryComputerComponent: diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index 21a5b36c..7d128a2b 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -47,16 +47,16 @@ delegate_components! { > Component: UseNamespace, - // @BarComponent::* : TestProvider, + // @BarComponent.* : TestProvider, // Cons: TestProvider, - // @BarComponent::BazComponent::* : TestProvider, + // @BarComponent.BazComponent.* : TestProvider, // Cons>: TestProvider, - // @BarComponent::BazComponent::FooProviderComponent : TestProvider, + // @BarComponent.BazComponent.FooProviderComponent : TestProvider, Product![BarComponent, BazComponent, FooProviderComponent]: TestProvider, - // @*::BazComponent::*: TestProvider, + // @*.BazComponent.*: TestProvider, // Cons>: TestProvider, } } From 474adbcf08af4fce286b04620530390aec86ab8b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 10 Mar 2026 22:24:42 +0100 Subject: [PATCH 09/45] Try expand lookup generics --- crates/cgp-component/src/lib.rs | 4 ++- crates/cgp-component/src/types/mod.rs | 2 +- .../src/types/redirect_lookup.rs | 27 +++++++++----- .../cgp-error/src/traits/can_raise_error.rs | 36 +++++++++++++------ crates/cgp-error/src/traits/has_error_type.rs | 18 +++++----- crates/cgp-error/src/traits/mod.rs | 2 +- crates/cgp-field/src/traits/concat_product.rs | 16 +++++++++ crates/cgp-field/src/traits/mod.rs | 2 ++ crates/cgp-tests/tests/namespace.rs | 16 +++++---- 9 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 crates/cgp-field/src/traits/concat_product.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 83f1f906..e279ea40 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -11,4 +11,6 @@ mod types; pub use namespaces::{CoreComponents, DefaultNamespace}; pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; -pub use types::{RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider}; +pub use types::{ + HasDelegate, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, +}; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index 19b867e1..9864c427 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -4,7 +4,7 @@ mod use_delegate; mod use_fields; mod with_provider; -pub use redirect_lookup::RedirectLookup; +pub use redirect_lookup::{HasDelegate, RedirectLookup}; pub use use_context::{UseContext, WithContext}; pub use use_delegate::UseDelegate; pub use use_fields::UseFields; diff --git a/crates/cgp-component/src/types/redirect_lookup.rs b/crates/cgp-component/src/types/redirect_lookup.rs index a305aca6..9c5d26af 100644 --- a/crates/cgp-component/src/types/redirect_lookup.rs +++ b/crates/cgp-component/src/types/redirect_lookup.rs @@ -4,21 +4,32 @@ use crate::{DelegateComponent, IsProviderFor}; pub struct RedirectLookup(pub PhantomData<(Key, Components)>); -impl DelegateComponent for RedirectLookup -where - Components: DelegateComponent, -{ - type Delegate = Components::Delegate; +pub trait HasDelegate { + type Delegate; } -impl IsProviderFor - for RedirectLookup +impl HasDelegate for Path where Components: DelegateComponent, - Components::Delegate: IsProviderFor, { + type Delegate = Components::Delegate; } +// impl DelegateComponent for RedirectLookup +// where +// Components: DelegateComponent, +// { +// type Delegate = Components::Delegate; +// } + +// impl IsProviderFor +// for RedirectLookup +// where +// Components: DelegateComponent, +// Components::Delegate: IsProviderFor, +// { +// } + // delegate_components! { // , Component> // RedirectLookup { diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index 8f0397bb..00a8b900 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -1,4 +1,5 @@ use cgp_component::{DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate}; +use cgp_field::traits::AppendProduct; use cgp_macro::{cgp_component, cgp_impl}; use crate::traits::has_error_type::HasErrorType; @@ -15,14 +16,27 @@ pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } -// #[cgp_impl(RedirectLookup)] -// #[use_type(HasErrorType::Error)] -// #[use_provider(Components::Delegate: ErrorRaiser)] -// impl ErrorRaiser -// where -// Components: DelegateComponent, -// { -// fn raise_error(error: E) -> Error { -// Components::Delegate::raise_error(error) -// } -// } +#[cgp_impl(RedirectLookup)] +#[use_type(HasErrorType::Error)] +#[use_provider(Delegate: ErrorRaiser)] +impl ErrorRaiser +where + Components: DelegateComponent, +{ + fn raise_error(error: E) -> Error { + Delegate::raise_error(error) + } +} + +#[cgp_impl(new LookupGenerics)] +#[use_type(HasErrorType::Error)] +#[use_provider(Delegate: ErrorRaiser)] +impl ErrorRaiser +where + Path: AppendProduct, + Components: DelegateComponent, +{ + fn raise_error(error: E) -> Error { + Delegate::raise_error(error) + } +} diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 9ac91dde..8a09de48 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -5,7 +5,7 @@ use cgp_component::{ WithProvider, }; use cgp_field::types::*; -use cgp_macro::{Product, cgp_type, delegate_components}; +use cgp_macro::{Product, cgp_impl, cgp_type, delegate_components}; use cgp_type::{TypeProvider, UseType}; use crate::ErrorComponents; @@ -48,11 +48,11 @@ impl DefaultNamespace for ErrorTypeProviderComponent { // } // } -// #[cgp_impl(RedirectLookup)] -// #[use_provider(Components::Delegate: ErrorTypeProvider)] -// impl ErrorTypeProvider -// where -// Components: DelegateComponent, -// { -// type Error = >::Error; -// } +#[cgp_impl(RedirectLookup)] +#[use_provider(Components::Delegate: ErrorTypeProvider)] +impl ErrorTypeProvider +where + Components: DelegateComponent, +{ + type Error = >::Error; +} diff --git a/crates/cgp-error/src/traits/mod.rs b/crates/cgp-error/src/traits/mod.rs index 92e26965..c9443848 100644 --- a/crates/cgp-error/src/traits/mod.rs +++ b/crates/cgp-error/src/traits/mod.rs @@ -2,6 +2,6 @@ mod can_raise_error; mod can_wrap_error; mod has_error_type; -pub use can_raise_error::{CanRaiseError, ErrorRaiser, ErrorRaiserComponent}; +pub use can_raise_error::{CanRaiseError, ErrorRaiser, ErrorRaiserComponent, LookupGenerics}; pub use can_wrap_error::{CanWrapError, ErrorWrapper, ErrorWrapperComponent}; pub use has_error_type::{ErrorOf, ErrorTypeProvider, ErrorTypeProviderComponent, HasErrorType}; diff --git a/crates/cgp-field/src/traits/concat_product.rs b/crates/cgp-field/src/traits/concat_product.rs new file mode 100644 index 00000000..39d01911 --- /dev/null +++ b/crates/cgp-field/src/traits/concat_product.rs @@ -0,0 +1,16 @@ +use crate::types::{Cons, Nil}; + +pub trait ConcatProduct { + type Output; +} + +impl ConcatProduct for Nil { + type Output = Items; +} + +impl ConcatProduct for Cons +where + Tail: ConcatProduct, +{ + type Output = Cons; +} diff --git a/crates/cgp-field/src/traits/mod.rs b/crates/cgp-field/src/traits/mod.rs index 3585c43b..63d13dc7 100644 --- a/crates/cgp-field/src/traits/mod.rs +++ b/crates/cgp-field/src/traits/mod.rs @@ -1,5 +1,6 @@ mod append_product; mod build_field; +mod concat_product; mod extract_field; mod from_fields; mod from_variant; @@ -21,6 +22,7 @@ mod update_field; pub use append_product::*; pub use build_field::*; +pub use concat_product::*; pub use extract_field::*; pub use from_fields::*; pub use from_variant::*; diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs index fb8c602f..9f9ddeed 100644 --- a/crates/cgp-tests/tests/namespace.rs +++ b/crates/cgp-tests/tests/namespace.rs @@ -1,5 +1,5 @@ use cgp::core::component::RedirectLookup; -use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent, LookupGenerics}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; @@ -7,10 +7,10 @@ use cgp_tests::{ExtendedNamespace, MyErrorComponents}; pub struct App; -impl DelegateComponent for App +impl DelegateComponent for App where Component: ExtendedNamespace, - App: DelegateComponent, + App: DelegateComponent, { // type Delegate = Delegate; type Delegate = RedirectLookup; @@ -29,13 +29,15 @@ delegate_components! { // > // Component: // RedirectLookup, - ErrorTypeProviderComponent: - UseType, + // ErrorTypeProviderComponent: + // UseType, // ErrorRaiserComponent: // RaiseFrom, - // Product![MyErrorComponents, ErrorTypeProviderComponent]: - // UseType, + Product![MyErrorComponents, ErrorTypeProviderComponent]: + UseType, Product![MyErrorComponents, ErrorRaiserComponent]: + LookupGenerics, + Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: RaiseFrom, TryComputerComponent: Foo, From 7a6a692915b2cee19c6726636dde84bb64cd29f7 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 10 Mar 2026 22:51:03 +0100 Subject: [PATCH 10/45] RedirectLookup can now expand generics --- .../src/types/redirect_lookup.rs | 25 +-------------- .../cgp-error/src/traits/can_raise_error.rs | 12 ------- crates/cgp-error/src/traits/has_error_type.rs | 10 +----- crates/cgp-error/src/traits/mod.rs | 2 +- crates/cgp-tests/tests/namespace.rs | 31 +++---------------- 5 files changed, 7 insertions(+), 73 deletions(-) diff --git a/crates/cgp-component/src/types/redirect_lookup.rs b/crates/cgp-component/src/types/redirect_lookup.rs index 9c5d26af..9b9a3367 100644 --- a/crates/cgp-component/src/types/redirect_lookup.rs +++ b/crates/cgp-component/src/types/redirect_lookup.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use crate::{DelegateComponent, IsProviderFor}; +use crate::DelegateComponent; pub struct RedirectLookup(pub PhantomData<(Key, Components)>); @@ -14,26 +14,3 @@ where { type Delegate = Components::Delegate; } - -// impl DelegateComponent for RedirectLookup -// where -// Components: DelegateComponent, -// { -// type Delegate = Components::Delegate; -// } - -// impl IsProviderFor -// for RedirectLookup -// where -// Components: DelegateComponent, -// Components::Delegate: IsProviderFor, -// { -// } - -// delegate_components! { -// , Component> -// RedirectLookup { -// Component: -// Components::Delegate, -// } -// } diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index 00a8b900..d6e4536f 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -20,18 +20,6 @@ pub trait CanRaiseError: HasErrorType { #[use_type(HasErrorType::Error)] #[use_provider(Delegate: ErrorRaiser)] impl ErrorRaiser -where - Components: DelegateComponent, -{ - fn raise_error(error: E) -> Error { - Delegate::raise_error(error) - } -} - -#[cgp_impl(new LookupGenerics)] -#[use_type(HasErrorType::Error)] -#[use_provider(Delegate: ErrorRaiser)] -impl ErrorRaiser where Path: AppendProduct, Components: DelegateComponent, diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 8a09de48..b085efc6 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -5,7 +5,7 @@ use cgp_component::{ WithProvider, }; use cgp_field::types::*; -use cgp_macro::{Product, cgp_impl, cgp_type, delegate_components}; +use cgp_macro::{Product, cgp_impl, cgp_type}; use cgp_type::{TypeProvider, UseType}; use crate::ErrorComponents; @@ -40,14 +40,6 @@ impl DefaultNamespace for ErrorTypeProviderComponent { type Path = Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]; } -// delegate_components! { -// > -// RedirectLookup { -// ErrorTypeProviderComponent: -// Components::Delegate, -// } -// } - #[cgp_impl(RedirectLookup)] #[use_provider(Components::Delegate: ErrorTypeProvider)] impl ErrorTypeProvider diff --git a/crates/cgp-error/src/traits/mod.rs b/crates/cgp-error/src/traits/mod.rs index c9443848..92e26965 100644 --- a/crates/cgp-error/src/traits/mod.rs +++ b/crates/cgp-error/src/traits/mod.rs @@ -2,6 +2,6 @@ mod can_raise_error; mod can_wrap_error; mod has_error_type; -pub use can_raise_error::{CanRaiseError, ErrorRaiser, ErrorRaiserComponent, LookupGenerics}; +pub use can_raise_error::{CanRaiseError, ErrorRaiser, ErrorRaiserComponent}; pub use can_wrap_error::{CanWrapError, ErrorWrapper, ErrorWrapperComponent}; pub use has_error_type::{ErrorOf, ErrorTypeProvider, ErrorTypeProviderComponent, HasErrorType}; diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs index 9f9ddeed..c98144d8 100644 --- a/crates/cgp-tests/tests/namespace.rs +++ b/crates/cgp-tests/tests/namespace.rs @@ -1,5 +1,5 @@ use cgp::core::component::RedirectLookup; -use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent, LookupGenerics}; +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; @@ -7,36 +7,13 @@ use cgp_tests::{ExtendedNamespace, MyErrorComponents}; pub struct App; -impl DelegateComponent for App -where - Component: ExtendedNamespace, - App: DelegateComponent, -{ - // type Delegate = Delegate; - type Delegate = RedirectLookup; -} - -impl IsProviderFor for App -where - Component: ExtendedNamespace, - RedirectLookup: IsProviderFor, - App: DelegateComponent, -{ -} - delegate_components! { App { - // > - // Component: - // RedirectLookup, - // ErrorTypeProviderComponent: - // UseType, - // ErrorRaiserComponent: - // RaiseFrom, + > + Component: + RedirectLookup, Product![MyErrorComponents, ErrorTypeProviderComponent]: UseType, - Product![MyErrorComponents, ErrorRaiserComponent]: - LookupGenerics, Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: RaiseFrom, TryComputerComponent: From 1e3514c9b883ee1866523bfe05b152f718c82f86 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 20:52:01 +0100 Subject: [PATCH 11/45] Set provider directly in namespace --- crates/cgp-component/src/namespaces.rs | 2 +- crates/cgp-error/src/traits/has_error_type.rs | 7 +++++-- crates/cgp-tests/src/lib.rs | 21 ++++++++++--------- crates/cgp-tests/tests/namespace.rs | 4 +--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs index 59ef4cca..9809c179 100644 --- a/crates/cgp-component/src/namespaces.rs +++ b/crates/cgp-component/src/namespaces.rs @@ -1,5 +1,5 @@ pub trait DefaultNamespace { - type Path; + type Provider; } pub struct CoreComponents; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index b085efc6..71fefea9 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -36,8 +36,11 @@ pub trait HasErrorType { pub type ErrorOf = ::Error; -impl DefaultNamespace for ErrorTypeProviderComponent { - type Path = Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent]; +impl DefaultNamespace for ErrorTypeProviderComponent { + type Provider = RedirectLookup< + Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent], + Components, + >; } #[cgp_impl(RedirectLookup)] diff --git a/crates/cgp-tests/src/lib.rs b/crates/cgp-tests/src/lib.rs index 33882cfe..c36ee9b9 100644 --- a/crates/cgp-tests/src/lib.rs +++ b/crates/cgp-tests/src/lib.rs @@ -1,32 +1,33 @@ #[cfg(test)] pub mod tests; -use cgp::core::component::CoreComponents; +use cgp::core::component::{CoreComponents, RedirectLookup}; use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::prelude::*; pub trait ExtendedNamespace { - type Path; + type Provider; } pub struct ExtendedNamespaceComponents; pub struct MyErrorComponents; -impl ExtendedNamespace for Component +impl ExtendedNamespace for Component where - Component: DefaultNamespace - + DefaultNamespace, + Component: DefaultNamespace + + DefaultNamespace, { - type Path = Path; + type Provider = Provider; } -impl ExtendedNamespace for ErrorRaiserComponent { - type Path = Product![MyErrorComponents, ErrorRaiserComponent]; +impl ExtendedNamespace for ErrorRaiserComponent { + type Provider = RedirectLookup; } -impl ExtendedNamespace +impl ExtendedNamespace for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] { - type Path = Product![MyErrorComponents, ErrorTypeProviderComponent]; + type Provider = + RedirectLookup; } diff --git a/crates/cgp-tests/tests/namespace.rs b/crates/cgp-tests/tests/namespace.rs index c98144d8..fc2fb289 100644 --- a/crates/cgp-tests/tests/namespace.rs +++ b/crates/cgp-tests/tests/namespace.rs @@ -1,4 +1,3 @@ -use cgp::core::component::RedirectLookup; use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; @@ -10,8 +9,7 @@ pub struct App; delegate_components! { App { > - Component: - RedirectLookup, + Component: Component::Provider, Product![MyErrorComponents, ErrorTypeProviderComponent]: UseType, Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: From 7f7dcb38ade219f6272e131e8b18b933d2df8403 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 21:44:08 +0100 Subject: [PATCH 12/45] Add #[use_namespace] attribute --- crates/cgp-component/src/namespaces.rs | 2 +- crates/cgp-macro-lib/src/attributes/mod.rs | 3 ++ .../src/attributes/use_namespace.rs | 19 ++++++++++++ .../src/derive_component/attributes.rs | 6 ++++ .../src/derive_component/derive.rs | 5 +++- .../cgp-macro-lib/src/derive_component/mod.rs | 1 + .../src/derive_component/preprocess.rs | 9 +++--- .../src/entrypoints/cgp_auto_getter.rs | 6 ++-- .../src/entrypoints/cgp_getter.rs | 8 +++-- crates/cgp-macro-lib/src/lib.rs | 1 + crates/cgp-tests/tests/namespace.rs | 30 +------------------ .../tests/namespace_tests/experiments.rs | 29 ++++++++++++++++++ crates/cgp-tests/tests/namespace_tests/mod.rs | 2 ++ .../tests/namespace_tests/use_namespace.rs | 9 ++++++ 14 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 crates/cgp-macro-lib/src/attributes/mod.rs create mode 100644 crates/cgp-macro-lib/src/attributes/use_namespace.rs create mode 100644 crates/cgp-tests/tests/namespace_tests/experiments.rs create mode 100644 crates/cgp-tests/tests/namespace_tests/mod.rs create mode 100644 crates/cgp-tests/tests/namespace_tests/use_namespace.rs diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs index 9809c179..4e20e742 100644 --- a/crates/cgp-component/src/namespaces.rs +++ b/crates/cgp-component/src/namespaces.rs @@ -1,4 +1,4 @@ -pub trait DefaultNamespace { +pub trait DefaultNamespace { type Provider; } diff --git a/crates/cgp-macro-lib/src/attributes/mod.rs b/crates/cgp-macro-lib/src/attributes/mod.rs new file mode 100644 index 00000000..98a06299 --- /dev/null +++ b/crates/cgp-macro-lib/src/attributes/mod.rs @@ -0,0 +1,3 @@ +mod use_namespace; + +pub use use_namespace::*; diff --git a/crates/cgp-macro-lib/src/attributes/use_namespace.rs b/crates/cgp-macro-lib/src/attributes/use_namespace.rs new file mode 100644 index 00000000..29209f24 --- /dev/null +++ b/crates/cgp-macro-lib/src/attributes/use_namespace.rs @@ -0,0 +1,19 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Colon, Dot}; +use syn::{Ident, Type}; + +pub struct UseNamespace { + pub namespace: Ident, + pub path: Punctuated, +} + +impl Parse for UseNamespace { + fn parse(input: ParseStream) -> syn::Result { + let namespace = input.parse()?; + let _: Colon = input.parse()?; + + let path = Punctuated::parse_separated_nonempty(input)?; + Ok(UseNamespace { namespace, path }) + } +} diff --git a/crates/cgp-macro-lib/src/derive_component/attributes.rs b/crates/cgp-macro-lib/src/derive_component/attributes.rs index 1b3c4ce2..c1fdf150 100644 --- a/crates/cgp-macro-lib/src/derive_component/attributes.rs +++ b/crates/cgp-macro-lib/src/derive_component/attributes.rs @@ -4,6 +4,7 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Attribute, TypeParamBound}; +use crate::attributes::UseNamespace; use crate::cgp_fn::UseTypeSpec; pub fn parse_component_attributes( @@ -35,6 +36,10 @@ pub fn parse_component_attributes( } parsed_attributes.use_type.extend(use_type_specs); + } else if ident == "use_namespace" { + let use_namespace_specs = attribute + .parse_args_with(Punctuated::::parse_terminated)?; + parsed_attributes.use_namespace.extend(use_namespace_specs); } else { attributes.push(attribute); } @@ -50,4 +55,5 @@ pub fn parse_component_attributes( pub struct ComponentAttributes { pub extend: Vec, pub use_type: Vec, + pub use_namespace: Vec, } diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index be02ae26..cdcba2f8 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -2,6 +2,7 @@ use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt, quote}; use syn::{ItemImpl, ItemStruct, ItemTrait, parse2}; +use crate::derive_component::attributes::parse_component_attributes; use crate::derive_component::component_name::derive_component_name_struct; use crate::derive_component::consumer_impl::derive_consumer_impl; use crate::derive_component::preprocess_consumer_trait; @@ -22,7 +23,9 @@ pub fn derive_component_with_ast( let component_name = &spec.component_name; let component_params = &spec.component_params; - preprocess_consumer_trait(&mut consumer_trait)?; + let attributes = parse_component_attributes(&mut consumer_trait.attrs)?; + + preprocess_consumer_trait(&mut consumer_trait, &attributes)?; let component_struct = derive_component_name_struct(component_name, component_params)?; diff --git a/crates/cgp-macro-lib/src/derive_component/mod.rs b/crates/cgp-macro-lib/src/derive_component/mod.rs index 314c34fc..5a0ba39a 100644 --- a/crates/cgp-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-macro-lib/src/derive_component/mod.rs @@ -11,5 +11,6 @@ mod signature_args; mod use_context_impl; mod use_delegate_impl; +pub use attributes::*; pub use derive::*; pub use preprocess::*; diff --git a/crates/cgp-macro-lib/src/derive_component/preprocess.rs b/crates/cgp-macro-lib/src/derive_component/preprocess.rs index 93078214..26d14ca6 100644 --- a/crates/cgp-macro-lib/src/derive_component/preprocess.rs +++ b/crates/cgp-macro-lib/src/derive_component/preprocess.rs @@ -1,11 +1,12 @@ use syn::ItemTrait; use crate::cgp_fn::expand_use_type_attributes_on_trait; -use crate::derive_component::attributes::parse_component_attributes; - -pub fn preprocess_consumer_trait(consumer_trait: &mut ItemTrait) -> syn::Result<()> { - let attributes = parse_component_attributes(&mut consumer_trait.attrs)?; +use crate::derive_component::attributes::ComponentAttributes; +pub fn preprocess_consumer_trait( + consumer_trait: &mut ItemTrait, + attributes: &ComponentAttributes, +) -> syn::Result<()> { consumer_trait.supertraits.extend(attributes.extend.clone()); if !attributes.use_type.is_empty() { diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs index ee627762..d0e0eef6 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs @@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{Error, Ident, ItemTrait}; -use crate::derive_component::preprocess_consumer_trait; +use crate::derive_component::{parse_component_attributes, preprocess_consumer_trait}; use crate::derive_getter::{derive_blanket_impl, parse_getter_fields}; pub fn cgp_auto_getter(attr: TokenStream, body: TokenStream) -> syn::Result { @@ -15,7 +15,9 @@ pub fn cgp_auto_getter(attr: TokenStream, body: TokenStream) -> syn::Result syn::Result> - Component: Component::Provider, - Product![MyErrorComponents, ErrorTypeProviderComponent]: - UseType, - Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: - RaiseFrom, - TryComputerComponent: - Foo, - } -} - -#[cgp_computer] -fn foo(x: u64) -> Result { - Ok(x * 2) -} - -pub trait CheckApp: HasErrorType + CanRaiseError<&'static str> + CanTryCompute<(), u64> {} - -impl CheckApp for App {} +pub mod namespace_tests; diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs new file mode 100644 index 00000000..fc2fb289 --- /dev/null +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -0,0 +1,29 @@ +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::extra::error::RaiseFrom; +use cgp::extra::handler::CanTryCompute; +use cgp::prelude::*; +use cgp_tests::{ExtendedNamespace, MyErrorComponents}; + +pub struct App; + +delegate_components! { + App { + > + Component: Component::Provider, + Product![MyErrorComponents, ErrorTypeProviderComponent]: + UseType, + Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: + RaiseFrom, + TryComputerComponent: + Foo, + } +} + +#[cgp_computer] +fn foo(x: u64) -> Result { + Ok(x * 2) +} + +pub trait CheckApp: HasErrorType + CanRaiseError<&'static str> + CanTryCompute<(), u64> {} + +impl CheckApp for App {} diff --git a/crates/cgp-tests/tests/namespace_tests/mod.rs b/crates/cgp-tests/tests/namespace_tests/mod.rs new file mode 100644 index 00000000..eca1bc43 --- /dev/null +++ b/crates/cgp-tests/tests/namespace_tests/mod.rs @@ -0,0 +1,2 @@ +pub mod experiments; +pub mod use_namespace; diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs new file mode 100644 index 00000000..8d09a160 --- /dev/null +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -0,0 +1,9 @@ +use cgp::prelude::*; + +pub struct MyComponents; + +#[cgp_component(FooProvider)] +#[use_namespace(DefaultNamespace: MyComponents)] +pub trait CanDoFoo { + fn foo(&self); +} From 1618fb5b34a7274dbfa3ab67d07db1aca83dfac3 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 22:09:21 +0100 Subject: [PATCH 13/45] Implement derive_namespace_impls --- crates/cgp-core/src/prelude.rs | 2 +- .../src/attributes/use_namespace.rs | 6 +-- .../src/derive_component/attributes.rs | 6 +-- .../src/derive_component/derive.rs | 4 ++ .../src/derive_component/derive_namespace.rs | 51 +++++++++++++++++++ .../cgp-macro-lib/src/derive_component/mod.rs | 1 + .../tests/namespace_tests/use_namespace.rs | 3 +- 7 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 crates/cgp-macro-lib/src/derive_component/derive_namespace.rs diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 355aafb7..4ea04769 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -3,7 +3,7 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, UseContext, UseDelegate, - UseFields, WithContext, WithProvider, + UseFields, WithContext, WithProvider, RedirectLookup, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-macro-lib/src/attributes/use_namespace.rs b/crates/cgp-macro-lib/src/attributes/use_namespace.rs index 29209f24..6a593ac3 100644 --- a/crates/cgp-macro-lib/src/attributes/use_namespace.rs +++ b/crates/cgp-macro-lib/src/attributes/use_namespace.rs @@ -3,17 +3,17 @@ use syn::punctuated::Punctuated; use syn::token::{Colon, Dot}; use syn::{Ident, Type}; -pub struct UseNamespace { +pub struct UseNamespaceAttribute { pub namespace: Ident, pub path: Punctuated, } -impl Parse for UseNamespace { +impl Parse for UseNamespaceAttribute { fn parse(input: ParseStream) -> syn::Result { let namespace = input.parse()?; let _: Colon = input.parse()?; let path = Punctuated::parse_separated_nonempty(input)?; - Ok(UseNamespace { namespace, path }) + Ok(UseNamespaceAttribute { namespace, path }) } } diff --git a/crates/cgp-macro-lib/src/derive_component/attributes.rs b/crates/cgp-macro-lib/src/derive_component/attributes.rs index c1fdf150..2a156d64 100644 --- a/crates/cgp-macro-lib/src/derive_component/attributes.rs +++ b/crates/cgp-macro-lib/src/derive_component/attributes.rs @@ -4,7 +4,7 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Attribute, TypeParamBound}; -use crate::attributes::UseNamespace; +use crate::attributes::UseNamespaceAttribute; use crate::cgp_fn::UseTypeSpec; pub fn parse_component_attributes( @@ -38,7 +38,7 @@ pub fn parse_component_attributes( parsed_attributes.use_type.extend(use_type_specs); } else if ident == "use_namespace" { let use_namespace_specs = attribute - .parse_args_with(Punctuated::::parse_terminated)?; + .parse_args_with(Punctuated::::parse_terminated)?; parsed_attributes.use_namespace.extend(use_namespace_specs); } else { attributes.push(attribute); @@ -55,5 +55,5 @@ pub fn parse_component_attributes( pub struct ComponentAttributes { pub extend: Vec, pub use_type: Vec, - pub use_namespace: Vec, + pub use_namespace: Vec, } diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index cdcba2f8..ebf3579d 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -5,6 +5,7 @@ use syn::{ItemImpl, ItemStruct, ItemTrait, parse2}; use crate::derive_component::attributes::parse_component_attributes; use crate::derive_component::component_name::derive_component_name_struct; use crate::derive_component::consumer_impl::derive_consumer_impl; +use crate::derive_component::derive_namespace::derive_namespace_impls; use crate::derive_component::preprocess_consumer_trait; use crate::derive_component::provider_impl::derive_provider_impl; use crate::derive_component::provider_trait::derive_provider_trait; @@ -79,6 +80,9 @@ pub fn derive_component_with_ast( } } + let namespace_impls = derive_namespace_impls(&attributes.use_namespace, &component_name)?; + item_impls.extend(namespace_impls); + let derived = DerivedComponent { component_struct, consumer_trait, diff --git a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs new file mode 100644 index 00000000..2fa35223 --- /dev/null +++ b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs @@ -0,0 +1,51 @@ +use syn::{Ident, ItemImpl, Type, parse2}; +use quote::{ToTokens, quote}; + +use crate::attributes::UseNamespaceAttribute; + +pub fn derive_namespace_impls( + attributes: &[UseNamespaceAttribute], + component_name: &Ident, +) -> syn::Result> { + let mut out = Vec::new(); + + for attribute in attributes { + out.push(derive_namespace_impl(attribute, component_name)?); + } + + Ok(out) +} + +pub fn derive_namespace_impl( + attribute: &UseNamespaceAttribute, + component_name: &Ident, +) -> syn::Result { + let namespace = &attribute.namespace; + let mut paths = Vec::from_iter(attribute.path.iter().map(Clone::clone)); + paths.push(parse2(component_name.to_token_stream())?); + + let path = path_to_product(&paths)?; + + let out = quote! { + impl<__Components__> #namespace < __Components__ > for #component_name + { + type Provider = RedirectLookup< #path, __Components__ >; + } + }; + + parse2(out) +} + +pub fn path_to_product( + paths: &[Type], +) -> syn::Result { + let mut out = quote! { ε }; + + for path in paths.iter().rev() { + out = quote! { + π< #path , #out > + }; + } + + parse2(out) +} \ No newline at end of file diff --git a/crates/cgp-macro-lib/src/derive_component/mod.rs b/crates/cgp-macro-lib/src/derive_component/mod.rs index 5a0ba39a..a0712dd7 100644 --- a/crates/cgp-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-macro-lib/src/derive_component/mod.rs @@ -10,6 +10,7 @@ mod provider_trait; mod signature_args; mod use_context_impl; mod use_delegate_impl; +mod derive_namespace; pub use attributes::*; pub use derive::*; diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs index 8d09a160..b2d8f9cb 100644 --- a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -1,9 +1,10 @@ +use cgp::core::component::CoreComponents; use cgp::prelude::*; pub struct MyComponents; #[cgp_component(FooProvider)] -#[use_namespace(DefaultNamespace: MyComponents)] +#[use_namespace(DefaultNamespace: CoreComponents.MyComponents)] pub trait CanDoFoo { fn foo(&self); } From 35a169a50ec10f9acbafa25272531e0f862a87c8 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 22:12:22 +0100 Subject: [PATCH 14/45] Use #[use_namespace] for HasErrorType --- crates/cgp-core/src/prelude.rs | 4 ++-- crates/cgp-error/src/traits/has_error_type.rs | 10 ++-------- .../cgp-macro-lib/src/derive_component/attributes.rs | 5 +++-- .../src/derive_component/derive_namespace.rs | 8 +++----- crates/cgp-macro-lib/src/derive_component/mod.rs | 2 +- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 4ea04769..5157a075 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, UseContext, UseDelegate, - UseFields, WithContext, WithProvider, RedirectLookup, + CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, RedirectLookup, + UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 71fefea9..1ceeb26a 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -5,7 +5,7 @@ use cgp_component::{ WithProvider, }; use cgp_field::types::*; -use cgp_macro::{Product, cgp_impl, cgp_type}; +use cgp_macro::{cgp_impl, cgp_type}; use cgp_type::{TypeProvider, UseType}; use crate::ErrorComponents; @@ -30,19 +30,13 @@ use crate::ErrorComponents; */ #[cgp_type] +#[use_namespace(DefaultNamespace: CoreComponents.ErrorComponents)] pub trait HasErrorType { type Error: Debug; } pub type ErrorOf = ::Error; -impl DefaultNamespace for ErrorTypeProviderComponent { - type Provider = RedirectLookup< - Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent], - Components, - >; -} - #[cgp_impl(RedirectLookup)] #[use_provider(Components::Delegate: ErrorTypeProvider)] impl ErrorTypeProvider diff --git a/crates/cgp-macro-lib/src/derive_component/attributes.rs b/crates/cgp-macro-lib/src/derive_component/attributes.rs index 2a156d64..1ddee85d 100644 --- a/crates/cgp-macro-lib/src/derive_component/attributes.rs +++ b/crates/cgp-macro-lib/src/derive_component/attributes.rs @@ -37,8 +37,9 @@ pub fn parse_component_attributes( parsed_attributes.use_type.extend(use_type_specs); } else if ident == "use_namespace" { - let use_namespace_specs = attribute - .parse_args_with(Punctuated::::parse_terminated)?; + let use_namespace_specs = attribute.parse_args_with( + Punctuated::::parse_terminated, + )?; parsed_attributes.use_namespace.extend(use_namespace_specs); } else { attributes.push(attribute); diff --git a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs index 2fa35223..53e990ce 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs @@ -1,5 +1,5 @@ -use syn::{Ident, ItemImpl, Type, parse2}; use quote::{ToTokens, quote}; +use syn::{Ident, ItemImpl, Type, parse2}; use crate::attributes::UseNamespaceAttribute; @@ -36,9 +36,7 @@ pub fn derive_namespace_impl( parse2(out) } -pub fn path_to_product( - paths: &[Type], -) -> syn::Result { +pub fn path_to_product(paths: &[Type]) -> syn::Result { let mut out = quote! { ε }; for path in paths.iter().rev() { @@ -48,4 +46,4 @@ pub fn path_to_product( } parse2(out) -} \ No newline at end of file +} diff --git a/crates/cgp-macro-lib/src/derive_component/mod.rs b/crates/cgp-macro-lib/src/derive_component/mod.rs index a0712dd7..5f60e49e 100644 --- a/crates/cgp-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-macro-lib/src/derive_component/mod.rs @@ -4,13 +4,13 @@ mod consumer_impl; mod delegate_fn; mod delegate_type; mod derive; +mod derive_namespace; mod preprocess; mod provider_impl; mod provider_trait; mod signature_args; mod use_context_impl; mod use_delegate_impl; -mod derive_namespace; pub use attributes::*; pub use derive::*; From 9cc550bb3831043b3cbe38473457ad05afbf6fa5 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 23:10:55 +0100 Subject: [PATCH 15/45] Refactoring derive_provider_impl --- .../cgp-error/src/traits/can_raise_error.rs | 10 ++-- crates/cgp-error/src/traits/has_error_type.rs | 6 +- .../src/derive_component/derive_namespace.rs | 2 +- .../derive_redirect_lookup.rs | 57 +++++++++++++++++++ .../cgp-macro-lib/src/derive_component/mod.rs | 1 + .../src/derive_component/provider_impl.rs | 33 ++++++----- crates/cgp-tests/src/lib.rs | 4 +- 7 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index d6e4536f..94f05bc6 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -16,15 +16,15 @@ pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } -#[cgp_impl(RedirectLookup)] +#[cgp_impl(RedirectLookup)] #[use_type(HasErrorType::Error)] -#[use_provider(Delegate: ErrorRaiser)] -impl ErrorRaiser +#[use_provider(Components::Delegate: ErrorRaiser)] +impl ErrorRaiser where Path: AppendProduct, - Components: DelegateComponent, + Components: DelegateComponent, { fn raise_error(error: E) -> Error { - Delegate::raise_error(error) + Components::Delegate::raise_error(error) } } diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 1ceeb26a..c2d838f3 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -37,11 +37,11 @@ pub trait HasErrorType { pub type ErrorOf = ::Error; -#[cgp_impl(RedirectLookup)] +#[cgp_impl(RedirectLookup)] #[use_provider(Components::Delegate: ErrorTypeProvider)] -impl ErrorTypeProvider +impl ErrorTypeProvider where - Components: DelegateComponent, + Components: DelegateComponent, { type Error = >::Error; } diff --git a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs index 53e990ce..91c7e139 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs @@ -29,7 +29,7 @@ pub fn derive_namespace_impl( let out = quote! { impl<__Components__> #namespace < __Components__ > for #component_name { - type Provider = RedirectLookup< #path, __Components__ >; + type Provider = RedirectLookup< __Components__, #path >; } }; diff --git a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs new file mode 100644 index 00000000..35902f46 --- /dev/null +++ b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs @@ -0,0 +1,57 @@ +use quote::quote; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{GenericParam, Generics, Ident, ItemImpl, ItemTrait, Type, parse2}; + +pub fn derive_redirect_lookup_impl( + context_type: &Ident, + consumer_trait: &ItemTrait, + provider_trait: &ItemTrait, +) -> syn::Result { + let generic_params = extract_type_generics(&provider_trait.generics)?; + + let mut impl_generics = provider_trait.generics.clone(); + + impl_generics + .params + .push(parse2(quote! { __Components__ })?); + impl_generics.params.push(parse2(quote! { __Path__ })?); + + if let Some(generic_params) = generic_params { + let where_clause = impl_generics.make_where_clause(); + where_clause.predicates.push(parse2(quote! { + __Path__: AppendProduct< ( #generic_params ) > + })?); + + where_clause.predicates.push(parse2(quote! { + __Components__: DelegateComponent<<__Path__ as AppendProduct< ( #generic_params ) >>::Output>, + })?); + } else { + let where_clause = impl_generics.make_where_clause(); + where_clause.predicates.push(parse2(quote! { + __Components__: DelegateComponent<__Path__>, + })?); + } + + todo!() +} + +pub fn extract_type_generics(generics: &Generics) -> syn::Result> { + let mut params = Punctuated::::new(); + + for param in generics.params.iter() { + if let GenericParam::Type(type_param) = param { + params.push(type_param.ident.clone()); + } + } + + if params.is_empty() { + Ok(None) + } else { + let params = parse2(quote! { + ( #params ) + })?; + + Ok(Some(params)) + } +} diff --git a/crates/cgp-macro-lib/src/derive_component/mod.rs b/crates/cgp-macro-lib/src/derive_component/mod.rs index 5f60e49e..24f59d93 100644 --- a/crates/cgp-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-macro-lib/src/derive_component/mod.rs @@ -5,6 +5,7 @@ mod delegate_fn; mod delegate_type; mod derive; mod derive_namespace; +mod derive_redirect_lookup; mod preprocess; mod provider_impl; mod provider_trait; diff --git a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs index 09e9ce33..d2a610db 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs @@ -24,7 +24,15 @@ pub fn derive_provider_impl( ) -> syn::Result { let provider_name = &provider_trait.ident; - let component_type = Ident::new("__Component__", Span::call_site()); + let provider_type = Ident::new("__Provider__", Span::call_site()); + + let delegate_constraint = quote! { + DelegateComponent< #component_name < #component_params > > + }; + + let delegate_type = quote! { + < #provider_type as #delegate_constraint > :: Delegate + }; let provider_generic_args = TypeGenerics::try_from(&provider_trait.generics)? .generics @@ -35,18 +43,16 @@ pub fn derive_provider_impl( impl_generics .params - .insert(0, parse2(quote!(#component_type))?); + .insert(0, parse2(quote!(#provider_type))?); { let is_provider_params = parse_is_provider_params(&consumer_trait.generics)?; - let mut delegate_constraint: Punctuated = Punctuated::default(); + let mut delegate_constraints: Punctuated = Punctuated::default(); - delegate_constraint.push(parse2(quote! { - DelegateComponent< #component_name < #component_params > > - })?); + delegate_constraints.push(parse2(delegate_constraint)?); - delegate_constraint.push(parse2(quote!( + delegate_constraints.push(parse2(quote!( IsProviderFor< #component_name < #component_params >, #context_type, ( #is_provider_params ) > ))?); @@ -57,11 +63,11 @@ pub fn derive_provider_impl( let where_clause = impl_generics.make_where_clause(); where_clause.predicates.push(parse2(quote! { - #component_type : #delegate_constraint + #provider_type : #delegate_constraints })?); where_clause.predicates.push(parse2(quote! { - #component_type :: Delegate : #provider_constraint + #provider_type :: Delegate : #provider_constraint })?); } @@ -73,8 +79,7 @@ pub fn derive_provider_impl( for trait_item in provider_trait.items.iter() { match &trait_item { TraitItem::Fn(trait_fn) => { - let impl_fn = - derive_delegated_fn_impl(&trait_fn.sig, "e!(#component_type :: Delegate))?; + let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, &delegate_type)?; impl_items.push(ImplItem::Fn(impl_fn)) } @@ -97,7 +102,7 @@ pub fn derive_provider_impl( let impl_type = derive_delegate_type_impl( trait_type, parse2(quote!( - < #component_type :: Delegate as #provider_name < #provider_generic_args > > :: #type_name #type_generics + < #delegate_type as #provider_name < #provider_generic_args > > :: #type_name #type_generics ))?, ); @@ -108,7 +113,7 @@ pub fn derive_provider_impl( let (_, type_generics, _) = trait_item_const.generics.split_for_impl(); let impl_expr = parse2(quote! { - < #component_type :: Delegate as #provider_name < #provider_generic_args > > :: #const_ident #type_generics + < #delegate_type as #provider_name < #provider_generic_args > > :: #const_ident #type_generics })?; let impl_item_const = ImplItemConst { @@ -145,7 +150,7 @@ pub fn derive_provider_impl( impl_token: Impl::default(), generics: impl_generics, trait_: Some((None, trait_path, For::default())), - self_ty: Box::new(parse2(quote!(#component_type))?), + self_ty: Box::new(parse2(quote!(#provider_type))?), brace_token: Brace::default(), items: impl_items, }; diff --git a/crates/cgp-tests/src/lib.rs b/crates/cgp-tests/src/lib.rs index c36ee9b9..7c489781 100644 --- a/crates/cgp-tests/src/lib.rs +++ b/crates/cgp-tests/src/lib.rs @@ -22,12 +22,12 @@ where } impl ExtendedNamespace for ErrorRaiserComponent { - type Provider = RedirectLookup; + type Provider = RedirectLookup; } impl ExtendedNamespace for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] { type Provider = - RedirectLookup; + RedirectLookup; } From d6c52c6e588d8b5e2ff4c39530a6d872c6d09d7a Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 23:24:06 +0100 Subject: [PATCH 16/45] Draft implement derive_redirect_lookup_impl --- .../derive_redirect_lookup.rs | 62 +++++++++++++++---- .../src/derive_component/provider_impl.rs | 56 ++++++++++------- 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs index 35902f46..03307260 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs @@ -1,13 +1,18 @@ use quote::quote; use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::{GenericParam, Generics, Ident, ItemImpl, ItemTrait, Type, parse2}; +use syn::token::{Brace, Comma, For, Impl}; +use syn::{GenericParam, Generics, Ident, ItemImpl, ItemTrait, Path, Type, parse2}; + +use crate::derive_component::provider_impl::derive_provider_item_impls; pub fn derive_redirect_lookup_impl( context_type: &Ident, consumer_trait: &ItemTrait, provider_trait: &ItemTrait, ) -> syn::Result { + let provider_name = &provider_trait.ident; + let provider_type_generics = provider_trait.generics.split_for_impl().1; + let generic_params = extract_type_generics(&provider_trait.generics)?; let mut impl_generics = provider_trait.generics.clone(); @@ -15,25 +20,56 @@ pub fn derive_redirect_lookup_impl( impl_generics .params .push(parse2(quote! { __Components__ })?); + impl_generics.params.push(parse2(quote! { __Path__ })?); - if let Some(generic_params) = generic_params { - let where_clause = impl_generics.make_where_clause(); + let where_clause = impl_generics.make_where_clause(); + + let delegate_constraint = if let Some(generic_params) = &generic_params { where_clause.predicates.push(parse2(quote! { __Path__: AppendProduct< ( #generic_params ) > })?); - where_clause.predicates.push(parse2(quote! { - __Components__: DelegateComponent<<__Path__ as AppendProduct< ( #generic_params ) >>::Output>, - })?); + quote! { + DelegateComponent<<__Path__ as AppendProduct< ( #generic_params ) >>::Output> + } } else { - let where_clause = impl_generics.make_where_clause(); - where_clause.predicates.push(parse2(quote! { - __Components__: DelegateComponent<__Path__>, - })?); - } + quote! { + DelegateComponent<__Path__> + } + }; + + where_clause.predicates.push(parse2(quote! { + __Components__: #delegate_constraint, + })?); + + let delegate_type = quote! { + < __Components__ as #delegate_constraint > :: Delegate + }; + + where_clause.predicates.push(parse2(quote! { + #delegate_type : #provider_name #provider_type_generics + })?); + + let impl_items = derive_provider_item_impls(provider_trait, &delegate_type)?; + + let self_type = parse2(quote!(RedirectLookup<__Components__, __Path__>))?; + + let trait_path: Path = parse2(quote!( #provider_name #provider_type_generics ))?; + + let item = ItemImpl { + attrs: provider_trait.attrs.clone(), + defaultness: None, + unsafety: provider_trait.unsafety, + impl_token: Impl::default(), + generics: impl_generics, + trait_: Some((None, trait_path, For::default())), + self_ty: Box::new(self_type), + brace_token: Brace::default(), + items: impl_items, + }; - todo!() + Ok(item) } pub fn extract_type_generics(generics: &Generics) -> syn::Result> { diff --git a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs index d2a610db..2ee9afe6 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; use alloc::vec::Vec; -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::punctuated::Punctuated; use syn::spanned::Spanned; @@ -34,9 +34,7 @@ pub fn derive_provider_impl( < #provider_type as #delegate_constraint > :: Delegate }; - let provider_generic_args = TypeGenerics::try_from(&provider_trait.generics)? - .generics - .params; + let provider_type_generics = provider_trait.generics.split_for_impl().1; let impl_generics = { let mut impl_generics = provider_trait.generics.clone(); @@ -57,7 +55,7 @@ pub fn derive_provider_impl( ))?); let provider_constraint: TypeParamBound = parse2(quote! { - #provider_name < #provider_generic_args > + #provider_name #provider_type_generics })?; let where_clause = impl_generics.make_where_clause(); @@ -67,13 +65,39 @@ pub fn derive_provider_impl( })?); where_clause.predicates.push(parse2(quote! { - #provider_type :: Delegate : #provider_constraint + #delegate_type : #provider_constraint })?); } impl_generics }; + let impl_items = derive_provider_item_impls(provider_trait, &delegate_type)?; + + let trait_path: Path = parse2(quote!( #provider_name #provider_type_generics ))?; + + let item = ItemImpl { + attrs: provider_trait.attrs.clone(), + defaultness: None, + unsafety: provider_trait.unsafety, + impl_token: Impl::default(), + generics: impl_generics, + trait_: Some((None, trait_path, For::default())), + self_ty: Box::new(parse2(quote!(#provider_type))?), + brace_token: Brace::default(), + items: impl_items, + }; + + Ok(item) +} + +pub fn derive_provider_item_impls( + provider_trait: &ItemTrait, + delegate_type: &TokenStream, +) -> syn::Result> { + let provider_name = &provider_trait.ident; + let provider_type_generics = provider_trait.generics.split_for_impl().1; + let mut impl_items: Vec = Vec::new(); for trait_item in provider_trait.items.iter() { @@ -102,7 +126,7 @@ pub fn derive_provider_impl( let impl_type = derive_delegate_type_impl( trait_type, parse2(quote!( - < #delegate_type as #provider_name < #provider_generic_args > > :: #type_name #type_generics + < #delegate_type as #provider_name #provider_type_generics > :: #type_name #type_generics ))?, ); @@ -113,7 +137,7 @@ pub fn derive_provider_impl( let (_, type_generics, _) = trait_item_const.generics.split_for_impl(); let impl_expr = parse2(quote! { - < #delegate_type as #provider_name < #provider_generic_args > > :: #const_ident #type_generics + < #delegate_type as #provider_name #provider_type_generics > :: #const_ident #type_generics })?; let impl_item_const = ImplItemConst { @@ -141,19 +165,5 @@ pub fn derive_provider_impl( } } - let trait_path: Path = parse2(quote!( #provider_name < #provider_generic_args > ))?; - - let item = ItemImpl { - attrs: provider_trait.attrs.clone(), - defaultness: None, - unsafety: provider_trait.unsafety, - impl_token: Impl::default(), - generics: impl_generics, - trait_: Some((None, trait_path, For::default())), - self_ty: Box::new(parse2(quote!(#provider_type))?), - brace_token: Brace::default(), - items: impl_items, - }; - - Ok(item) + Ok(impl_items) } From baaa8b8d054632caa8cc9cfcb5877fe368b1c0d9 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 11 Mar 2026 23:46:02 +0100 Subject: [PATCH 17/45] Derive RedirectLookup in #[cgp_component] --- crates/cgp-component/src/lib.rs | 2 +- .../src/traits/append_product.rs | 3 +++ crates/cgp-component/src/traits/mod.rs | 2 ++ crates/cgp-core/src/prelude.rs | 4 ++-- .../cgp-error/src/traits/can_raise_error.rs | 20 ++++--------------- crates/cgp-error/src/traits/can_wrap_error.rs | 4 +++- crates/cgp-error/src/traits/has_error_type.rs | 11 +--------- crates/cgp-field/src/traits/append_product.rs | 6 ++---- crates/cgp-field/src/traits/mod.rs | 1 - .../src/derive_component/derive.rs | 11 ++++++++++ .../derive_redirect_lookup.rs | 9 ++++----- .../src/derive_component/provider_impl.rs | 2 +- crates/cgp-tests/tests/redirect.rs | 15 +------------- crates/cgp-type/src/traits/has_type.rs | 4 +++- 14 files changed, 38 insertions(+), 56 deletions(-) create mode 100644 crates/cgp-component/src/traits/append_product.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index e279ea40..db692a36 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -10,7 +10,7 @@ mod traits; mod types; pub use namespaces::{CoreComponents, DefaultNamespace}; -pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; +pub use traits::{AppendProduct, CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ HasDelegate, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; diff --git a/crates/cgp-component/src/traits/append_product.rs b/crates/cgp-component/src/traits/append_product.rs new file mode 100644 index 00000000..e6baa88f --- /dev/null +++ b/crates/cgp-component/src/traits/append_product.rs @@ -0,0 +1,3 @@ +pub trait AppendProduct { + type Output; +} diff --git a/crates/cgp-component/src/traits/mod.rs b/crates/cgp-component/src/traits/mod.rs index 1e5f1a04..ec1b65a7 100644 --- a/crates/cgp-component/src/traits/mod.rs +++ b/crates/cgp-component/src/traits/mod.rs @@ -1,7 +1,9 @@ +mod append_product; mod can_use_component; mod delegate_component; mod is_provider; +pub use append_product::*; pub use can_use_component::*; pub use delegate_component::DelegateComponent; pub use is_provider::*; diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 5157a075..2e0043aa 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, RedirectLookup, - UseContext, UseDelegate, UseFields, WithContext, WithProvider, + AppendProduct, CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, + RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index 94f05bc6..2fa2da94 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -1,6 +1,7 @@ -use cgp_component::{DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate}; -use cgp_field::traits::AppendProduct; -use cgp_macro::{cgp_component, cgp_impl}; +use cgp_component::{ + AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, +}; +use cgp_macro::cgp_component; use crate::traits::has_error_type::HasErrorType; @@ -15,16 +16,3 @@ use crate::traits::has_error_type::HasErrorType; pub trait CanRaiseError: HasErrorType { fn raise_error(error: SourceError) -> Self::Error; } - -#[cgp_impl(RedirectLookup)] -#[use_type(HasErrorType::Error)] -#[use_provider(Components::Delegate: ErrorRaiser)] -impl ErrorRaiser -where - Path: AppendProduct, - Components: DelegateComponent, -{ - fn raise_error(error: E) -> Error { - Components::Delegate::raise_error(error) - } -} diff --git a/crates/cgp-error/src/traits/can_wrap_error.rs b/crates/cgp-error/src/traits/can_wrap_error.rs index 7dfdf0de..49452566 100644 --- a/crates/cgp-error/src/traits/can_wrap_error.rs +++ b/crates/cgp-error/src/traits/can_wrap_error.rs @@ -1,4 +1,6 @@ -use cgp_component::{DelegateComponent, IsProviderFor, UseContext, UseDelegate}; +use cgp_component::{ + AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, +}; use cgp_macro::cgp_component; use crate::traits::HasErrorType; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index c2d838f3..23f0b604 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -5,7 +5,7 @@ use cgp_component::{ WithProvider, }; use cgp_field::types::*; -use cgp_macro::{cgp_impl, cgp_type}; +use cgp_macro::cgp_type; use cgp_type::{TypeProvider, UseType}; use crate::ErrorComponents; @@ -36,12 +36,3 @@ pub trait HasErrorType { } pub type ErrorOf = ::Error; - -#[cgp_impl(RedirectLookup)] -#[use_provider(Components::Delegate: ErrorTypeProvider)] -impl ErrorTypeProvider -where - Components: DelegateComponent, -{ - type Error = >::Error; -} diff --git a/crates/cgp-field/src/traits/append_product.rs b/crates/cgp-field/src/traits/append_product.rs index c7b465cf..320b8600 100644 --- a/crates/cgp-field/src/traits/append_product.rs +++ b/crates/cgp-field/src/traits/append_product.rs @@ -1,8 +1,6 @@ -use crate::types::{Cons, Nil}; +use cgp_component::AppendProduct; -pub trait AppendProduct { - type Output; -} +use crate::types::{Cons, Nil}; impl AppendProduct for Nil { type Output = Cons; diff --git a/crates/cgp-field/src/traits/mod.rs b/crates/cgp-field/src/traits/mod.rs index 63d13dc7..3f377848 100644 --- a/crates/cgp-field/src/traits/mod.rs +++ b/crates/cgp-field/src/traits/mod.rs @@ -20,7 +20,6 @@ mod to_fields; mod transform_map; mod update_field; -pub use append_product::*; pub use build_field::*; pub use concat_product::*; pub use extract_field::*; diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index ebf3579d..3f654ae5 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -6,6 +6,7 @@ use crate::derive_component::attributes::parse_component_attributes; use crate::derive_component::component_name::derive_component_name_struct; use crate::derive_component::consumer_impl::derive_consumer_impl; use crate::derive_component::derive_namespace::derive_namespace_impls; +use crate::derive_component::derive_redirect_lookup::derive_redirect_lookup_impl; use crate::derive_component::preprocess_consumer_trait; use crate::derive_component::provider_impl::derive_provider_impl; use crate::derive_component::provider_trait::derive_provider_trait; @@ -57,11 +58,21 @@ pub fn derive_component_with_ast( &use_context_impl, )?; + let redirect_lookup_impl = derive_redirect_lookup_impl(&consumer_trait, &provider_trait)?; + let redirect_lookup_is_provider_impl = derive_is_provider_for( + &parse2(quote! { + #component_name < #component_params > + })?, + &redirect_lookup_impl, + )?; + let mut item_impls = vec![ provider_impl, consumer_impl, use_context_impl, use_context_is_provider_impl, + redirect_lookup_impl, + redirect_lookup_is_provider_impl, ]; if !spec.use_delegate_spec.is_empty() { diff --git a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs index 03307260..9000a92f 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs @@ -6,14 +6,13 @@ use syn::{GenericParam, Generics, Ident, ItemImpl, ItemTrait, Path, Type, parse2 use crate::derive_component::provider_impl::derive_provider_item_impls; pub fn derive_redirect_lookup_impl( - context_type: &Ident, consumer_trait: &ItemTrait, provider_trait: &ItemTrait, ) -> syn::Result { let provider_name = &provider_trait.ident; let provider_type_generics = provider_trait.generics.split_for_impl().1; - let generic_params = extract_type_generics(&provider_trait.generics)?; + let generic_params = extract_type_generics(&consumer_trait.generics)?; let mut impl_generics = provider_trait.generics.clone(); @@ -27,11 +26,11 @@ pub fn derive_redirect_lookup_impl( let delegate_constraint = if let Some(generic_params) = &generic_params { where_clause.predicates.push(parse2(quote! { - __Path__: AppendProduct< ( #generic_params ) > + __Path__: AppendProduct< #generic_params > })?); quote! { - DelegateComponent<<__Path__ as AppendProduct< ( #generic_params ) >>::Output> + DelegateComponent<<__Path__ as AppendProduct< #generic_params >>::Output> } } else { quote! { @@ -40,7 +39,7 @@ pub fn derive_redirect_lookup_impl( }; where_clause.predicates.push(parse2(quote! { - __Components__: #delegate_constraint, + __Components__: #delegate_constraint })?); let delegate_type = quote! { diff --git a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs index 2ee9afe6..0a6a474f 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs @@ -13,7 +13,7 @@ use syn::{ use crate::derive_component::delegate_fn::derive_delegated_fn_impl; use crate::derive_component::delegate_type::derive_delegate_type_impl; -use crate::parse::{TypeGenerics, parse_is_provider_params}; +use crate::parse::parse_is_provider_params; pub fn derive_provider_impl( context_type: &Ident, diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/redirect.rs index 7d128a2b..d4654a6c 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/redirect.rs @@ -4,8 +4,6 @@ pub trait HasNamespace {} pub struct UseNamespace(pub PhantomData); -pub struct RedirectLookup(pub PhantomData<(Key, Components)>); - #[cgp_component(FooProvider)] pub trait CanDoFoo { fn foo(); @@ -13,20 +11,9 @@ pub trait CanDoFoo { impl HasNamespace for FooProviderComponent {} -#[cgp_impl(RedirectLookup)] -#[use_provider(Components::Delegate: FooProvider)] -impl FooProvider -where - Components: DelegateComponent, -{ - fn foo() { - Components::Delegate::foo(); - } -} - delegate_components! { UseNamespace { - FooProviderComponent: RedirectLookup, + FooProviderComponent: RedirectLookup, } } diff --git a/crates/cgp-type/src/traits/has_type.rs b/crates/cgp-type/src/traits/has_type.rs index da5a4aad..66166cdd 100644 --- a/crates/cgp-type/src/traits/has_type.rs +++ b/crates/cgp-type/src/traits/has_type.rs @@ -1,4 +1,6 @@ -use cgp_component::{DelegateComponent, IsProviderFor, UseContext, UseDelegate}; +use cgp_component::{ + AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, +}; use cgp_macro::cgp_component; #[cgp_component { From 8dfa03ef5dd56c28bebfc718c4b76d04dff2ec31 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 21:28:59 +0100 Subject: [PATCH 18/45] Move ExtendedNamespace --- crates/cgp-component/src/lib.rs | 3 +- crates/cgp-component/src/types/mod.rs | 2 ++ crates/cgp-component/src/types/use_default.rs | 1 + crates/cgp-tests/src/lib.rs | 31 +------------------ crates/cgp-tests/src/namespaces/extend.rs | 30 ++++++++++++++++++ crates/cgp-tests/src/namespaces/mod.rs | 3 ++ .../tests/namespace_tests/experiments.rs | 2 +- 7 files changed, 40 insertions(+), 32 deletions(-) create mode 100644 crates/cgp-component/src/types/use_default.rs create mode 100644 crates/cgp-tests/src/namespaces/extend.rs create mode 100644 crates/cgp-tests/src/namespaces/mod.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index db692a36..935914c1 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -12,5 +12,6 @@ mod types; pub use namespaces::{CoreComponents, DefaultNamespace}; pub use traits::{AppendProduct, CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ - HasDelegate, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, + HasDelegate, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, + WithProvider, }; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index 9864c427..2ef16542 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -1,11 +1,13 @@ mod redirect_lookup; mod use_context; +mod use_default; mod use_delegate; mod use_fields; mod with_provider; pub use redirect_lookup::{HasDelegate, RedirectLookup}; pub use use_context::{UseContext, WithContext}; +pub use use_default::UseDefault; pub use use_delegate::UseDelegate; pub use use_fields::UseFields; pub use with_provider::WithProvider; diff --git a/crates/cgp-component/src/types/use_default.rs b/crates/cgp-component/src/types/use_default.rs new file mode 100644 index 00000000..f6a2e318 --- /dev/null +++ b/crates/cgp-component/src/types/use_default.rs @@ -0,0 +1 @@ +pub struct UseDefault; diff --git a/crates/cgp-tests/src/lib.rs b/crates/cgp-tests/src/lib.rs index 7c489781..3c4e0340 100644 --- a/crates/cgp-tests/src/lib.rs +++ b/crates/cgp-tests/src/lib.rs @@ -1,33 +1,4 @@ #[cfg(test)] pub mod tests; -use cgp::core::component::{CoreComponents, RedirectLookup}; -use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; -use cgp::prelude::*; - -pub trait ExtendedNamespace { - type Provider; -} - -pub struct ExtendedNamespaceComponents; - -pub struct MyErrorComponents; - -impl ExtendedNamespace for Component -where - Component: DefaultNamespace - + DefaultNamespace, -{ - type Provider = Provider; -} - -impl ExtendedNamespace for ErrorRaiserComponent { - type Provider = RedirectLookup; -} - -impl ExtendedNamespace - for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] -{ - type Provider = - RedirectLookup; -} +pub mod namespaces; diff --git a/crates/cgp-tests/src/namespaces/extend.rs b/crates/cgp-tests/src/namespaces/extend.rs new file mode 100644 index 00000000..16d1c135 --- /dev/null +++ b/crates/cgp-tests/src/namespaces/extend.rs @@ -0,0 +1,30 @@ +use cgp::core::component::{CoreComponents, RedirectLookup}; +use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::prelude::*; + +pub trait ExtendedNamespace { + type Provider; +} + +pub struct ExtendedNamespaceComponents; + +pub struct MyErrorComponents; + +impl ExtendedNamespace for Component +where + Component: DefaultNamespace + + DefaultNamespace, +{ + type Provider = Provider; +} + +impl ExtendedNamespace for ErrorRaiserComponent { + type Provider = RedirectLookup; +} + +impl ExtendedNamespace + for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] +{ + type Provider = + RedirectLookup; +} diff --git a/crates/cgp-tests/src/namespaces/mod.rs b/crates/cgp-tests/src/namespaces/mod.rs new file mode 100644 index 00000000..d5e97d22 --- /dev/null +++ b/crates/cgp-tests/src/namespaces/mod.rs @@ -0,0 +1,3 @@ +mod extend; + +pub use extend::*; diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index fc2fb289..188e03b0 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -2,7 +2,7 @@ use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; -use cgp_tests::{ExtendedNamespace, MyErrorComponents}; +use cgp_tests::namespaces::{ExtendedNamespace, MyErrorComponents}; pub struct App; From 0b3f995c83f8cc85382be4a698b9e639117bf4e1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 22:25:59 +0100 Subject: [PATCH 19/45] Experimenting with default provider --- crates/cgp-component/src/lib.rs | 5 ++--- crates/cgp-component/src/namespaces.rs | 8 ++++++-- crates/cgp-component/src/types/mod.rs | 2 +- .../cgp-component/src/types/redirect_lookup.rs | 13 ------------- crates/cgp-core/src/prelude.rs | 4 ++-- crates/cgp-error/src/traits/has_error_type.rs | 4 ++-- crates/cgp-tests/src/namespaces/extend.rs | 8 ++++---- .../tests/namespace_tests/use_namespace.rs | 16 ++++++++++++++-- 8 files changed, 31 insertions(+), 29 deletions(-) diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 935914c1..d6ed086b 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -9,9 +9,8 @@ mod namespaces; mod traits; mod types; -pub use namespaces::{CoreComponents, DefaultNamespace}; +pub use namespaces::{CgpCore, CgpNamespace, DefaultComponentsNamespace}; pub use traits::{AppendProduct, CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ - HasDelegate, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, - WithProvider, + RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, WithProvider, }; diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs index 4e20e742..d216c394 100644 --- a/crates/cgp-component/src/namespaces.rs +++ b/crates/cgp-component/src/namespaces.rs @@ -1,5 +1,9 @@ -pub trait DefaultNamespace { +pub trait CgpNamespace { type Provider; } -pub struct CoreComponents; +pub trait DefaultComponentsNamespace { + type Provider; +} + +pub struct CgpCore; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index 2ef16542..1e07f782 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -5,7 +5,7 @@ mod use_delegate; mod use_fields; mod with_provider; -pub use redirect_lookup::{HasDelegate, RedirectLookup}; +pub use redirect_lookup::RedirectLookup; pub use use_context::{UseContext, WithContext}; pub use use_default::UseDefault; pub use use_delegate::UseDelegate; diff --git a/crates/cgp-component/src/types/redirect_lookup.rs b/crates/cgp-component/src/types/redirect_lookup.rs index 9b9a3367..dbc117e5 100644 --- a/crates/cgp-component/src/types/redirect_lookup.rs +++ b/crates/cgp-component/src/types/redirect_lookup.rs @@ -1,16 +1,3 @@ use core::marker::PhantomData; -use crate::DelegateComponent; - pub struct RedirectLookup(pub PhantomData<(Key, Components)>); - -pub trait HasDelegate { - type Delegate; -} - -impl HasDelegate for Path -where - Components: DelegateComponent, -{ - type Delegate = Components::Delegate; -} diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 2e0043aa..8a09fbeb 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - AppendProduct, CanUseComponent, DefaultNamespace, DelegateComponent, IsProviderFor, - RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, + AppendProduct, CanUseComponent, CgpNamespace, DelegateComponent, IsProviderFor, RedirectLookup, + UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 23f0b604..f9d11dd2 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; use cgp_component::{ - CoreComponents, DefaultNamespace, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, + CgpCore, CgpNamespace, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, WithProvider, }; use cgp_field::types::*; @@ -30,7 +30,7 @@ use crate::ErrorComponents; */ #[cgp_type] -#[use_namespace(DefaultNamespace: CoreComponents.ErrorComponents)] +#[use_namespace(CgpNamespace: CgpCore.ErrorComponents)] pub trait HasErrorType { type Error: Debug; } diff --git a/crates/cgp-tests/src/namespaces/extend.rs b/crates/cgp-tests/src/namespaces/extend.rs index 16d1c135..8f4fcafc 100644 --- a/crates/cgp-tests/src/namespaces/extend.rs +++ b/crates/cgp-tests/src/namespaces/extend.rs @@ -1,4 +1,4 @@ -use cgp::core::component::{CoreComponents, RedirectLookup}; +use cgp::core::component::{CgpCore, RedirectLookup}; use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::prelude::*; @@ -12,8 +12,8 @@ pub struct MyErrorComponents; impl ExtendedNamespace for Component where - Component: DefaultNamespace - + DefaultNamespace, + Component: + CgpNamespace + CgpNamespace, { type Provider = Provider; } @@ -23,7 +23,7 @@ impl ExtendedNamespace for ErrorRaiserComponent { } impl ExtendedNamespace - for Product![CoreComponents, ErrorComponents, ErrorTypeProviderComponent] + for Product![CgpCore, ErrorComponents, ErrorTypeProviderComponent] { type Provider = RedirectLookup; diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs index b2d8f9cb..1bf89e9b 100644 --- a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -1,10 +1,22 @@ -use cgp::core::component::CoreComponents; +use cgp::core::component::{CgpCore, DefaultComponentsNamespace}; +use cgp::core::error::ErrorTypeProviderComponent; use cgp::prelude::*; pub struct MyComponents; #[cgp_component(FooProvider)] -#[use_namespace(DefaultNamespace: CoreComponents.MyComponents)] +#[use_namespace(CgpNamespace: CgpCore.MyComponents)] pub trait CanDoFoo { fn foo(&self); } + +pub struct App; + +delegate_components! { + App { + > + Component: Component::Provider, + ErrorTypeProviderComponent: + UseType, + } +} From e04f87db37b5620b0317043cc8b8c2f728a1f113 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 22:29:45 +0100 Subject: [PATCH 20/45] Allow namespace trait to be omitted --- crates/cgp-error/src/traits/has_error_type.rs | 2 +- crates/cgp-macro-lib/src/attributes/use_namespace.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index f9d11dd2..758be7d2 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -30,7 +30,7 @@ use crate::ErrorComponents; */ #[cgp_type] -#[use_namespace(CgpNamespace: CgpCore.ErrorComponents)] +#[use_namespace(CgpCore.ErrorComponents)] pub trait HasErrorType { type Error: Debug; } diff --git a/crates/cgp-macro-lib/src/attributes/use_namespace.rs b/crates/cgp-macro-lib/src/attributes/use_namespace.rs index 6a593ac3..5c6b013d 100644 --- a/crates/cgp-macro-lib/src/attributes/use_namespace.rs +++ b/crates/cgp-macro-lib/src/attributes/use_namespace.rs @@ -10,8 +10,13 @@ pub struct UseNamespaceAttribute { impl Parse for UseNamespaceAttribute { fn parse(input: ParseStream) -> syn::Result { - let namespace = input.parse()?; - let _: Colon = input.parse()?; + let namespace = if input.peek2(Colon) { + let namespace = input.parse()?; + let _: Colon = input.parse()?; + namespace + } else { + Ident::new("CgpNamespace", input.span()) + }; let path = Punctuated::parse_separated_nonempty(input)?; Ok(UseNamespaceAttribute { namespace, path }) From 72702bdb7647fcb4a01c3edcf447887ddacc171c Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 22:56:40 +0100 Subject: [PATCH 21/45] Draft implement component path parsing --- .../src/parse/delegate_components.rs | 119 +++++++++++++++--- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index acd18e30..4435ac1b 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -5,8 +5,9 @@ use quote::{ToTokens, TokenStreamExt, quote}; use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::token::{Bracket, Colon, Comma, Gt, Lt, RArrow}; -use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote}; +use syn::spanned::Spanned; +use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, RArrow, Star}; +use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; use crate::parse::{ImplGenerics, TypeGenerics}; @@ -138,18 +139,38 @@ where Type: Parse, { fn parse(input: ParseStream) -> syn::Result { - let component_generics = if input.peek(Lt) { + let mut component_generics: ImplGenerics = if input.peek(Lt) { input.parse()? } else { Default::default() }; - let component_type: Type = input.parse()?; + if input.peek(At) { + let _: At = input.parse()?; - Ok(Self { - ty: component_type, - generics: component_generics, - }) + let path: ComponentPath = input.parse()?; + + if path.wildcard { + component_generics + .generics + .params + .push(parse_quote!( __Wildcard__: ?Sized )); + } + + let path_type = parse2(path.to_type())?; + + Ok(Self { + ty: path_type, + generics: component_generics, + }) + } else { + let component_type: Type = input.parse()?; + + Ok(Self { + ty: component_type, + generics: component_generics, + }) + } } } @@ -167,13 +188,13 @@ impl Parse for DelegateValue { fn parse(input: ParseStream) -> syn::Result { let fork = input.fork(); - match fork.parse::() { - Ok(value) => { - input.advance_to(&fork); - Ok(Self::New(value)) - } - _ => Ok(Self::Type(input.parse()?)), + if let Ok(value) = fork.parse::() { + input.advance_to(&fork); + return Ok(Self::New(value)); } + + let ty: Type = input.parse()?; + Ok(Self::Type(ty)) } } @@ -283,3 +304,73 @@ impl ToTokens for DelegateNewValue { }); } } + +pub struct ComponentPath { + pub elements: Vec, + pub wildcard: bool, +} + +impl Parse for ComponentPath { + fn parse(input: ParseStream) -> syn::Result { + let raw_elements: Punctuated = + Punctuated::parse_separated_nonempty(input)?; + + let mut elements = Vec::new(); + let mut wildcard = false; + + for element in raw_elements { + match element { + PathElement::Type(ty) => { + if wildcard { + return Err(Error::new(ty.span(), "unexpected component after wildcard")); + } + + elements.push(ty); + } + PathElement::Wildcard => wildcard = true, + } + } + + if elements.is_empty() { + return Err(Error::new( + input.span(), + "expect at least one component in component path", + )); + } + + Ok(Self { elements, wildcard }) + } +} + +impl ComponentPath { + pub fn to_type(&self) -> TokenStream { + let mut out = if self.wildcard { + quote! { __Wildcard__ } + } else { + quote! { ε } + }; + + for element in self.elements.iter().rev() { + out = quote! { π< #element, #out> }; + } + + out + } +} + +pub enum PathElement { + Type(Type), + Wildcard, +} + +impl Parse for PathElement { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Star) { + let _: Star = input.parse()?; + Ok(Self::Wildcard) + } else { + let ty: Type = input.parse()?; + Ok(Self::Type(ty)) + } + } +} From 81bc70b8437ecc5fb5cb37d595ca4d6141cc3b43 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 22:58:24 +0100 Subject: [PATCH 22/45] Use namespace path syntax --- crates/cgp-tests/tests/namespace_tests/experiments.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index 188e03b0..66cb9213 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -10,9 +10,9 @@ delegate_components! { App { > Component: Component::Provider, - Product![MyErrorComponents, ErrorTypeProviderComponent]: + @MyErrorComponents.ErrorTypeProviderComponent: UseType, - Product![MyErrorComponents, ErrorRaiserComponent, &'static str]: + @MyErrorComponents.ErrorRaiserComponent.&'static str: RaiseFrom, TryComputerComponent: Foo, From 60cfd23cc939c5ff368b9fd79c6a166398720df1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 23:02:50 +0100 Subject: [PATCH 23/45] Test wildcard --- crates/cgp-macro-lib/src/parse/delegate_components.rs | 2 +- crates/cgp-tests/tests/namespace_tests/mod.rs | 1 + crates/cgp-tests/tests/{ => namespace_tests}/redirect.rs | 9 +++------ 3 files changed, 5 insertions(+), 7 deletions(-) rename crates/cgp-tests/tests/{ => namespace_tests}/redirect.rs (69%) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 4435ac1b..f72b1fa7 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -154,7 +154,7 @@ where component_generics .generics .params - .push(parse_quote!( __Wildcard__: ?Sized )); + .push(parse_quote!(__Wildcard__)); } let path_type = parse2(path.to_type())?; diff --git a/crates/cgp-tests/tests/namespace_tests/mod.rs b/crates/cgp-tests/tests/namespace_tests/mod.rs index eca1bc43..2a24debe 100644 --- a/crates/cgp-tests/tests/namespace_tests/mod.rs +++ b/crates/cgp-tests/tests/namespace_tests/mod.rs @@ -1,2 +1,3 @@ pub mod experiments; +pub mod redirect; pub mod use_namespace; diff --git a/crates/cgp-tests/tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs similarity index 69% rename from crates/cgp-tests/tests/redirect.rs rename to crates/cgp-tests/tests/namespace_tests/redirect.rs index d4654a6c..5d17008a 100644 --- a/crates/cgp-tests/tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -34,14 +34,11 @@ delegate_components! { > Component: UseNamespace, - // @BarComponent.* : TestProvider, - // Cons: TestProvider, + // @BarComponent.*: TestProvider, - // @BarComponent.BazComponent.* : TestProvider, - // Cons>: TestProvider, + @BarComponent.BazComponent.*: TestProvider, - // @BarComponent.BazComponent.FooProviderComponent : TestProvider, - Product![BarComponent, BazComponent, FooProviderComponent]: TestProvider, + // @BarComponent.BazComponent.FooProviderComponent: TestProvider, // @*.BazComponent.*: TestProvider, // Cons>: TestProvider, From 7a91a134dea5dfa94ecd44fbaac822c287b5b950 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 23:38:12 +0100 Subject: [PATCH 24/45] Use separate PathCons type to allow unsized head --- crates/cgp-component/src/lib.rs | 3 +- crates/cgp-component/src/types/mod.rs | 2 + crates/cgp-component/src/types/path.rs | 20 +++++++++ crates/cgp-core/src/prelude.rs | 5 ++- .../cgp-error/src/traits/can_raise_error.rs | 4 +- crates/cgp-error/src/traits/can_wrap_error.rs | 4 +- crates/cgp-error/src/traits/has_error_type.rs | 5 +-- .../src/derive_component/derive_namespace.rs | 4 +- .../derive_redirect_lookup.rs | 43 +++++++++++-------- .../src/parse/delegate_components.rs | 4 +- crates/cgp-tests/src/namespaces/extend.rs | 13 ++++-- .../tests/namespace_tests/redirect.rs | 2 +- crates/cgp-type/src/traits/has_type.rs | 4 +- 13 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 crates/cgp-component/src/types/path.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index d6ed086b..ff93766e 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -12,5 +12,6 @@ mod types; pub use namespaces::{CgpCore, CgpNamespace, DefaultComponentsNamespace}; pub use traits::{AppendProduct, CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ - RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, WithProvider, + ConcatPath, PathCons, PathNil, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, + WithContext, WithProvider, }; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index 1e07f782..56890656 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -1,3 +1,4 @@ +mod path; mod redirect_lookup; mod use_context; mod use_default; @@ -5,6 +6,7 @@ mod use_delegate; mod use_fields; mod with_provider; +pub use path::{ConcatPath, PathCons, PathNil}; pub use redirect_lookup::RedirectLookup; pub use use_context::{UseContext, WithContext}; pub use use_default::UseDefault; diff --git a/crates/cgp-component/src/types/path.rs b/crates/cgp-component/src/types/path.rs new file mode 100644 index 00000000..c09902af --- /dev/null +++ b/crates/cgp-component/src/types/path.rs @@ -0,0 +1,20 @@ +use core::marker::PhantomData; + +pub struct PathCons(pub PhantomData, pub PhantomData); + +pub struct PathNil; + +pub trait ConcatPath { + type Output: ?Sized; +} + +impl ConcatPath for PathCons +where + Tail: ConcatPath, +{ + type Output = PathCons>::Output>; +} + +impl ConcatPath for PathNil { + type Output = Other; +} diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 8a09fbeb..e638bd7f 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,9 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - AppendProduct, CanUseComponent, CgpNamespace, DelegateComponent, IsProviderFor, RedirectLookup, - UseContext, UseDelegate, UseFields, WithContext, WithProvider, + AppendProduct, CanUseComponent, CgpNamespace, ConcatPath, DelegateComponent, IsProviderFor, + PathCons, PathNil, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, + WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/src/traits/can_raise_error.rs b/crates/cgp-error/src/traits/can_raise_error.rs index 2fa2da94..835592af 100644 --- a/crates/cgp-error/src/traits/can_raise_error.rs +++ b/crates/cgp-error/src/traits/can_raise_error.rs @@ -1,6 +1,4 @@ -use cgp_component::{ - AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, -}; +use cgp_component::*; use cgp_macro::cgp_component; use crate::traits::has_error_type::HasErrorType; diff --git a/crates/cgp-error/src/traits/can_wrap_error.rs b/crates/cgp-error/src/traits/can_wrap_error.rs index 49452566..a28b03a0 100644 --- a/crates/cgp-error/src/traits/can_wrap_error.rs +++ b/crates/cgp-error/src/traits/can_wrap_error.rs @@ -1,6 +1,4 @@ -use cgp_component::{ - AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, -}; +use cgp_component::*; use cgp_macro::cgp_component; use crate::traits::HasErrorType; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index 758be7d2..c4c7b1df 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -1,10 +1,9 @@ use core::fmt::Debug; use cgp_component::{ - CgpCore, CgpNamespace, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, - WithProvider, + CgpCore, CgpNamespace, DelegateComponent, IsProviderFor, PathCons, PathNil, RedirectLookup, + UseContext, WithProvider, }; -use cgp_field::types::*; use cgp_macro::cgp_type; use cgp_type::{TypeProvider, UseType}; diff --git a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs index 91c7e139..2714bf43 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs @@ -37,11 +37,11 @@ pub fn derive_namespace_impl( } pub fn path_to_product(paths: &[Type]) -> syn::Result { - let mut out = quote! { ε }; + let mut out = quote! { PathNil }; for path in paths.iter().rev() { out = quote! { - π< #path , #out > + PathCons< #path , #out > }; } diff --git a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs index 9000a92f..ea294f3f 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_redirect_lookup.rs @@ -1,7 +1,6 @@ use quote::quote; -use syn::punctuated::Punctuated; -use syn::token::{Brace, Comma, For, Impl}; -use syn::{GenericParam, Generics, Ident, ItemImpl, ItemTrait, Path, Type, parse2}; +use syn::token::{Brace, For, Impl}; +use syn::{GenericParam, Generics, ItemImpl, ItemTrait, Path, Type, parse2}; use crate::derive_component::provider_impl::derive_provider_item_impls; @@ -26,11 +25,11 @@ pub fn derive_redirect_lookup_impl( let delegate_constraint = if let Some(generic_params) = &generic_params { where_clause.predicates.push(parse2(quote! { - __Path__: AppendProduct< #generic_params > + __Path__: ConcatPath< #generic_params > })?); quote! { - DelegateComponent<<__Path__ as AppendProduct< #generic_params >>::Output> + DelegateComponent<<__Path__ as ConcatPath< #generic_params >>::Output> } } else { quote! { @@ -72,21 +71,29 @@ pub fn derive_redirect_lookup_impl( } pub fn extract_type_generics(generics: &Generics) -> syn::Result> { - let mut params = Punctuated::::new(); - - for param in generics.params.iter() { - if let GenericParam::Type(type_param) = param { - params.push(type_param.ident.clone()); - } - } - - if params.is_empty() { + let type_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParam::Type(type_param) = param { + Some(type_param.ident.clone()) + } else { + None + } + }) + .collect::>(); + + if type_params.is_empty() { Ok(None) } else { - let params = parse2(quote! { - ( #params ) - })?; + let mut out = quote! { PathNil }; + + for param in type_params.iter().rev() { + out = quote! { + PathCons< #param , #out > + }; + } - Ok(Some(params)) + Ok(Some(parse2(out)?)) } } diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index f72b1fa7..2e85052e 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -347,11 +347,11 @@ impl ComponentPath { let mut out = if self.wildcard { quote! { __Wildcard__ } } else { - quote! { ε } + quote! { PathNil } }; for element in self.elements.iter().rev() { - out = quote! { π< #element, #out> }; + out = quote! { PathCons< #element, #out> }; } out diff --git a/crates/cgp-tests/src/namespaces/extend.rs b/crates/cgp-tests/src/namespaces/extend.rs index 8f4fcafc..de449594 100644 --- a/crates/cgp-tests/src/namespaces/extend.rs +++ b/crates/cgp-tests/src/namespaces/extend.rs @@ -19,12 +19,17 @@ where } impl ExtendedNamespace for ErrorRaiserComponent { - type Provider = RedirectLookup; + type Provider = RedirectLookup< + Components, + PathCons>, + >; } impl ExtendedNamespace - for Product![CgpCore, ErrorComponents, ErrorTypeProviderComponent] + for PathCons>> { - type Provider = - RedirectLookup; + type Provider = RedirectLookup< + Components, + PathCons>, + >; } diff --git a/crates/cgp-tests/tests/namespace_tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs index 5d17008a..581762cd 100644 --- a/crates/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -13,7 +13,7 @@ impl HasNamespace for FooProviderComponent {} delegate_components! { UseNamespace { - FooProviderComponent: RedirectLookup, + FooProviderComponent: RedirectLookup>>>, } } diff --git a/crates/cgp-type/src/traits/has_type.rs b/crates/cgp-type/src/traits/has_type.rs index 66166cdd..84a39a3b 100644 --- a/crates/cgp-type/src/traits/has_type.rs +++ b/crates/cgp-type/src/traits/has_type.rs @@ -1,6 +1,4 @@ -use cgp_component::{ - AppendProduct, DelegateComponent, IsProviderFor, RedirectLookup, UseContext, UseDelegate, -}; +use cgp_component::*; use cgp_macro::cgp_component; #[cgp_component { From 158f94e0594b6cfbfd4c640a1512278835c1b92e Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 12 Mar 2026 23:39:36 +0100 Subject: [PATCH 25/45] Move back AppendProduct --- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/traits/append_product.rs | 3 --- crates/cgp-component/src/traits/mod.rs | 2 -- crates/cgp-core/src/prelude.rs | 5 ++--- crates/cgp-field/src/traits/append_product.rs | 6 ++++-- crates/cgp-field/src/traits/mod.rs | 1 + 6 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 crates/cgp-component/src/traits/append_product.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index ff93766e..1100d816 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -10,7 +10,7 @@ mod traits; mod types; pub use namespaces::{CgpCore, CgpNamespace, DefaultComponentsNamespace}; -pub use traits::{AppendProduct, CanUseComponent, DelegateComponent, IsProviderFor}; +pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ ConcatPath, PathCons, PathNil, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, WithContext, WithProvider, diff --git a/crates/cgp-component/src/traits/append_product.rs b/crates/cgp-component/src/traits/append_product.rs deleted file mode 100644 index e6baa88f..00000000 --- a/crates/cgp-component/src/traits/append_product.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub trait AppendProduct { - type Output; -} diff --git a/crates/cgp-component/src/traits/mod.rs b/crates/cgp-component/src/traits/mod.rs index ec1b65a7..1e5f1a04 100644 --- a/crates/cgp-component/src/traits/mod.rs +++ b/crates/cgp-component/src/traits/mod.rs @@ -1,9 +1,7 @@ -mod append_product; mod can_use_component; mod delegate_component; mod is_provider; -pub use append_product::*; pub use can_use_component::*; pub use delegate_component::DelegateComponent; pub use is_provider::*; diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index e638bd7f..03d76f3b 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,9 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - AppendProduct, CanUseComponent, CgpNamespace, ConcatPath, DelegateComponent, IsProviderFor, - PathCons, PathNil, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, - WithProvider, + CanUseComponent, CgpNamespace, ConcatPath, DelegateComponent, IsProviderFor, PathCons, PathNil, + RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-field/src/traits/append_product.rs b/crates/cgp-field/src/traits/append_product.rs index 320b8600..a2aae98c 100644 --- a/crates/cgp-field/src/traits/append_product.rs +++ b/crates/cgp-field/src/traits/append_product.rs @@ -1,7 +1,9 @@ -use cgp_component::AppendProduct; - use crate::types::{Cons, Nil}; +pub trait AppendProduct { + type Output; +} + impl AppendProduct for Nil { type Output = Cons; } diff --git a/crates/cgp-field/src/traits/mod.rs b/crates/cgp-field/src/traits/mod.rs index 3f377848..63d13dc7 100644 --- a/crates/cgp-field/src/traits/mod.rs +++ b/crates/cgp-field/src/traits/mod.rs @@ -20,6 +20,7 @@ mod to_fields; mod transform_map; mod update_field; +pub use append_product::*; pub use build_field::*; pub use concat_product::*; pub use extract_field::*; From f31df0b89f41888fae5924d16ef84b0e8c839833 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 21:31:41 +0100 Subject: [PATCH 26/45] Convert lowercase namespace ident to symbol --- crates/cgp-macro-lib/src/cgp_fn/bounds.rs | 2 +- crates/cgp-macro-lib/src/cgp_fn/fn_body.rs | 2 +- .../cgp-macro-lib/src/derive_builder/utils.rs | 2 +- .../derive_extractor/extract_field_impls.rs | 2 +- .../src/derive_getter/blanket.rs | 2 +- .../src/derive_getter/use_fields.rs | 2 +- .../src/derive_has_fields/product.rs | 2 +- .../src/derive_has_fields/sum.rs | 2 +- .../src/entrypoints/cgp_record.rs | 2 +- .../src/entrypoints/derive_from_variant.rs | 2 +- crates/cgp-macro-lib/src/field.rs | 16 +++++++------- .../src/parse/delegate_components.rs | 21 ++++++++++++++++--- crates/cgp-macro-lib/src/symbol.rs | 16 +++++++------- crates/cgp-macro/src/lib.rs | 8 +++++-- .../tests/namespace_tests/redirect.rs | 2 ++ 15 files changed, 52 insertions(+), 31 deletions(-) diff --git a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs index 76ec19c0..17b90993 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs @@ -13,7 +13,7 @@ pub fn build_implicit_args_bounds( let mut constraints: Punctuated = Punctuated::new(); for arg in implicit_args { - let field_symbol = symbol_from_string(&arg.field_name.to_string()); + let field_symbol = symbol_from_string(&arg.field_name.to_string())?; let constraint = derive_getter_constraint( &arg.field_type, diff --git a/crates/cgp-macro-lib/src/cgp_fn/fn_body.rs b/crates/cgp-macro-lib/src/cgp_fn/fn_body.rs index f4919e74..20684e17 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/fn_body.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/fn_body.rs @@ -16,7 +16,7 @@ pub fn inject_implicit_arg(arg: &ImplicitArgField, body: &mut Block) -> syn::Res let field_name = &arg.field_name; let arg_type = &arg.arg_type; - let field_symbol = symbol_from_string(&field_name.to_string()); + let field_symbol = symbol_from_string(&field_name.to_string())?; let call_expr = if arg.field_mut.is_none() { quote! { diff --git a/crates/cgp-macro-lib/src/derive_builder/utils.rs b/crates/cgp-macro-lib/src/derive_builder/utils.rs index 20823af9..07f86f90 100644 --- a/crates/cgp-macro-lib/src/derive_builder/utils.rs +++ b/crates/cgp-macro-lib/src/derive_builder/utils.rs @@ -26,7 +26,7 @@ pub fn field_to_member(index: usize, field: &Field) -> Member { pub fn field_to_tag(index: usize, field: &Field) -> syn::Result { match &field.ident { - Some(ident) => Ok(symbol_from_string(&ident.to_string())), + Some(ident) => symbol_from_string(&ident.to_string()), None => { let index = LitInt::new(&format!("{index}"), field.span()); parse2(quote! { δ< #index > }) diff --git a/crates/cgp-macro-lib/src/derive_extractor/extract_field_impls.rs b/crates/cgp-macro-lib/src/derive_extractor/extract_field_impls.rs index e9682841..ccb941aa 100644 --- a/crates/cgp-macro-lib/src/derive_extractor/extract_field_impls.rs +++ b/crates/cgp-macro-lib/src/derive_extractor/extract_field_impls.rs @@ -83,7 +83,7 @@ pub fn derive_extract_field_impls( } }; - let tag_type = symbol_from_string(¤t_variant.ident.to_string()); + let tag_type = symbol_from_string(¤t_variant.ident.to_string())?; let source_type: Type = parse2(quote! { #extractor_ident < #source_generic_args > diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index 8dc02a9a..730a643e 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -64,7 +64,7 @@ pub fn derive_blanket_impl( ), }; - let field_symbol = symbol_from_string(&field.field_name.to_string()); + let field_symbol = symbol_from_string(&field.field_name.to_string())?; let method = derive_getter_method( &context_arg, diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 068122db..73f6849c 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -55,7 +55,7 @@ pub fn derive_use_fields_impl( ReceiverMode::Type(ty) => ty.to_token_stream(), }; - let field_symbol = symbol_from_string(&field.field_name.to_string()); + let field_symbol = symbol_from_string(&field.field_name.to_string())?; let method = derive_getter_method( &ContextArg::Ident(receiver_type.clone()), diff --git a/crates/cgp-macro-lib/src/derive_has_fields/product.rs b/crates/cgp-macro-lib/src/derive_has_fields/product.rs index 72cbbd83..7ca37440 100644 --- a/crates/cgp-macro-lib/src/derive_has_fields/product.rs +++ b/crates/cgp-macro-lib/src/derive_has_fields/product.rs @@ -15,7 +15,7 @@ pub fn item_fields_to_product_type(fields: &Fields, reference: &TokenStream) -> Error::new_spanned(field, "expect struct field to contain name identifier") })?; - let field_tag = symbol_from_string(&field_name.to_string()); + let field_tag = symbol_from_string(&field_name.to_string())?; let field_type = &field.ty; fields_type = parse2(quote! { diff --git a/crates/cgp-macro-lib/src/derive_has_fields/sum.rs b/crates/cgp-macro-lib/src/derive_has_fields/sum.rs index 9eb29425..8f9ce680 100644 --- a/crates/cgp-macro-lib/src/derive_has_fields/sum.rs +++ b/crates/cgp-macro-lib/src/derive_has_fields/sum.rs @@ -15,7 +15,7 @@ pub fn variants_to_sum_type( for variant in variants.iter().rev() { let variant_ident = &variant.ident; - let variant_symbol = symbol_from_string(&variant_ident.to_string()); + let variant_symbol = symbol_from_string(&variant_ident.to_string())?; let variant_fields = item_fields_to_product_type(&variant.fields, reference)?; diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_record.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_record.rs index 7c2e8032..a1bb5c27 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_record.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_record.rs @@ -12,7 +12,7 @@ pub fn derive_cgp_record(body: TokenStream) -> syn::Result { } pub fn derive_cgp_record_from_struct(item_struct: &ItemStruct) -> syn::Result { - let has_field_impls = derive_has_field_impls_from_struct(item_struct); + let has_field_impls = derive_has_field_impls_from_struct(item_struct)?; let has_fields_impls = derive_has_fields_impls_from_struct(item_struct)?; let build_field_impls = derive_build_field_from_struct(item_struct)?; diff --git a/crates/cgp-macro-lib/src/entrypoints/derive_from_variant.rs b/crates/cgp-macro-lib/src/entrypoints/derive_from_variant.rs index 0e6c0819..ffcf6bce 100644 --- a/crates/cgp-macro-lib/src/entrypoints/derive_from_variant.rs +++ b/crates/cgp-macro-lib/src/entrypoints/derive_from_variant.rs @@ -20,7 +20,7 @@ pub fn derive_from_variant_from_enum(item_enum: &ItemEnum) -> syn::Result Vec { +pub fn derive_has_field_impls_from_struct(item_struct: &ItemStruct) -> syn::Result> { let struct_ident = &item_struct.ident; let (impl_generics, ty_generics, where_clause) = item_struct.generics.split_for_impl(); @@ -20,7 +20,7 @@ pub fn derive_has_field_impls_from_struct(item_struct: &ItemStruct) -> Vec Vec {} } - item_impls + Ok(item_impls) } -pub fn derive_has_field(input: TokenStream) -> TokenStream { - let item_struct: ItemStruct = syn::parse2(input).unwrap(); +pub fn derive_has_field(input: TokenStream) -> syn::Result { + let item_struct: ItemStruct = syn::parse2(input)?; - let item_impls = derive_has_field_impls_from_struct(&item_struct); + let item_impls = derive_has_field_impls_from_struct(&item_struct)?; - quote! { + Ok(quote! { #( #item_impls )* - } + }) } diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 2e85052e..fa0d9cf6 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -1,6 +1,6 @@ use core::iter; -use proc_macro2::TokenStream; +use proc_macro2::{TokenStream, TokenTree}; use quote::{ToTokens, TokenStreamExt, quote}; use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; @@ -10,6 +10,7 @@ use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, RArrow, Star}; use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; use crate::parse::{ImplGenerics, TypeGenerics}; +use crate::symbol::symbol_from_string; pub struct DelegateComponents { pub new_struct: bool, @@ -369,8 +370,22 @@ impl Parse for PathElement { let _: Star = input.parse()?; Ok(Self::Wildcard) } else { - let ty: Type = input.parse()?; - Ok(Self::Type(ty)) + let path_type: Type = input.parse()?; + + let path_tokens = path_type.to_token_stream().into_iter().collect::>(); + let path_token: Result<[TokenTree; 1], _> = path_tokens.try_into(); + + if let Ok([TokenTree::Ident(path_ident)]) = path_token { + let path_str = path_ident.to_string(); + if let Some(path_char) = path_str.chars().next() { + if path_char.is_ascii_lowercase() { + let path_symbol = symbol_from_string(&path_str)?; + return Ok(Self::Type(path_symbol)); + } + } + } + + Ok(Self::Type(path_type)) } } } diff --git a/crates/cgp-macro-lib/src/symbol.rs b/crates/cgp-macro-lib/src/symbol.rs index 600a27ac..81ae9fdd 100644 --- a/crates/cgp-macro-lib/src/symbol.rs +++ b/crates/cgp-macro-lib/src/symbol.rs @@ -1,8 +1,8 @@ use proc_macro2::{Literal, TokenStream}; -use quote::ToTokens; -use syn::{LitStr, Type, parse_quote}; +use quote::{ToTokens, quote}; +use syn::{LitStr, Type, parse_quote, parse2}; -pub fn symbol_from_string(value: &str) -> Type { +pub fn symbol_from_string(value: &str) -> syn::Result { let chars = value .chars() .rfold(parse_quote! { ε }, |tail, c: char| -> Type { @@ -11,13 +11,13 @@ pub fn symbol_from_string(value: &str) -> Type { let len = Literal::usize_unsuffixed(value.len()); - parse_quote!( ψ< #len, #chars > ) + parse2(quote! { ψ< #len, #chars > }) } -pub fn make_symbol(input: TokenStream) -> TokenStream { - let literal: LitStr = syn::parse2(input).unwrap(); +pub fn make_symbol(input: TokenStream) -> syn::Result { + let literal: LitStr = syn::parse2(input)?; - let symbol = symbol_from_string(&literal.value()); + let symbol = symbol_from_string(&literal.value())?; - symbol.to_token_stream() + Ok(symbol.to_token_stream()) } diff --git a/crates/cgp-macro/src/lib.rs b/crates/cgp-macro/src/lib.rs index a4f08a71..e70c0cd1 100644 --- a/crates/cgp-macro/src/lib.rs +++ b/crates/cgp-macro/src/lib.rs @@ -893,7 +893,9 @@ pub fn replace_with(body: TokenStream) -> TokenStream { #[proc_macro] #[allow(non_snake_case)] pub fn Symbol(body: TokenStream) -> TokenStream { - cgp_macro_lib::make_symbol(body.into()).into() + cgp_macro_lib::make_symbol(body.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } /** @@ -972,7 +974,9 @@ pub fn product(body: TokenStream) -> TokenStream { #[proc_macro_derive(HasField)] pub fn derive_fields(item: TokenStream) -> TokenStream { - cgp_macro_lib::derive_has_field(item.into()).into() + cgp_macro_lib::derive_has_field(item.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } #[proc_macro_derive(HasFields)] diff --git a/crates/cgp-tests/tests/namespace_tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs index 581762cd..1bde0aeb 100644 --- a/crates/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -38,6 +38,8 @@ delegate_components! { @BarComponent.BazComponent.*: TestProvider, + @bar.BazComponent.*: TestProvider, + // @BarComponent.BazComponent.FooProviderComponent: TestProvider, // @*.BazComponent.*: TestProvider, From b40daa8aee88ec12ecd6d140c7f914cf8e6227ef Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 21:35:12 +0100 Subject: [PATCH 27/45] Add symbol_from_string_spanned --- .../src/parse/delegate_components.rs | 4 ++-- crates/cgp-macro-lib/src/symbol.rs | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index fa0d9cf6..2aa3eee1 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -10,7 +10,7 @@ use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, RArrow, Star}; use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; use crate::parse::{ImplGenerics, TypeGenerics}; -use crate::symbol::symbol_from_string; +use crate::symbol::symbol_from_string_spanned; pub struct DelegateComponents { pub new_struct: bool, @@ -379,7 +379,7 @@ impl Parse for PathElement { let path_str = path_ident.to_string(); if let Some(path_char) = path_str.chars().next() { if path_char.is_ascii_lowercase() { - let path_symbol = symbol_from_string(&path_str)?; + let path_symbol = symbol_from_string_spanned(path_ident.span(), &path_str)?; return Ok(Self::Type(path_symbol)); } } diff --git a/crates/cgp-macro-lib/src/symbol.rs b/crates/cgp-macro-lib/src/symbol.rs index 81ae9fdd..adaf6271 100644 --- a/crates/cgp-macro-lib/src/symbol.rs +++ b/crates/cgp-macro-lib/src/symbol.rs @@ -1,23 +1,27 @@ -use proc_macro2::{Literal, TokenStream}; -use quote::{ToTokens, quote}; -use syn::{LitStr, Type, parse_quote, parse2}; +use proc_macro2::{Literal, Span, TokenStream}; +use quote::{ToTokens, quote_spanned}; +use syn::{LitStr, Type, parse2}; pub fn symbol_from_string(value: &str) -> syn::Result { - let chars = value - .chars() - .rfold(parse_quote! { ε }, |tail, c: char| -> Type { - parse_quote!( ζ< #c, #tail > ) - }); + symbol_from_string_spanned(Span::call_site(), value) +} + +pub fn symbol_from_string_spanned(span: Span, value: &str) -> syn::Result { + let mut chars = quote_spanned! { span => ε }; + + for c in value.chars().rev() { + chars = quote_spanned! { span => ζ< #c, #chars > }; + } let len = Literal::usize_unsuffixed(value.len()); - parse2(quote! { ψ< #len, #chars > }) + parse2(quote_spanned! { span => ψ< #len, #chars > }) } pub fn make_symbol(input: TokenStream) -> syn::Result { let literal: LitStr = syn::parse2(input)?; - let symbol = symbol_from_string(&literal.value())?; + let symbol = symbol_from_string_spanned(literal.span(), &literal.value())?; Ok(symbol.to_token_stream()) } From 08f41a3b652acec1e617157870eafaa99d85cddd Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 21:44:41 +0100 Subject: [PATCH 28/45] Use path symbol in #[use_namespace] --- crates/cgp-error/src/traits/has_error_type.rs | 9 ++--- .../src/attributes/use_namespace.rs | 6 ++- .../src/derive_component/derive_namespace.rs | 2 +- .../src/parse/delegate_components.rs | 37 +++++++++++++------ crates/cgp-tests/src/namespaces/extend.rs | 18 +++++---- .../tests/namespace_tests/experiments.rs | 6 +-- 6 files changed, 48 insertions(+), 30 deletions(-) diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index c4c7b1df..d83e9aa2 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -1,14 +1,13 @@ use core::fmt::Debug; use cgp_component::{ - CgpCore, CgpNamespace, DelegateComponent, IsProviderFor, PathCons, PathNil, RedirectLookup, - UseContext, WithProvider, + CgpNamespace, DelegateComponent, IsProviderFor, PathCons, PathNil, RedirectLookup, UseContext, + WithProvider, }; +use cgp_field::types::*; use cgp_macro::cgp_type; use cgp_type::{TypeProvider, UseType}; -use crate::ErrorComponents; - /** The `HasErrorType` trait provides an abstract error type that can be used by CGP components to decouple the code from any concrete error implementation. @@ -29,7 +28,7 @@ use crate::ErrorComponents; */ #[cgp_type] -#[use_namespace(CgpCore.ErrorComponents)] +#[use_namespace(cgp.core.error)] pub trait HasErrorType { type Error: Debug; } diff --git a/crates/cgp-macro-lib/src/attributes/use_namespace.rs b/crates/cgp-macro-lib/src/attributes/use_namespace.rs index 5c6b013d..4478b472 100644 --- a/crates/cgp-macro-lib/src/attributes/use_namespace.rs +++ b/crates/cgp-macro-lib/src/attributes/use_namespace.rs @@ -1,11 +1,13 @@ +use syn::Ident; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Colon, Dot}; -use syn::{Ident, Type}; + +use crate::parse::PathType; pub struct UseNamespaceAttribute { pub namespace: Ident, - pub path: Punctuated, + pub path: Punctuated, } impl Parse for UseNamespaceAttribute { diff --git a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs index 2714bf43..6229f5af 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive_namespace.rs @@ -21,7 +21,7 @@ pub fn derive_namespace_impl( component_name: &Ident, ) -> syn::Result { let namespace = &attribute.namespace; - let mut paths = Vec::from_iter(attribute.path.iter().map(Clone::clone)); + let mut paths = Vec::from_iter(attribute.path.iter().map(|path| path.path_type.clone())); paths.push(parse2(component_name.to_token_stream())?); let path = path_to_product(&paths)?; diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 2aa3eee1..35b3b9f5 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -370,22 +370,35 @@ impl Parse for PathElement { let _: Star = input.parse()?; Ok(Self::Wildcard) } else { - let path_type: Type = input.parse()?; + let PathType { path_type } = input.parse()?; + Ok(Self::Type(path_type)) + } + } +} - let path_tokens = path_type.to_token_stream().into_iter().collect::>(); - let path_token: Result<[TokenTree; 1], _> = path_tokens.try_into(); +pub struct PathType { + pub path_type: Type, +} - if let Ok([TokenTree::Ident(path_ident)]) = path_token { - let path_str = path_ident.to_string(); - if let Some(path_char) = path_str.chars().next() { - if path_char.is_ascii_lowercase() { - let path_symbol = symbol_from_string_spanned(path_ident.span(), &path_str)?; - return Ok(Self::Type(path_symbol)); - } +impl Parse for PathType { + fn parse(input: ParseStream) -> syn::Result { + let path_type: Type = input.parse()?; + + let path_tokens = path_type.to_token_stream().into_iter().collect::>(); + let path_token: Result<[TokenTree; 1], _> = path_tokens.try_into(); + + if let Ok([TokenTree::Ident(path_ident)]) = path_token { + let path_str = path_ident.to_string(); + if let Some(path_char) = path_str.chars().next() { + if path_char.is_ascii_lowercase() { + let path_symbol = symbol_from_string_spanned(path_ident.span(), &path_str)?; + return Ok(Self { + path_type: path_symbol, + }); } } - - Ok(Self::Type(path_type)) } + + Ok(Self { path_type }) } } diff --git a/crates/cgp-tests/src/namespaces/extend.rs b/crates/cgp-tests/src/namespaces/extend.rs index de449594..9cdd704b 100644 --- a/crates/cgp-tests/src/namespaces/extend.rs +++ b/crates/cgp-tests/src/namespaces/extend.rs @@ -1,5 +1,5 @@ -use cgp::core::component::{CgpCore, RedirectLookup}; -use cgp::core::error::{ErrorComponents, ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::core::component::RedirectLookup; +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::prelude::*; pub trait ExtendedNamespace { @@ -8,8 +8,6 @@ pub trait ExtendedNamespace { pub struct ExtendedNamespaceComponents; -pub struct MyErrorComponents; - impl ExtendedNamespace for Component where Component: @@ -21,15 +19,21 @@ where impl ExtendedNamespace for ErrorRaiserComponent { type Provider = RedirectLookup< Components, - PathCons>, + PathCons>, >; } impl ExtendedNamespace - for PathCons>> + for PathCons< + Symbol!("cgp"), + PathCons< + Symbol!("core"), + PathCons>, + >, + > { type Provider = RedirectLookup< Components, - PathCons>, + PathCons>, >; } diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index 66cb9213..ab3309a8 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -2,7 +2,7 @@ use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; -use cgp_tests::namespaces::{ExtendedNamespace, MyErrorComponents}; +use cgp_tests::namespaces::ExtendedNamespace; pub struct App; @@ -10,9 +10,9 @@ delegate_components! { App { > Component: Component::Provider, - @MyErrorComponents.ErrorTypeProviderComponent: + @app.ErrorTypeProviderComponent: UseType, - @MyErrorComponents.ErrorRaiserComponent.&'static str: + @app.ErrorRaiserComponent.&'static str: RaiseFrom, TryComputerComponent: Foo, From bb5d9a878300dcfcd1667b9622b4fd045d6b11c3 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 21:48:02 +0100 Subject: [PATCH 29/45] Remove obsolete constructs --- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/namespaces.rs | 6 ------ crates/cgp-error/src/lib.rs | 2 -- crates/cgp-error/src/paths.rs | 1 - crates/cgp-tests/tests/namespace_tests/use_namespace.rs | 7 +++---- 5 files changed, 4 insertions(+), 14 deletions(-) delete mode 100644 crates/cgp-error/src/paths.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 1100d816..ac087937 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -9,7 +9,7 @@ mod namespaces; mod traits; mod types; -pub use namespaces::{CgpCore, CgpNamespace, DefaultComponentsNamespace}; +pub use namespaces::CgpNamespace; pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ ConcatPath, PathCons, PathNil, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs index d216c394..9f92fb51 100644 --- a/crates/cgp-component/src/namespaces.rs +++ b/crates/cgp-component/src/namespaces.rs @@ -1,9 +1,3 @@ pub trait CgpNamespace { type Provider; } - -pub trait DefaultComponentsNamespace { - type Provider; -} - -pub struct CgpCore; diff --git a/crates/cgp-error/src/lib.rs b/crates/cgp-error/src/lib.rs index b30722f3..56a70f70 100644 --- a/crates/cgp-error/src/lib.rs +++ b/crates/cgp-error/src/lib.rs @@ -1,9 +1,7 @@ #![no_std] mod contexts; -mod paths; mod traits; pub use contexts::*; -pub use paths::*; pub use traits::*; diff --git a/crates/cgp-error/src/paths.rs b/crates/cgp-error/src/paths.rs deleted file mode 100644 index 35968e7c..00000000 --- a/crates/cgp-error/src/paths.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct ErrorComponents; diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs index 1bf89e9b..2ebdd90a 100644 --- a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -1,11 +1,10 @@ -use cgp::core::component::{CgpCore, DefaultComponentsNamespace}; use cgp::core::error::ErrorTypeProviderComponent; use cgp::prelude::*; pub struct MyComponents; #[cgp_component(FooProvider)] -#[use_namespace(CgpNamespace: CgpCore.MyComponents)] +#[use_namespace(CgpNamespace: app.MyComponents)] pub trait CanDoFoo { fn foo(&self); } @@ -14,9 +13,9 @@ pub struct App; delegate_components! { App { - > + > Component: Component::Provider, - ErrorTypeProviderComponent: + @cgp.core.error.ErrorTypeProviderComponent: UseType, } } From 2d46298cd0851e2a913e0960438b84ddae245eb0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 21:49:43 +0100 Subject: [PATCH 30/45] Rename CgpNamespace to DefaultNamespace --- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/namespaces.rs | 2 +- crates/cgp-core/src/prelude.rs | 4 ++-- crates/cgp-error/src/traits/has_error_type.rs | 4 ++-- crates/cgp-macro-lib/src/attributes/use_namespace.rs | 2 +- crates/cgp-tests/src/namespaces/extend.rs | 4 ++-- crates/cgp-tests/tests/namespace_tests/use_namespace.rs | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index ac087937..a92a79a1 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -9,7 +9,7 @@ mod namespaces; mod traits; mod types; -pub use namespaces::CgpNamespace; +pub use namespaces::DefaultNamespace; pub use traits::{CanUseComponent, DelegateComponent, IsProviderFor}; pub use types::{ ConcatPath, PathCons, PathNil, RedirectLookup, UseContext, UseDefault, UseDelegate, UseFields, diff --git a/crates/cgp-component/src/namespaces.rs b/crates/cgp-component/src/namespaces.rs index 9f92fb51..3c8e4bf5 100644 --- a/crates/cgp-component/src/namespaces.rs +++ b/crates/cgp-component/src/namespaces.rs @@ -1,3 +1,3 @@ -pub trait CgpNamespace { +pub trait DefaultNamespace { type Provider; } diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 03d76f3b..cc2321ab 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -2,8 +2,8 @@ pub use core::marker::PhantomData; pub use cgp_async_macro::async_trait; pub use cgp_component::{ - CanUseComponent, CgpNamespace, ConcatPath, DelegateComponent, IsProviderFor, PathCons, PathNil, - RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, + CanUseComponent, ConcatPath, DefaultNamespace, DelegateComponent, IsProviderFor, PathCons, + PathNil, RedirectLookup, UseContext, UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; diff --git a/crates/cgp-error/src/traits/has_error_type.rs b/crates/cgp-error/src/traits/has_error_type.rs index d83e9aa2..c02beb4a 100644 --- a/crates/cgp-error/src/traits/has_error_type.rs +++ b/crates/cgp-error/src/traits/has_error_type.rs @@ -1,8 +1,8 @@ use core::fmt::Debug; use cgp_component::{ - CgpNamespace, DelegateComponent, IsProviderFor, PathCons, PathNil, RedirectLookup, UseContext, - WithProvider, + DefaultNamespace, DelegateComponent, IsProviderFor, PathCons, PathNil, RedirectLookup, + UseContext, WithProvider, }; use cgp_field::types::*; use cgp_macro::cgp_type; diff --git a/crates/cgp-macro-lib/src/attributes/use_namespace.rs b/crates/cgp-macro-lib/src/attributes/use_namespace.rs index 4478b472..35199567 100644 --- a/crates/cgp-macro-lib/src/attributes/use_namespace.rs +++ b/crates/cgp-macro-lib/src/attributes/use_namespace.rs @@ -17,7 +17,7 @@ impl Parse for UseNamespaceAttribute { let _: Colon = input.parse()?; namespace } else { - Ident::new("CgpNamespace", input.span()) + Ident::new("DefaultNamespace", input.span()) }; let path = Punctuated::parse_separated_nonempty(input)?; diff --git a/crates/cgp-tests/src/namespaces/extend.rs b/crates/cgp-tests/src/namespaces/extend.rs index 9cdd704b..13490859 100644 --- a/crates/cgp-tests/src/namespaces/extend.rs +++ b/crates/cgp-tests/src/namespaces/extend.rs @@ -10,8 +10,8 @@ pub struct ExtendedNamespaceComponents; impl ExtendedNamespace for Component where - Component: - CgpNamespace + CgpNamespace, + Component: DefaultNamespace + + DefaultNamespace, { type Provider = Provider; } diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs index 2ebdd90a..089de423 100644 --- a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -4,7 +4,7 @@ use cgp::prelude::*; pub struct MyComponents; #[cgp_component(FooProvider)] -#[use_namespace(CgpNamespace: app.MyComponents)] +#[use_namespace(DefaultNamespace: app.MyComponents)] pub trait CanDoFoo { fn foo(&self); } @@ -13,7 +13,7 @@ pub struct App; delegate_components! { App { - > + > Component: Component::Provider, @cgp.core.error.ErrorTypeProviderComponent: UseType, From 948a63601cd62d2330470e9413ebad279e5c6af9 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 22:24:54 +0100 Subject: [PATCH 31/45] Use #[use_namespace] in foobar example --- .../tests/namespace_tests/redirect.rs | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/crates/cgp-tests/tests/namespace_tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs index 1bde0aeb..575f8591 100644 --- a/crates/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -1,22 +1,11 @@ use cgp::prelude::*; -pub trait HasNamespace {} - -pub struct UseNamespace(pub PhantomData); - #[cgp_component(FooProvider)] +#[use_namespace(bar.baz)] pub trait CanDoFoo { fn foo(); } -impl HasNamespace for FooProviderComponent {} - -delegate_components! { - UseNamespace { - FooProviderComponent: RedirectLookup>>>, - } -} - pub struct BarComponent; pub struct BazComponent; @@ -31,19 +20,14 @@ pub struct App; delegate_components! { // #[use_namespace] App { - > Component: - UseNamespace, - - // @BarComponent.*: TestProvider, - - @BarComponent.BazComponent.*: TestProvider, + > Component: + Component::Provider, - @bar.BazComponent.*: TestProvider, + // @bar.*: TestProvider, - // @BarComponent.BazComponent.FooProviderComponent: TestProvider, + @bar.baz.*: TestProvider, - // @*.BazComponent.*: TestProvider, - // Cons>: TestProvider, + // @bar.baz.FooProviderComponent: TestProvider, } } From 3ecdaa21a82975eb511c613a1c06cb9dc87d5b73 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 22:52:50 +0100 Subject: [PATCH 32/45] Accept attributes in delegate_components! --- .../src/delegate_components/attributes.rs | 1 + crates/cgp-macro-lib/src/delegate_components/mod.rs | 1 + .../cgp-macro-lib/src/parse/delegate_components.rs | 12 ++++++++++-- crates/cgp-tests/tests/namespace_tests/redirect.rs | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 crates/cgp-macro-lib/src/delegate_components/attributes.rs diff --git a/crates/cgp-macro-lib/src/delegate_components/attributes.rs b/crates/cgp-macro-lib/src/delegate_components/attributes.rs new file mode 100644 index 00000000..8205b3ed --- /dev/null +++ b/crates/cgp-macro-lib/src/delegate_components/attributes.rs @@ -0,0 +1 @@ +pub fn parse_delegate_attributes() {} diff --git a/crates/cgp-macro-lib/src/delegate_components/mod.rs b/crates/cgp-macro-lib/src/delegate_components/mod.rs index 6a725623..3ccfb12b 100644 --- a/crates/cgp-macro-lib/src/delegate_components/mod.rs +++ b/crates/cgp-macro-lib/src/delegate_components/mod.rs @@ -1,3 +1,4 @@ +mod attributes; mod define_struct; mod impl_delegate; mod merge_generics; diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 35b3b9f5..d68d3f81 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -6,13 +6,14 @@ use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, RArrow, Star}; -use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; +use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, Pound, RArrow, Star}; +use syn::{Attribute, Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; use crate::parse::{ImplGenerics, TypeGenerics}; use crate::symbol::symbol_from_string_spanned; pub struct DelegateComponents { + pub attributes: Vec, pub new_struct: bool, pub target_type: Type, pub target_generics: ImplGenerics, @@ -74,6 +75,12 @@ impl DelegateValue { impl Parse for DelegateComponents { fn parse(input: ParseStream) -> syn::Result { + let attributes = if input.peek(Pound) { + input.call(Attribute::parse_outer)? + } else { + Vec::new() + }; + let target_generics = if input.peek(Lt) { input.parse()? } else { @@ -101,6 +108,7 @@ impl Parse for DelegateComponents { }; Ok(Self { + attributes, new_struct, target_type, target_generics, diff --git a/crates/cgp-tests/tests/namespace_tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs index 575f8591..083d3d3a 100644 --- a/crates/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -18,7 +18,7 @@ impl FooProvider { pub struct App; delegate_components! { - // #[use_namespace] + #[use_namespace] App { > Component: Component::Provider, From 98b05e6472baefc158ce604743f8837f161a58d4 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 22:58:13 +0100 Subject: [PATCH 33/45] Implement parse_delegate_attributes --- .../src/delegate_components/attributes.rs | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/cgp-macro-lib/src/delegate_components/attributes.rs b/crates/cgp-macro-lib/src/delegate_components/attributes.rs index 8205b3ed..23619292 100644 --- a/crates/cgp-macro-lib/src/delegate_components/attributes.rs +++ b/crates/cgp-macro-lib/src/delegate_components/attributes.rs @@ -1 +1,42 @@ -pub fn parse_delegate_attributes() {} +use syn::{Attribute, Ident}; + +pub fn parse_delegate_attributes(attributes: Vec) -> syn::Result { + let mut parsed_attributes = DelegateAttributes::default(); + + for attribute in attributes.iter() { + if let Some(ident) = attribute.path().get_ident() { + if ident == "use_namespace" { + if parsed_attributes.use_namespace.is_some() { + return Err(syn::Error::new_spanned( + attribute, + "Multiple #[use_namespace] attributes are not allowed", + )); + } + + let namespace = attribute.parse_args::>()?; + parsed_attributes.use_namespace = Some(DelegateNamespaceAttribute { namespace }); + } else { + return Err(syn::Error::new_spanned( + attribute, + format!("Unknown attribute {} for delegate_components", ident), + )); + } + } else { + return Err(syn::Error::new_spanned( + attribute, + "Unexpected attribute format for delegate_components", + )); + } + } + + Ok(parsed_attributes) +} + +#[derive(Default)] +pub struct DelegateAttributes { + pub use_namespace: Option, +} + +pub struct DelegateNamespaceAttribute { + pub namespace: Option, +} From 4dd1a79532b095c5fca7f203426c306c9777ff18 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 23:16:18 +0100 Subject: [PATCH 34/45] Derive #[use_namespace] --- .../src/delegate_components/attributes.rs | 12 ++- .../src/delegate_components/mod.rs | 1 + .../src/entrypoints/delegate_components.rs | 74 +++++++++++++++---- .../tests/namespace_tests/experiments.rs | 3 +- .../tests/namespace_tests/redirect.rs | 3 - .../tests/namespace_tests/use_namespace.rs | 3 +- 6 files changed, 70 insertions(+), 26 deletions(-) diff --git a/crates/cgp-macro-lib/src/delegate_components/attributes.rs b/crates/cgp-macro-lib/src/delegate_components/attributes.rs index 23619292..f5174e89 100644 --- a/crates/cgp-macro-lib/src/delegate_components/attributes.rs +++ b/crates/cgp-macro-lib/src/delegate_components/attributes.rs @@ -1,4 +1,4 @@ -use syn::{Attribute, Ident}; +use syn::{Attribute, Ident, Meta}; pub fn parse_delegate_attributes(attributes: Vec) -> syn::Result { let mut parsed_attributes = DelegateAttributes::default(); @@ -13,8 +13,14 @@ pub fn parse_delegate_attributes(attributes: Vec) -> syn::Result>()?; - parsed_attributes.use_namespace = Some(DelegateNamespaceAttribute { namespace }); + if let Meta::Path(_) = attribute.meta { + parsed_attributes.use_namespace = + Some(DelegateNamespaceAttribute { namespace: None }); + } else { + let namespace = attribute.parse_args::>()?; + parsed_attributes.use_namespace = + Some(DelegateNamespaceAttribute { namespace }); + } } else { return Err(syn::Error::new_spanned( attribute, diff --git a/crates/cgp-macro-lib/src/delegate_components/mod.rs b/crates/cgp-macro-lib/src/delegate_components/mod.rs index 3ccfb12b..3f5a4f7c 100644 --- a/crates/cgp-macro-lib/src/delegate_components/mod.rs +++ b/crates/cgp-macro-lib/src/delegate_components/mod.rs @@ -3,5 +3,6 @@ mod define_struct; mod impl_delegate; mod merge_generics; +pub use attributes::*; pub use define_struct::*; pub use impl_delegate::*; diff --git a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs index 2fe3b8d3..7ec0bf34 100644 --- a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs +++ b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs @@ -1,35 +1,77 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, quote}; -use syn::parse2; +use syn::{Ident, parse2}; -use crate::delegate_components::{define_struct, impl_delegate_components}; +use crate::delegate_components::{ + DelegateNamespaceAttribute, define_struct, impl_delegate_components, parse_delegate_attributes, +}; use crate::parse::{DelegateComponents, SimpleType, TypeGenerics}; pub fn delegate_components(body: TokenStream) -> syn::Result { let spec: DelegateComponents = parse2(body)?; - let component_struct = if spec.new_struct { - let target_type: SimpleType = parse2(spec.target_type.to_token_stream())?; + let target_type = &spec.target_type; + let target_generics = &spec.target_generics; + + let mut output = TokenStream::new(); + + if spec.new_struct { + let target_type: SimpleType = parse2(target_type.to_token_stream())?; let type_generics = target_type.generics.unwrap_or_default().generics; let component_struct = define_struct(&target_type.name, &type_generics)?; - Some(component_struct) - } else { - None - }; + output.extend(component_struct.to_token_stream()); + } + + let attributes = parse_delegate_attributes(spec.attributes)?; + + if let Some(DelegateNamespaceAttribute { namespace }) = attributes.use_namespace { + let namespace = + namespace.unwrap_or_else(|| Ident::new("DefaultNamespace", Span::call_site())); + + let mut generics = target_generics.generics.clone(); + generics.params.push(parse2(quote! { __Component__ })?); - let impl_items = - impl_delegate_components(&spec.target_type, &spec.target_generics, &spec.entries)?; + let impl_generics = generics.split_for_impl().0; - let mut output = quote! { - #component_struct - }; + let namespace_impl = quote! { + impl #impl_generics + DelegateComponent<__Component__> + for #target_type + where + __Component__: #namespace< #target_type >, + { + type Delegate = < __Component__ as #namespace< #target_type >>::Provider; + } + }; - for impl_item in impl_items { - output.extend(impl_item.to_token_stream()); + output.extend(namespace_impl); + + let mut generics = generics.clone(); + generics.params.push(parse2(quote! { __Context__ })?); + generics.params.push(parse2(quote! { __Params__ })?); + + let impl_generics = generics.split_for_impl().0; + + let is_provider_for_impl = quote! { + impl #impl_generics + IsProviderFor<__Component__, __Context__, __Params__> + for #target_type + where + __Component__: #namespace< #target_type >, + < __Component__ as #namespace< #target_type >>::Provider: IsProviderFor<__Component__, __Context__, __Params__>, + { + } + }; + + output.extend(is_provider_for_impl); } + let impl_items = impl_delegate_components(&target_type, &target_generics, &spec.entries)?; + + output.extend(impl_items); + Ok(output) } diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index ab3309a8..3dd0c33e 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -7,9 +7,8 @@ use cgp_tests::namespaces::ExtendedNamespace; pub struct App; delegate_components! { + #[use_namespace(ExtendedNamespace)] App { - > - Component: Component::Provider, @app.ErrorTypeProviderComponent: UseType, @app.ErrorRaiserComponent.&'static str: diff --git a/crates/cgp-tests/tests/namespace_tests/redirect.rs b/crates/cgp-tests/tests/namespace_tests/redirect.rs index 083d3d3a..b42f959a 100644 --- a/crates/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/cgp-tests/tests/namespace_tests/redirect.rs @@ -20,9 +20,6 @@ pub struct App; delegate_components! { #[use_namespace] App { - > Component: - Component::Provider, - // @bar.*: TestProvider, @bar.baz.*: TestProvider, diff --git a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs index 089de423..ada8fe0e 100644 --- a/crates/cgp-tests/tests/namespace_tests/use_namespace.rs +++ b/crates/cgp-tests/tests/namespace_tests/use_namespace.rs @@ -12,9 +12,8 @@ pub trait CanDoFoo { pub struct App; delegate_components! { + #[use_namespace] App { - > - Component: Component::Provider, @cgp.core.error.ErrorTypeProviderComponent: UseType, } From 108255f0b3472af487577a222e42239b80df8059 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 16 Mar 2026 23:20:03 +0100 Subject: [PATCH 35/45] Add derive_namespace_delegate helper --- .../delegate_components/derive_namespace.rs | 49 +++++++++++++++++++ .../src/delegate_components/mod.rs | 2 + .../src/entrypoints/delegate_components.rs | 48 +++--------------- 3 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 crates/cgp-macro-lib/src/delegate_components/derive_namespace.rs diff --git a/crates/cgp-macro-lib/src/delegate_components/derive_namespace.rs b/crates/cgp-macro-lib/src/delegate_components/derive_namespace.rs new file mode 100644 index 00000000..071dc720 --- /dev/null +++ b/crates/cgp-macro-lib/src/delegate_components/derive_namespace.rs @@ -0,0 +1,49 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{Generics, Ident, Type, parse2}; + +pub fn derive_namespace_delegate( + namespace: Option, + target_type: &Type, + target_generics: &Generics, +) -> syn::Result { + let namespace = namespace.unwrap_or_else(|| Ident::new("DefaultNamespace", Span::call_site())); + + let mut generics = target_generics.clone(); + generics.params.push(parse2(quote! { __Component__ })?); + + let impl_generics = generics.split_for_impl().0; + + let namespace_impl = quote! { + impl #impl_generics + DelegateComponent<__Component__> + for #target_type + where + __Component__: #namespace< #target_type >, + { + type Delegate = < __Component__ as #namespace< #target_type >>::Provider; + } + }; + + let mut generics = generics.clone(); + generics.params.push(parse2(quote! { __Context__ })?); + generics.params.push(parse2(quote! { __Params__ })?); + + let impl_generics = generics.split_for_impl().0; + + let is_provider_for_impl = quote! { + impl #impl_generics + IsProviderFor<__Component__, __Context__, __Params__> + for #target_type + where + __Component__: #namespace< #target_type >, + < __Component__ as #namespace< #target_type >>::Provider: IsProviderFor<__Component__, __Context__, __Params__>, + { + } + }; + + Ok(quote! { + #namespace_impl + #is_provider_for_impl + }) +} diff --git a/crates/cgp-macro-lib/src/delegate_components/mod.rs b/crates/cgp-macro-lib/src/delegate_components/mod.rs index 3f5a4f7c..3d9674c5 100644 --- a/crates/cgp-macro-lib/src/delegate_components/mod.rs +++ b/crates/cgp-macro-lib/src/delegate_components/mod.rs @@ -1,8 +1,10 @@ mod attributes; mod define_struct; +mod derive_namespace; mod impl_delegate; mod merge_generics; pub use attributes::*; pub use define_struct::*; +pub use derive_namespace::*; pub use impl_delegate::*; diff --git a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs index 7ec0bf34..92e83ae5 100644 --- a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs +++ b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs @@ -1,9 +1,10 @@ -use proc_macro2::{Span, TokenStream}; -use quote::{ToTokens, quote}; -use syn::{Ident, parse2}; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse2; use crate::delegate_components::{ - DelegateNamespaceAttribute, define_struct, impl_delegate_components, parse_delegate_attributes, + DelegateNamespaceAttribute, define_struct, derive_namespace_delegate, impl_delegate_components, + parse_delegate_attributes, }; use crate::parse::{DelegateComponents, SimpleType, TypeGenerics}; @@ -28,45 +29,10 @@ pub fn delegate_components(body: TokenStream) -> syn::Result { let attributes = parse_delegate_attributes(spec.attributes)?; if let Some(DelegateNamespaceAttribute { namespace }) = attributes.use_namespace { - let namespace = - namespace.unwrap_or_else(|| Ident::new("DefaultNamespace", Span::call_site())); - - let mut generics = target_generics.generics.clone(); - generics.params.push(parse2(quote! { __Component__ })?); - - let impl_generics = generics.split_for_impl().0; - - let namespace_impl = quote! { - impl #impl_generics - DelegateComponent<__Component__> - for #target_type - where - __Component__: #namespace< #target_type >, - { - type Delegate = < __Component__ as #namespace< #target_type >>::Provider; - } - }; + let namespace_impl = + derive_namespace_delegate(namespace, target_type, &target_generics.generics)?; output.extend(namespace_impl); - - let mut generics = generics.clone(); - generics.params.push(parse2(quote! { __Context__ })?); - generics.params.push(parse2(quote! { __Params__ })?); - - let impl_generics = generics.split_for_impl().0; - - let is_provider_for_impl = quote! { - impl #impl_generics - IsProviderFor<__Component__, __Context__, __Params__> - for #target_type - where - __Component__: #namespace< #target_type >, - < __Component__ as #namespace< #target_type >>::Provider: IsProviderFor<__Component__, __Context__, __Params__>, - { - } - }; - - output.extend(is_provider_for_impl); } let impl_items = impl_delegate_components(&target_type, &target_generics, &spec.entries)?; From e887d9434d2959913dd9073215242f3809cf025e Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 22:58:42 +0100 Subject: [PATCH 36/45] Implement multi-path ComponentPath type --- .../src/parse/delegate_components.rs | 3 +- crates/cgp-macro-lib/src/parse/mod.rs | 2 + crates/cgp-macro-lib/src/parse/path.rs | 148 ++++++++++++++++++ crates/cgp-macro-lib/src/symbol.rs | 10 +- 4 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 crates/cgp-macro-lib/src/parse/path.rs diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index d68d3f81..4ef6b475 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -399,7 +399,8 @@ impl Parse for PathType { let path_str = path_ident.to_string(); if let Some(path_char) = path_str.chars().next() { if path_char.is_ascii_lowercase() { - let path_symbol = symbol_from_string_spanned(path_ident.span(), &path_str)?; + let path_symbol = + parse2(symbol_from_string_spanned(path_ident.span(), &path_str))?; return Ok(Self { path_type: path_symbol, }); diff --git a/crates/cgp-macro-lib/src/parse/mod.rs b/crates/cgp-macro-lib/src/parse/mod.rs index 162be924..0c63770c 100644 --- a/crates/cgp-macro-lib/src/parse/mod.rs +++ b/crates/cgp-macro-lib/src/parse/mod.rs @@ -6,6 +6,7 @@ mod delegate_components; mod entry; mod impl_generics; mod is_provider_params; +mod path; mod simple_type; mod type_generics; mod type_spec; @@ -18,6 +19,7 @@ pub use delegate_components::*; pub use entry::*; pub use impl_generics::*; pub use is_provider_params::*; +pub use path::*; pub use simple_type::*; pub use type_generics::*; pub use type_spec::*; diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs new file mode 100644 index 00000000..e43c1314 --- /dev/null +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -0,0 +1,148 @@ +use proc_macro2::{TokenStream, TokenTree}; +use quote::{ToTokens, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Brace, Comma, Dot, Star}; +use syn::{Ident, Type, braced}; + +use crate::symbol::symbol_from_string_spanned; + +pub struct ComponentPath { + pub paths: Vec, +} + +impl Parse for ComponentPath { + fn parse(input: ParseStream) -> syn::Result { + let path_head = PathHead::parse(input)?; + + let mut paths = Vec::new(); + + for path in path_head.to_types() { + let path_type: Type = syn::parse2(path)?; + paths.push(path_type); + } + + Ok(Self { paths }) + } +} + +// pub struct ComponentPath { +// elements: Punctuated, +// } + +// impl Parse for ComponentPath { +// fn parse(input: ParseStream) -> syn::Result { +// let elements = Punctuated::parse_terminated(input)?; +// Ok(Self { elements }) +// } +// } + +// impl ComponentPath { +// pub fn to_types(&self) -> Vec { + +// todo!() +// } +// } + +// pub fn path_elements_to_types(mut elements: impl Iterator) -> Vec { +// if let Some(element) = elements.next() { +// let types = element.to_types(); +// let mut rest_types = path_elements_to_types(elements); +// todo!() +// } else { +// vec![ quote! { PathNil } ] +// } + +// } + +pub enum PathHead { + Type(Type, Box), + Symbol(Ident, Box), + Group(Punctuated), + Wildcard, + Nil, +} + +impl PathHead { + pub fn to_types(&self) -> Vec { + match self { + Self::Type(path_type, rest) => { + let rest_types = rest.to_types(); + rest_types + .into_iter() + .map(|rest_type| { + quote! { PathCons< #path_type , #rest_type > } + }) + .collect() + } + Self::Symbol(ident, rest) => { + let ident_str = ident.to_string(); + let path_type = symbol_from_string_spanned(ident.span(), &ident_str); + + let rest_types = rest.to_types(); + rest_types + .into_iter() + .map(|rest_type| { + quote! { PathCons< #path_type , #rest_type > } + }) + .collect() + } + Self::Group(paths) => paths.iter().flat_map(|path| path.to_types()).collect(), + Self::Wildcard => { + vec![quote! { __Wildcard__ }] + } + Self::Nil => { + vec![quote! { PathNil }] + } + } + } +} + +impl Parse for PathHead { + fn parse(input: ParseStream) -> syn::Result { + if input.is_empty() { + Ok(Self::Nil) + } else if input.peek(Star) { + let _: Star = input.parse()?; + Ok(Self::Wildcard) + } else if input.peek(Brace) { + let body; + braced!(body in input); + + let group = Punctuated::parse_terminated(&body)?; + + Ok(Self::Group(group)) + } else { + let path_type: Type = input.parse()?; + + let rest_path = if input.peek(Dot) { + let _: Dot = input.parse()?; + Box::new(Self::parse(input)?) + } else { + Box::new(Self::Nil) + }; + + if let Some(path_ident) = path_type_as_ident(&path_type) { + Ok(Self::Symbol(path_ident, rest_path)) + } else { + Ok(Self::Type(path_type, rest_path)) + } + } + } +} + +pub fn path_type_as_ident(path_type: &Type) -> Option { + let path_tokens = path_type.to_token_stream().into_iter().collect::>(); + let [path_token]: [TokenTree; 1] = path_tokens.try_into().ok()?; + + if let TokenTree::Ident(path_ident) = path_token { + let path_str = path_ident.to_string(); + if let Some(path_char) = path_str.chars().next() { + if path_char.is_ascii_lowercase() { + return Some(path_ident); + } + } + } + + None +} diff --git a/crates/cgp-macro-lib/src/symbol.rs b/crates/cgp-macro-lib/src/symbol.rs index adaf6271..5768b3ff 100644 --- a/crates/cgp-macro-lib/src/symbol.rs +++ b/crates/cgp-macro-lib/src/symbol.rs @@ -3,10 +3,10 @@ use quote::{ToTokens, quote_spanned}; use syn::{LitStr, Type, parse2}; pub fn symbol_from_string(value: &str) -> syn::Result { - symbol_from_string_spanned(Span::call_site(), value) + parse2(symbol_from_string_spanned(Span::call_site(), value)) } -pub fn symbol_from_string_spanned(span: Span, value: &str) -> syn::Result { +pub fn symbol_from_string_spanned(span: Span, value: &str) -> TokenStream { let mut chars = quote_spanned! { span => ε }; for c in value.chars().rev() { @@ -15,13 +15,13 @@ pub fn symbol_from_string_spanned(span: Span, value: &str) -> syn::Result let len = Literal::usize_unsuffixed(value.len()); - parse2(quote_spanned! { span => ψ< #len, #chars > }) + quote_spanned! { span => ψ< #len, #chars > } } pub fn make_symbol(input: TokenStream) -> syn::Result { let literal: LitStr = syn::parse2(input)?; - let symbol = symbol_from_string_spanned(literal.span(), &literal.value())?; + let symbol = symbol_from_string_spanned(literal.span(), &literal.value()); - Ok(symbol.to_token_stream()) + Ok(symbol) } From d268b5522a766c599b9a9e1aad31d8956e303136 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 23:14:44 +0100 Subject: [PATCH 37/45] Propagate wildcard info --- crates/cgp-macro-lib/src/parse/path.rs | 22 ++++++++++++---------- crates/cgp-macro-lib/src/symbol.rs | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index e43c1314..593af857 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -8,7 +8,7 @@ use syn::{Ident, Type, braced}; use crate::symbol::symbol_from_string_spanned; pub struct ComponentPath { - pub paths: Vec, + pub paths: Vec<(Type, bool)>, } impl Parse for ComponentPath { @@ -17,9 +17,9 @@ impl Parse for ComponentPath { let mut paths = Vec::new(); - for path in path_head.to_types() { + for (path, has_wildcard) in path_head.to_types() { let path_type: Type = syn::parse2(path)?; - paths.push(path_type); + paths.push((path_type, has_wildcard)); } Ok(Self { paths }) @@ -64,14 +64,15 @@ pub enum PathHead { } impl PathHead { - pub fn to_types(&self) -> Vec { + pub fn to_types(&self) -> Vec<(TokenStream, bool)> { match self { Self::Type(path_type, rest) => { let rest_types = rest.to_types(); rest_types .into_iter() - .map(|rest_type| { - quote! { PathCons< #path_type , #rest_type > } + .map(|(rest_type, has_wildcard)| { + let new_path = quote! { PathCons< #path_type , #rest_type > }; + (new_path, has_wildcard) }) .collect() } @@ -82,17 +83,18 @@ impl PathHead { let rest_types = rest.to_types(); rest_types .into_iter() - .map(|rest_type| { - quote! { PathCons< #path_type , #rest_type > } + .map(|(rest_type, has_wildcard)| { + let new_path = quote! { PathCons< #path_type , #rest_type > }; + (new_path, has_wildcard) }) .collect() } Self::Group(paths) => paths.iter().flat_map(|path| path.to_types()).collect(), Self::Wildcard => { - vec![quote! { __Wildcard__ }] + vec![(quote! { __Wildcard__ }, true)] } Self::Nil => { - vec![quote! { PathNil }] + vec![(quote! { PathNil }, false)] } } } diff --git a/crates/cgp-macro-lib/src/symbol.rs b/crates/cgp-macro-lib/src/symbol.rs index 5768b3ff..c7b9ff51 100644 --- a/crates/cgp-macro-lib/src/symbol.rs +++ b/crates/cgp-macro-lib/src/symbol.rs @@ -1,5 +1,5 @@ use proc_macro2::{Literal, Span, TokenStream}; -use quote::{ToTokens, quote_spanned}; +use quote::quote_spanned; use syn::{LitStr, Type, parse2}; pub fn symbol_from_string(value: &str) -> syn::Result { From 34f20be2ef76f60882b716a60f1283eb8db0d914 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 23:24:06 +0100 Subject: [PATCH 38/45] Use new path in single mode --- .../src/parse/delegate_components.rs | 137 ++++-------------- crates/cgp-macro-lib/src/parse/path.rs | 53 +++---- 2 files changed, 48 insertions(+), 142 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 4ef6b475..3c1a0722 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -1,16 +1,14 @@ use core::iter; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt, quote}; use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{At, Bracket, Colon, Comma, Dot, Gt, Lt, Pound, RArrow, Star}; -use syn::{Attribute, Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote, parse2}; +use syn::token::{At, Bracket, Colon, Comma, Gt, Lt, Pound, RArrow}; +use syn::{Attribute, Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote}; -use crate::parse::{ImplGenerics, TypeGenerics}; -use crate::symbol::symbol_from_string_spanned; +use crate::parse::{ComponentPath, ImplGenerics, SimpleType, TypeGenerics}; pub struct DelegateComponents { pub attributes: Vec, @@ -119,7 +117,7 @@ impl Parse for DelegateComponents { impl Parse for DelegateEntry where - Type: Parse, + DelegateKey: Parse, { fn parse(input: ParseStream) -> syn::Result { let components = if input.peek(Bracket) { @@ -143,10 +141,24 @@ where } } -impl Parse for DelegateKey -where - Type: Parse, -{ +impl Parse for DelegateKey { + fn parse(input: ParseStream) -> syn::Result { + let component_generics: ImplGenerics = if input.peek(Lt) { + input.parse()? + } else { + Default::default() + }; + + let component_type: SimpleType = input.parse()?; + + Ok(Self { + ty: component_type, + generics: component_generics, + }) + } +} + +impl Parse for DelegateKey { fn parse(input: ParseStream) -> syn::Result { let mut component_generics: ImplGenerics = if input.peek(Lt) { input.parse()? @@ -158,16 +170,15 @@ where let _: At = input.parse()?; let path: ComponentPath = input.parse()?; + let (path_type, is_wildcard) = path.paths[0].clone(); - if path.wildcard { + if is_wildcard { component_generics .generics .params .push(parse_quote!(__Wildcard__)); } - let path_type = parse2(path.to_type())?; - Ok(Self { ty: path_type, generics: component_generics, @@ -313,101 +324,3 @@ impl ToTokens for DelegateNewValue { }); } } - -pub struct ComponentPath { - pub elements: Vec, - pub wildcard: bool, -} - -impl Parse for ComponentPath { - fn parse(input: ParseStream) -> syn::Result { - let raw_elements: Punctuated = - Punctuated::parse_separated_nonempty(input)?; - - let mut elements = Vec::new(); - let mut wildcard = false; - - for element in raw_elements { - match element { - PathElement::Type(ty) => { - if wildcard { - return Err(Error::new(ty.span(), "unexpected component after wildcard")); - } - - elements.push(ty); - } - PathElement::Wildcard => wildcard = true, - } - } - - if elements.is_empty() { - return Err(Error::new( - input.span(), - "expect at least one component in component path", - )); - } - - Ok(Self { elements, wildcard }) - } -} - -impl ComponentPath { - pub fn to_type(&self) -> TokenStream { - let mut out = if self.wildcard { - quote! { __Wildcard__ } - } else { - quote! { PathNil } - }; - - for element in self.elements.iter().rev() { - out = quote! { PathCons< #element, #out> }; - } - - out - } -} - -pub enum PathElement { - Type(Type), - Wildcard, -} - -impl Parse for PathElement { - fn parse(input: ParseStream) -> syn::Result { - if input.peek(Star) { - let _: Star = input.parse()?; - Ok(Self::Wildcard) - } else { - let PathType { path_type } = input.parse()?; - Ok(Self::Type(path_type)) - } - } -} - -pub struct PathType { - pub path_type: Type, -} - -impl Parse for PathType { - fn parse(input: ParseStream) -> syn::Result { - let path_type: Type = input.parse()?; - - let path_tokens = path_type.to_token_stream().into_iter().collect::>(); - let path_token: Result<[TokenTree; 1], _> = path_tokens.try_into(); - - if let Ok([TokenTree::Ident(path_ident)]) = path_token { - let path_str = path_ident.to_string(); - if let Some(path_char) = path_str.chars().next() { - if path_char.is_ascii_lowercase() { - let path_symbol = - parse2(symbol_from_string_spanned(path_ident.span(), &path_str))?; - return Ok(Self { - path_type: path_symbol, - }); - } - } - } - - Ok(Self { path_type }) - } -} diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index 593af857..2cd7f7ad 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -3,7 +3,7 @@ use quote::{ToTokens, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Brace, Comma, Dot, Star}; -use syn::{Ident, Type, braced}; +use syn::{Ident, Type, braced, parse2}; use crate::symbol::symbol_from_string_spanned; @@ -26,35 +26,6 @@ impl Parse for ComponentPath { } } -// pub struct ComponentPath { -// elements: Punctuated, -// } - -// impl Parse for ComponentPath { -// fn parse(input: ParseStream) -> syn::Result { -// let elements = Punctuated::parse_terminated(input)?; -// Ok(Self { elements }) -// } -// } - -// impl ComponentPath { -// pub fn to_types(&self) -> Vec { - -// todo!() -// } -// } - -// pub fn path_elements_to_types(mut elements: impl Iterator) -> Vec { -// if let Some(element) = elements.next() { -// let types = element.to_types(); -// let mut rest_types = path_elements_to_types(elements); -// todo!() -// } else { -// vec![ quote! { PathNil } ] -// } - -// } - pub enum PathHead { Type(Type, Box), Symbol(Ident, Box), @@ -148,3 +119,25 @@ pub fn path_type_as_ident(path_type: &Type) -> Option { None } + +pub struct PathType { + pub path_type: Type, +} + +impl Parse for PathType { + fn parse(input: ParseStream) -> syn::Result { + let path_type: Type = input.parse()?; + + if let Some(path_ident) = path_type_as_ident(&path_type) { + let path_symbol = parse2(symbol_from_string_spanned( + path_ident.span(), + &path_ident.to_string(), + ))?; + Ok(Self { + path_type: path_symbol, + }) + } else { + Ok(Self { path_type }) + } + } +} From e4471e0141af644ba131f786c87d7431a28957c8 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 23:45:46 +0100 Subject: [PATCH 39/45] Parse multi-path in DelegateEntry --- .../src/parse/delegate_components.rs | 51 +++++++++++++++++-- .../tests/namespace_tests/experiments.rs | 7 ++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index 3c1a0722..f97a3ce0 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -115,15 +115,35 @@ impl Parse for DelegateComponents { } } -impl Parse for DelegateEntry -where - DelegateKey: Parse, -{ +impl Parse for DelegateEntry { fn parse(input: ParseStream) -> syn::Result { let components = if input.peek(Bracket) { let components_body; bracketed!(components_body in input); components_body.parse_terminated(DelegateKey::parse, Token![,])? + } else if input.peek(At) { + let _: At = input.parse()?; + + let path: ComponentPath = input.parse()?; + + let mut keys = Punctuated::new(); + + for (path_type, is_wildcard) in path.paths { + let mut generics = ImplGenerics::default(); + + if is_wildcard { + generics.generics.params.push(parse_quote!(__Wildcard__)); + } + + let key = DelegateKey { + ty: path_type, + generics, + }; + + keys.push(key); + } + + keys } else { let component: DelegateKey = input.parse()?; Punctuated::from_iter(iter::once(component)) @@ -141,6 +161,29 @@ where } } +impl Parse for DelegateEntry { + fn parse(input: ParseStream) -> syn::Result { + let components = if input.peek(Bracket) { + let components_body; + bracketed!(components_body in input); + components_body.parse_terminated(DelegateKey::parse, Token![,])? + } else { + let component: DelegateKey = input.parse()?; + Punctuated::from_iter(iter::once(component)) + }; + + let mode = input.parse()?; + + let source = input.parse()?; + + Ok(Self { + keys: components, + mode, + value: source, + }) + } +} + impl Parse for DelegateKey { fn parse(input: ParseStream) -> syn::Result { let component_generics: ImplGenerics = if input.peek(Lt) { diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index 3dd0c33e..54f19ae0 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -1,4 +1,4 @@ -use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; +use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent, ErrorWrapperComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; @@ -11,7 +11,10 @@ delegate_components! { App { @app.ErrorTypeProviderComponent: UseType, - @app.ErrorRaiserComponent.&'static str: + @app.{ + ErrorRaiserComponent.{&'static str, String}, + ErrorWrapperComponent, + }: RaiseFrom, TryComputerComponent: Foo, From 8e747340ed6da2f31705ef56762a43bdfe1246b6 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 23:48:36 +0100 Subject: [PATCH 40/45] Remove path parsing in DelegateKey --- .../src/parse/delegate_components.rs | 40 +------------------ 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index f97a3ce0..f727bc44 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -184,7 +184,7 @@ impl Parse for DelegateEntry { } } -impl Parse for DelegateKey { +impl Parse for DelegateKey { fn parse(input: ParseStream) -> syn::Result { let component_generics: ImplGenerics = if input.peek(Lt) { input.parse()? @@ -192,7 +192,7 @@ impl Parse for DelegateKey { Default::default() }; - let component_type: SimpleType = input.parse()?; + let component_type: Type = input.parse()?; Ok(Self { ty: component_type, @@ -201,42 +201,6 @@ impl Parse for DelegateKey { } } -impl Parse for DelegateKey { - fn parse(input: ParseStream) -> syn::Result { - let mut component_generics: ImplGenerics = if input.peek(Lt) { - input.parse()? - } else { - Default::default() - }; - - if input.peek(At) { - let _: At = input.parse()?; - - let path: ComponentPath = input.parse()?; - let (path_type, is_wildcard) = path.paths[0].clone(); - - if is_wildcard { - component_generics - .generics - .params - .push(parse_quote!(__Wildcard__)); - } - - Ok(Self { - ty: path_type, - generics: component_generics, - }) - } else { - let component_type: Type = input.parse()?; - - Ok(Self { - ty: component_type, - generics: component_generics, - }) - } - } -} - impl Parse for DelegateMode { fn parse(input: ParseStream) -> syn::Result { if input.peek(RArrow) { From 4773240106395e7bd2c4134adb2f5d5bb540798d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Mar 2026 23:56:51 +0100 Subject: [PATCH 41/45] Pass generics directly --- .../src/parse/delegate_components.rs | 16 ++--- crates/cgp-macro-lib/src/parse/path.rs | 62 +++++++++++++------ 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index f727bc44..d5a98895 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::token::{At, Bracket, Colon, Comma, Gt, Lt, Pound, RArrow}; use syn::{Attribute, Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote}; -use crate::parse::{ComponentPath, ImplGenerics, SimpleType, TypeGenerics}; +use crate::parse::{ComponentPaths, ImplGenerics, SimpleType, TypeGenerics}; pub struct DelegateComponents { pub attributes: Vec, @@ -124,20 +124,14 @@ impl Parse for DelegateEntry { } else if input.peek(At) { let _: At = input.parse()?; - let path: ComponentPath = input.parse()?; + let path: ComponentPaths = input.parse()?; let mut keys = Punctuated::new(); - for (path_type, is_wildcard) in path.paths { - let mut generics = ImplGenerics::default(); - - if is_wildcard { - generics.generics.params.push(parse_quote!(__Wildcard__)); - } - + for path in path.paths { let key = DelegateKey { - ty: path_type, - generics, + ty: path.path_type, + generics: path.generics, }; keys.push(key); diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index 2cd7f7ad..64baa9b9 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -3,29 +3,38 @@ use quote::{ToTokens, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Brace, Comma, Dot, Star}; -use syn::{Ident, Type, braced, parse2}; +use syn::{Ident, Type, braced, parse_quote, parse2}; +use crate::parse::ImplGenerics; use crate::symbol::symbol_from_string_spanned; -pub struct ComponentPath { - pub paths: Vec<(Type, bool)>, +pub struct ComponentPaths { + pub paths: Vec>, } -impl Parse for ComponentPath { +impl Parse for ComponentPaths { fn parse(input: ParseStream) -> syn::Result { let path_head = PathHead::parse(input)?; let mut paths = Vec::new(); - for (path, has_wildcard) in path_head.to_types() { - let path_type: Type = syn::parse2(path)?; - paths.push((path_type, has_wildcard)); + for path in path_head.to_paths() { + let path_type: Type = syn::parse2(path.path_type)?; + paths.push(ComponentPath { + path_type, + generics: path.generics, + }); } Ok(Self { paths }) } } +pub struct ComponentPath { + pub path_type: Path, + pub generics: ImplGenerics, +} + pub enum PathHead { Type(Type, Box), Symbol(Ident, Box), @@ -35,15 +44,20 @@ pub enum PathHead { } impl PathHead { - pub fn to_types(&self) -> Vec<(TokenStream, bool)> { + pub fn to_paths(&self) -> Vec> { match self { Self::Type(path_type, rest) => { - let rest_types = rest.to_types(); + let rest_types = rest.to_paths(); rest_types .into_iter() - .map(|(rest_type, has_wildcard)| { - let new_path = quote! { PathCons< #path_type , #rest_type > }; - (new_path, has_wildcard) + .map(|path| { + let rest_tokens = path.path_type; + + let new_path = quote! { PathCons< #path_type , #rest_tokens > }; + ComponentPath { + path_type: new_path, + generics: path.generics, + } }) .collect() } @@ -51,21 +65,31 @@ impl PathHead { let ident_str = ident.to_string(); let path_type = symbol_from_string_spanned(ident.span(), &ident_str); - let rest_types = rest.to_types(); + let rest_types = rest.to_paths(); rest_types .into_iter() - .map(|(rest_type, has_wildcard)| { - let new_path = quote! { PathCons< #path_type , #rest_type > }; - (new_path, has_wildcard) + .map(|path| { + let rest_tokens = path.path_type; + let new_path = quote! { PathCons< #path_type , #rest_tokens > }; + ComponentPath { + path_type: new_path, + generics: path.generics, + } }) .collect() } - Self::Group(paths) => paths.iter().flat_map(|path| path.to_types()).collect(), + Self::Group(paths) => paths.iter().flat_map(|path| path.to_paths()).collect(), Self::Wildcard => { - vec![(quote! { __Wildcard__ }, true)] + vec![ComponentPath { + path_type: quote! { __Wildcard__ }, + generics: parse_quote! { <__Wildcard__> }, + }] } Self::Nil => { - vec![(quote! { PathNil }, false)] + vec![ComponentPath { + path_type: quote! { PathNil }, + generics: Default::default(), + }] } } } From 29e03c3aed8d0f26ca605403f719bdca426b53d0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 19 Mar 2026 22:02:24 +0100 Subject: [PATCH 42/45] Disallow empty path --- crates/cgp-macro-lib/src/parse/path.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index 64baa9b9..2787d4d9 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -16,6 +16,13 @@ impl Parse for ComponentPaths { fn parse(input: ParseStream) -> syn::Result { let path_head = PathHead::parse(input)?; + if let PathHead::Nil = path_head { + return Err(syn::Error::new( + input.span(), + "Expected at least one path element", + )); + } + let mut paths = Vec::new(); for path in path_head.to_paths() { From 0da56dd3a896eea61aecd369aafbb30f8a959240 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 19 Mar 2026 22:11:11 +0100 Subject: [PATCH 43/45] Allow generic parameters to be specified in path --- crates/cgp-macro-lib/src/parse/path.rs | 39 ++++++++++++++----- .../tests/namespace_tests/experiments.rs | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index 2787d4d9..b378887c 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -2,7 +2,7 @@ use proc_macro2::{TokenStream, TokenTree}; use quote::{ToTokens, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::token::{Brace, Comma, Dot, Star}; +use syn::token::{Brace, Comma, Dot, Lt, Star}; use syn::{Ident, Type, braced, parse_quote, parse2}; use crate::parse::ImplGenerics; @@ -43,8 +43,8 @@ pub struct ComponentPath { } pub enum PathHead { - Type(Type, Box), - Symbol(Ident, Box), + Type(Option, Type, Box), + Symbol(Option, Ident, Box), Group(Punctuated), Wildcard, Nil, @@ -53,13 +53,20 @@ pub enum PathHead { impl PathHead { pub fn to_paths(&self) -> Vec> { match self { - Self::Type(path_type, rest) => { + Self::Type(generics, path_type, rest) => { let rest_types = rest.to_paths(); rest_types .into_iter() - .map(|path| { + .map(|mut path| { let rest_tokens = path.path_type; + if let Some(generics) = generics { + path.generics + .generics + .params + .extend(generics.generics.params.clone()); + } + let new_path = quote! { PathCons< #path_type , #rest_tokens > }; ComponentPath { path_type: new_path, @@ -68,15 +75,23 @@ impl PathHead { }) .collect() } - Self::Symbol(ident, rest) => { + Self::Symbol(generics, ident, rest) => { let ident_str = ident.to_string(); let path_type = symbol_from_string_spanned(ident.span(), &ident_str); let rest_types = rest.to_paths(); rest_types .into_iter() - .map(|path| { + .map(|mut path| { let rest_tokens = path.path_type; + + if let Some(generics) = generics { + path.generics + .generics + .params + .extend(generics.generics.params.clone()); + } + let new_path = quote! { PathCons< #path_type , #rest_tokens > }; ComponentPath { path_type: new_path, @@ -117,6 +132,12 @@ impl Parse for PathHead { Ok(Self::Group(group)) } else { + let generics = if input.peek(Lt) { + Some(input.parse()?) + } else { + None + }; + let path_type: Type = input.parse()?; let rest_path = if input.peek(Dot) { @@ -127,9 +148,9 @@ impl Parse for PathHead { }; if let Some(path_ident) = path_type_as_ident(&path_type) { - Ok(Self::Symbol(path_ident, rest_path)) + Ok(Self::Symbol(generics, path_ident, rest_path)) } else { - Ok(Self::Type(path_type, rest_path)) + Ok(Self::Type(generics, path_type, rest_path)) } } } diff --git a/crates/cgp-tests/tests/namespace_tests/experiments.rs b/crates/cgp-tests/tests/namespace_tests/experiments.rs index 54f19ae0..b0b90a97 100644 --- a/crates/cgp-tests/tests/namespace_tests/experiments.rs +++ b/crates/cgp-tests/tests/namespace_tests/experiments.rs @@ -13,7 +13,7 @@ delegate_components! { UseType, @app.{ ErrorRaiserComponent.{&'static str, String}, - ErrorWrapperComponent, + ErrorWrapperComponent.*, }: RaiseFrom, TryComputerComponent: From 3d6a433256306eacdade594f19db732f7077fa21 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 19 Mar 2026 22:14:56 +0100 Subject: [PATCH 44/45] Add prepend path helper --- crates/cgp-macro-lib/src/parse/path.rs | 67 +++++++++++--------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index b378887c..067ddd0c 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -55,50 +55,15 @@ impl PathHead { match self { Self::Type(generics, path_type, rest) => { let rest_types = rest.to_paths(); - rest_types - .into_iter() - .map(|mut path| { - let rest_tokens = path.path_type; - - if let Some(generics) = generics { - path.generics - .generics - .params - .extend(generics.generics.params.clone()); - } - - let new_path = quote! { PathCons< #path_type , #rest_tokens > }; - ComponentPath { - path_type: new_path, - generics: path.generics, - } - }) - .collect() + + prepend_path(path_type.to_token_stream(), generics.clone(), rest_types) } Self::Symbol(generics, ident, rest) => { let ident_str = ident.to_string(); let path_type = symbol_from_string_spanned(ident.span(), &ident_str); let rest_types = rest.to_paths(); - rest_types - .into_iter() - .map(|mut path| { - let rest_tokens = path.path_type; - - if let Some(generics) = generics { - path.generics - .generics - .params - .extend(generics.generics.params.clone()); - } - - let new_path = quote! { PathCons< #path_type , #rest_tokens > }; - ComponentPath { - path_type: new_path, - generics: path.generics, - } - }) - .collect() + prepend_path(path_type, generics.clone(), rest_types) } Self::Group(paths) => paths.iter().flat_map(|path| path.to_paths()).collect(), Self::Wildcard => { @@ -117,6 +82,32 @@ impl PathHead { } } +pub fn prepend_path( + path_type: TokenStream, + generics: Option, + rest_types: Vec>, +) -> Vec> { + rest_types + .into_iter() + .map(|mut path| { + let rest_tokens = path.path_type; + + if let Some(generics) = &generics { + path.generics + .generics + .params + .extend(generics.generics.params.clone()); + } + + let new_path = quote! { PathCons< #path_type , #rest_tokens > }; + ComponentPath { + path_type: new_path, + generics: path.generics, + } + }) + .collect() +} + impl Parse for PathHead { fn parse(input: ParseStream) -> syn::Result { if input.is_empty() { From f42554d9550538bb1939505c3a8d3b3bcfabb4d1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 26 Mar 2026 20:59:26 +0100 Subject: [PATCH 45/45] Fix clippy --- crates/cgp-macro-lib/src/derive_component/derive.rs | 2 +- .../cgp-macro-lib/src/derive_component/provider_impl.rs | 2 +- .../cgp-macro-lib/src/entrypoints/delegate_components.rs | 2 +- crates/cgp-macro-lib/src/parse/path.rs | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index 3f654ae5..75c0324a 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -91,7 +91,7 @@ pub fn derive_component_with_ast( } } - let namespace_impls = derive_namespace_impls(&attributes.use_namespace, &component_name)?; + let namespace_impls = derive_namespace_impls(&attributes.use_namespace, component_name)?; item_impls.extend(namespace_impls); let derived = DerivedComponent { diff --git a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs index 0a6a474f..3dcbe882 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_impl.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_impl.rs @@ -103,7 +103,7 @@ pub fn derive_provider_item_impls( for trait_item in provider_trait.items.iter() { match &trait_item { TraitItem::Fn(trait_fn) => { - let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, &delegate_type)?; + let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, delegate_type)?; impl_items.push(ImplItem::Fn(impl_fn)) } diff --git a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs index 92e83ae5..f43a873f 100644 --- a/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs +++ b/crates/cgp-macro-lib/src/entrypoints/delegate_components.rs @@ -35,7 +35,7 @@ pub fn delegate_components(body: TokenStream) -> syn::Result { output.extend(namespace_impl); } - let impl_items = impl_delegate_components(&target_type, &target_generics, &spec.entries)?; + let impl_items = impl_delegate_components(target_type, target_generics, &spec.entries)?; output.extend(impl_items); diff --git a/crates/cgp-macro-lib/src/parse/path.rs b/crates/cgp-macro-lib/src/parse/path.rs index 067ddd0c..ebc09cd4 100644 --- a/crates/cgp-macro-lib/src/parse/path.rs +++ b/crates/cgp-macro-lib/src/parse/path.rs @@ -153,10 +153,10 @@ pub fn path_type_as_ident(path_type: &Type) -> Option { if let TokenTree::Ident(path_ident) = path_token { let path_str = path_ident.to_string(); - if let Some(path_char) = path_str.chars().next() { - if path_char.is_ascii_lowercase() { - return Some(path_ident); - } + if let Some(path_char) = path_str.chars().next() + && path_char.is_ascii_lowercase() + { + return Some(path_ident); } }