From d7b0ea653678541bad5ea248a6e505498749ff63 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 19:23:41 +0000 Subject: [PATCH 01/12] Draft implement new #[cgp_impl] macro --- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/traits/mod.rs | 2 + crates/cgp-component/src/traits/refl.rs | 7 + .../cgp-macro-lib/src/derive_component/mod.rs | 1 + .../src/derive_component/provider_trait.rs | 2 +- .../derive_component/replace_self_receiver.rs | 6 +- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 136 ++++++++++++++++++ crates/cgp-macro-lib/src/entrypoints/mod.rs | 2 + 8 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 crates/cgp-component/src/traits/refl.rs create mode 100644 crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 3e9c86a9..6a731f6f 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -8,5 +8,5 @@ mod traits; mod types; -pub use traits::{CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor}; +pub use traits::{CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor, Refl}; pub use types::{UseContext, UseDelegate, UseFields, WithContext, WithProvider}; diff --git a/crates/cgp-component/src/traits/mod.rs b/crates/cgp-component/src/traits/mod.rs index 16104b7e..ceb106e6 100644 --- a/crates/cgp-component/src/traits/mod.rs +++ b/crates/cgp-component/src/traits/mod.rs @@ -2,8 +2,10 @@ mod can_use_component; mod delegate_component; mod has_provider; mod is_provider; +mod refl; pub use can_use_component::*; pub use delegate_component::DelegateComponent; pub use has_provider::HasCgpProvider; pub use is_provider::*; +pub use refl::*; diff --git a/crates/cgp-component/src/traits/refl.rs b/crates/cgp-component/src/traits/refl.rs new file mode 100644 index 00000000..cb1e6cc3 --- /dev/null +++ b/crates/cgp-component/src/traits/refl.rs @@ -0,0 +1,7 @@ +pub trait Refl { + type Type; +} + +impl Refl for T { + type Type = T; +} diff --git a/crates/cgp-macro-lib/src/derive_component/mod.rs b/crates/cgp-macro-lib/src/derive_component/mod.rs index 3398b32e..9a1b035c 100644 --- a/crates/cgp-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-macro-lib/src/derive_component/mod.rs @@ -13,5 +13,6 @@ mod use_context_impl; mod use_delegate_impl; pub use derive::*; +pub use replace_self_receiver::*; pub use replace_self_type::*; pub use snake_case::*; diff --git a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs index cc432542..1cc4233f 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs @@ -89,7 +89,7 @@ pub fn derive_provider_trait( parse_and_replace_self_type(item, context_type, &local_assoc_types)?; if let TraitItem::Fn(func) = &mut replaced_item { - replace_self_receiver(func, context_type); + replace_self_receiver(&mut func.sig, context_type); } *item = replaced_item; diff --git a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs index b9493ac9..175ddcb3 100644 --- a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs +++ b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs @@ -1,10 +1,10 @@ use proc_macro2::Ident; -use syn::{FnArg, TraitItemFn, parse_quote}; +use syn::{FnArg, Signature, parse_quote}; use crate::derive_component::snake_case::to_snake_case_ident; -pub fn replace_self_receiver(func: &mut TraitItemFn, replaced_type: &Ident) { - if let Some(arg) = func.sig.inputs.first_mut() +pub fn replace_self_receiver(sig: &mut Signature, replaced_type: &Ident) { + if let Some(arg) = sig.inputs.first_mut() && let FnArg::Receiver(receiver) = arg { let replaced_var = to_snake_case_ident(replaced_type); diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs new file mode 100644 index 00000000..dd216f79 --- /dev/null +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -0,0 +1,136 @@ +use proc_macro2::{Group, Span, TokenStream, TokenTree}; +use quote::{ToTokens, format_ident, quote}; +use syn::token::For; +use syn::{Ident, ImplItem, ItemImpl, Type, parse2}; + +use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; +use crate::parse::SimpleType; + +pub fn transform_impl_trait( + item_impl: &ItemImpl, + provider_trait_ident: &Ident, + provider_type: &Type, +) -> syn::Result { + let context_type = item_impl.self_ty.as_ref(); + + let (context_ident, use_refl) = + if let Some(ident) = parse2::(context_type.to_token_stream()).ok() { + (ident, false) + } else { + (Ident::new("__Context__", Span::call_site()), true) + }; + + let local_assoc_types: Vec = item_impl + .items + .iter() + .filter_map(|item| { + if let ImplItem::Type(assoc_type) = item { + Some(assoc_type.ident.clone()) + } else { + None + } + }) + .collect(); + + let raw_out_impl = replace_self_type( + item_impl.to_token_stream(), + &context_ident, + &local_assoc_types, + ); + + let mut out_impl: ItemImpl = parse2(raw_out_impl)?; + out_impl.self_ty = Box::new(provider_type.clone()); + + let source_trait_path = &item_impl.trait_.as_ref().unwrap().1; + let mut provider_trait_path: SimpleType = parse2(source_trait_path.to_token_stream())?; + provider_trait_path.name = provider_trait_ident.clone(); + + match &mut provider_trait_path.generics { + Some(generics) => { + generics + .args + .insert(0, parse2(context_ident.to_token_stream())?); + } + None => { + provider_trait_path.generics = Some(parse2(quote! { < #context_ident > })?); + } + } + + out_impl.trait_ = Some(( + None, + parse2(provider_trait_path.to_token_stream())?, + For(Span::call_site()), + )); + + if use_refl { + let where_clause = out_impl.generics.make_where_clause(); + where_clause.predicates.push(parse2(quote! { + #context_ident: Refl + })?); + } + + // let mut provider_trait_spec = item_impl.trait_.clone().unwrap(); + // let provider_trait_path = &mut provider_trait_spec.1; + // let segment = provider_trait_path.segments.last_mut().unwrap(); + + // match &mut segment.arguments { + // PathArguments::None => { + // segment.arguments = PathArguments::AngleBracketed(parse2(quote! { < #context_ident > })?); + // } + // PathArguments::AngleBracketed(args) => { + // args.args.insert(0, parse2(quote! { #context_ident })?); + // } + // _ => { + // return Err(Error::new(segment.span(), "trait path must end with angle bracket generic arguments")) + // } + // } + + // let provider_trait_path = item_impl.trait_.unwrap().1.clone(); + // out_impl.trait_ = Some((None, provider_trait_path, For(Span::call_site()))); + + // let context_param: GenericParam = parse2(context_type.to_token_stream())?; + // out_impl.generics.params.insert(0, context_param); + + for item in out_impl.items.iter_mut() { + if let ImplItem::Fn(item_fn) = item { + replace_self_receiver(&mut item_fn.sig, &context_ident); + let context_var = to_snake_case_ident(&context_ident); + let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); + item_fn.block = parse2(replaced_block)?; + } + } + + Ok(out_impl) +} + +fn replace_self_var(stream: TokenStream, replaced_ident: &Ident) -> TokenStream { + let self_ident = format_ident!("self"); + + let mut result_stream: Vec = Vec::new(); + + let mut token_iter = stream.into_iter(); + + while let Some(tree) = token_iter.next() { + match tree { + TokenTree::Ident(ident) => { + if ident == self_ident { + result_stream.push(TokenTree::Ident(replaced_ident.clone())); + } else { + result_stream.push(TokenTree::Ident(ident)); + } + } + TokenTree::Group(group) => { + let replaced_stream = replace_self_var(group.stream(), replaced_ident); + let replaced_group = Group::new(group.delimiter(), replaced_stream); + + result_stream.push(TokenTree::Group(replaced_group)); + } + TokenTree::Punct(punct) => { + result_stream.push(TokenTree::Punct(punct)); + } + TokenTree::Literal(lit) => result_stream.push(TokenTree::Literal(lit)), + } + } + + result_stream.into_iter().collect() +} diff --git a/crates/cgp-macro-lib/src/entrypoints/mod.rs b/crates/cgp-macro-lib/src/entrypoints/mod.rs index ba5bdc44..16aa4233 100644 --- a/crates/cgp-macro-lib/src/entrypoints/mod.rs +++ b/crates/cgp-macro-lib/src/entrypoints/mod.rs @@ -4,6 +4,7 @@ mod cgp_component; mod cgp_context; mod cgp_data; mod cgp_getter; +mod cgp_impl; mod cgp_new_provider; mod cgp_preset; mod cgp_provider; @@ -26,6 +27,7 @@ pub use cgp_component::*; pub use cgp_context::*; pub use cgp_data::*; pub use cgp_getter::*; +pub use cgp_impl::*; pub use cgp_new_provider::*; pub use cgp_preset::*; pub use cgp_provider::*; From e29724252034620c3a909e3413de468c234630bf Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 19:32:32 +0000 Subject: [PATCH 02/12] Finish draft #[cgp_impl] macro --- crates/cgp-core/src/prelude.rs | 6 ++-- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 34 ++++++++++++++++++- crates/cgp-macro/src/lib.rs | 7 ++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index d4a02ac6..f918fb98 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, HasCgpProvider, IsProviderFor, UseContext, UseDelegate, - UseFields, WithContext, WithProvider, + CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor, Refl, UseContext, + UseDelegate, UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; @@ -18,7 +18,7 @@ pub use cgp_field::types::{ }; pub use cgp_macro::{ BuildField, CgpData, CgpRecord, CgpVariant, ExtractField, FromVariant, HasField, HasFields, - Product, Sum, Symbol, cgp_auto_getter, cgp_component, cgp_context, cgp_getter, + Product, Sum, Symbol, cgp_auto_getter, cgp_component, cgp_context, cgp_getter, cgp_impl, cgp_new_provider, cgp_preset, cgp_provider, cgp_type, check_components, delegate_and_check_components, delegate_components, product, re_export_imports, replace_with, }; diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index dd216f79..e1f36542 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,11 +1,43 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree}; use quote::{ToTokens, format_ident, quote}; -use syn::token::For; +use syn::parse::{Parse, ParseStream}; +use syn::token::{Colon, For}; use syn::{Ident, ImplItem, ItemImpl, Type, parse2}; use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; use crate::parse::SimpleType; +pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { + let spec: ImplProviderSpec = parse2(attr)?; + let item_impl: ItemImpl = parse2(body)?; + + let out_impl = + transform_impl_trait(&item_impl, &spec.provider_trait_ident, &spec.provider_type)?; + + Ok(quote! { + #[cgp_provider] + #out_impl + }) +} + +pub struct ImplProviderSpec { + pub provider_type: Type, + pub provider_trait_ident: Ident, +} + +impl Parse for ImplProviderSpec { + fn parse(input: ParseStream) -> syn::Result { + let provider_type = input.parse()?; + let _colon: Colon = input.parse()?; + let provider_trait_ident = input.parse()?; + + Ok(ImplProviderSpec { + provider_type, + provider_trait_ident, + }) + } +} + pub fn transform_impl_trait( item_impl: &ItemImpl, provider_trait_ident: &Ident, diff --git a/crates/cgp-macro/src/lib.rs b/crates/cgp-macro/src/lib.rs index 7140b7d0..ac08186e 100644 --- a/crates/cgp-macro/src/lib.rs +++ b/crates/cgp-macro/src/lib.rs @@ -143,6 +143,13 @@ pub fn cgp_provider(attr: TokenStream, item: TokenStream) -> TokenStream { .into() } +#[proc_macro_attribute] +pub fn cgp_impl(attr: TokenStream, item: TokenStream) -> TokenStream { + cgp_macro_lib::cgp_impl(attr.into(), item.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + /** The `#[cgp_new_provider]` macro is an extension to [`#[cgp_provider]`](macro@cgp_provider) that in addition to the derivation of `IsProviderFor`, also generates a new provider From f70e16547b34adb8f745ca9b8759a2201fb4a657 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 19:35:43 +0000 Subject: [PATCH 03/12] Basic test is working --- crates/cgp-tests/src/tests/cgp_impl/basic.rs | 15 +++++++++++++++ crates/cgp-tests/src/tests/cgp_impl/mod.rs | 1 + crates/cgp-tests/src/tests/mod.rs | 1 + 3 files changed, 17 insertions(+) create mode 100644 crates/cgp-tests/src/tests/cgp_impl/basic.rs create mode 100644 crates/cgp-tests/src/tests/cgp_impl/mod.rs diff --git a/crates/cgp-tests/src/tests/cgp_impl/basic.rs b/crates/cgp-tests/src/tests/cgp_impl/basic.rs new file mode 100644 index 00000000..7c85abdd --- /dev/null +++ b/crates/cgp-tests/src/tests/cgp_impl/basic.rs @@ -0,0 +1,15 @@ +use cgp::prelude::*; + +#[cgp_component(FooProvider)] +pub trait CanDoFoo { + fn foo(&self, value: u32) -> String; +} + +pub struct ValueToString; + +#[cgp_impl(ValueToString: FooProvider)] +impl CanDoFoo for Context { + fn foo(&self, value: u32) -> String { + value.to_string() + } +} diff --git a/crates/cgp-tests/src/tests/cgp_impl/mod.rs b/crates/cgp-tests/src/tests/cgp_impl/mod.rs new file mode 100644 index 00000000..38883ee0 --- /dev/null +++ b/crates/cgp-tests/src/tests/cgp_impl/mod.rs @@ -0,0 +1 @@ +pub mod basic; diff --git a/crates/cgp-tests/src/tests/mod.rs b/crates/cgp-tests/src/tests/mod.rs index 06236f51..4cde5e95 100644 --- a/crates/cgp-tests/src/tests/mod.rs +++ b/crates/cgp-tests/src/tests/mod.rs @@ -12,3 +12,4 @@ pub mod monad; pub mod preset; pub mod symbol; pub mod use_delegate; +pub mod cgp_impl; From 01d719fadac496304c5426890680d28b442f0ef5 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 19:49:37 +0000 Subject: [PATCH 04/12] Improve impl macro --- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 87 ++++++++++++------- crates/cgp-tests/src/tests/cgp_impl/basic.rs | 17 +++- crates/cgp-tests/src/tests/mod.rs | 2 +- 3 files changed, 74 insertions(+), 32 deletions(-) diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index e1f36542..565df905 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,37 +1,88 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree}; use quote::{ToTokens, format_ident, quote}; +use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; +use syn::spanned::Spanned; use syn::token::{Colon, For}; -use syn::{Ident, ImplItem, ItemImpl, Type, parse2}; +use syn::{Error, Ident, ImplItem, ItemImpl, Type, parse2}; use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; +use crate::derive_provider::{ + derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, +}; use crate::parse::SimpleType; pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { let spec: ImplProviderSpec = parse2(attr)?; let item_impl: ItemImpl = parse2(body)?; - let out_impl = - transform_impl_trait(&item_impl, &spec.provider_trait_ident, &spec.provider_type)?; + let consumer_trait_path = &item_impl + .trait_ + .as_ref() + .ok_or_else(|| Error::new(item_impl.span(), "expect impl trait to contain path"))? + .1; + + let consumer_trait_path: SimpleType = parse2(consumer_trait_path.to_token_stream())?; + + let provider_impl = transform_impl_trait( + &item_impl, + &consumer_trait_path, + &spec.provider_trait_ident, + &spec.provider_type, + )?; + + let component_name = derive_component_name_from_provider_impl(&provider_impl)?; + + let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_name, &provider_impl)?; + + let provider_struct = if spec.new_struct { + Some(derive_provider_struct(&provider_impl)?) + } else { + None + }; + + let consumer_trait_name = &consumer_trait_path.name; Ok(quote! { - #[cgp_provider] - #out_impl + #[allow(unused_imports)] + use #consumer_trait_name as _; + + #provider_struct + + #provider_impl + + #is_provider_for_impl }) } pub struct ImplProviderSpec { + pub new_struct: bool, pub provider_type: Type, pub provider_trait_ident: Ident, } impl Parse for ImplProviderSpec { fn parse(input: ParseStream) -> syn::Result { + let new_struct = { + let fork = input.fork(); + let new_ident: Option = fork.parse().ok(); + match new_ident { + Some(new_ident) if new_ident == "new" => { + input.advance_to(&fork); + true + } + _ => false, + } + }; + let provider_type = input.parse()?; + let _colon: Colon = input.parse()?; + let provider_trait_ident = input.parse()?; Ok(ImplProviderSpec { + new_struct, provider_type, provider_trait_ident, }) @@ -40,6 +91,7 @@ impl Parse for ImplProviderSpec { pub fn transform_impl_trait( item_impl: &ItemImpl, + consumer_trait_path: &SimpleType, provider_trait_ident: &Ident, provider_type: &Type, ) -> syn::Result { @@ -73,8 +125,7 @@ pub fn transform_impl_trait( let mut out_impl: ItemImpl = parse2(raw_out_impl)?; out_impl.self_ty = Box::new(provider_type.clone()); - let source_trait_path = &item_impl.trait_.as_ref().unwrap().1; - let mut provider_trait_path: SimpleType = parse2(source_trait_path.to_token_stream())?; + let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); provider_trait_path.name = provider_trait_ident.clone(); match &mut provider_trait_path.generics { @@ -101,28 +152,6 @@ pub fn transform_impl_trait( })?); } - // let mut provider_trait_spec = item_impl.trait_.clone().unwrap(); - // let provider_trait_path = &mut provider_trait_spec.1; - // let segment = provider_trait_path.segments.last_mut().unwrap(); - - // match &mut segment.arguments { - // PathArguments::None => { - // segment.arguments = PathArguments::AngleBracketed(parse2(quote! { < #context_ident > })?); - // } - // PathArguments::AngleBracketed(args) => { - // args.args.insert(0, parse2(quote! { #context_ident })?); - // } - // _ => { - // return Err(Error::new(segment.span(), "trait path must end with angle bracket generic arguments")) - // } - // } - - // let provider_trait_path = item_impl.trait_.unwrap().1.clone(); - // out_impl.trait_ = Some((None, provider_trait_path, For(Span::call_site()))); - - // let context_param: GenericParam = parse2(context_type.to_token_stream())?; - // out_impl.generics.params.insert(0, context_param); - for item in out_impl.items.iter_mut() { if let ImplItem::Fn(item_fn) = item { replace_self_receiver(&mut item_fn.sig, &context_ident); diff --git a/crates/cgp-tests/src/tests/cgp_impl/basic.rs b/crates/cgp-tests/src/tests/cgp_impl/basic.rs index 7c85abdd..50a5fdbb 100644 --- a/crates/cgp-tests/src/tests/cgp_impl/basic.rs +++ b/crates/cgp-tests/src/tests/cgp_impl/basic.rs @@ -5,11 +5,24 @@ pub trait CanDoFoo { fn foo(&self, value: u32) -> String; } -pub struct ValueToString; +#[cgp_auto_getter] +pub trait HasName { + fn name(&self) -> &str; +} -#[cgp_impl(ValueToString: FooProvider)] +#[cgp_impl(new ValueToString: FooProvider)] impl CanDoFoo for Context { fn foo(&self, value: u32) -> String { value.to_string() } } + +#[cgp_impl(new WithNamePrefix: FooProvider)] +impl CanDoFoo for Context +where + Context: HasName, +{ + fn foo(&self, value: u32) -> String { + format!("{}: {}", self.name(), value) + } +} diff --git a/crates/cgp-tests/src/tests/mod.rs b/crates/cgp-tests/src/tests/mod.rs index 4cde5e95..7031c152 100644 --- a/crates/cgp-tests/src/tests/mod.rs +++ b/crates/cgp-tests/src/tests/mod.rs @@ -1,6 +1,7 @@ pub mod r#async; pub mod blanket_trait; pub mod cgp_component; +pub mod cgp_impl; pub mod check_components; pub mod compose; pub mod delegate_and_check_components; @@ -12,4 +13,3 @@ pub mod monad; pub mod preset; pub mod symbol; pub mod use_delegate; -pub mod cgp_impl; From 50034410117a3b0963fe2b70af0fd5485a284ad0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 20:07:33 +0000 Subject: [PATCH 05/12] Revert use of Refl for composite type --- .../src/derive_component/provider_trait.rs | 9 ++++-- .../derive_component/replace_self_receiver.rs | 12 +++---- .../src/derive_component/replace_self_type.rs | 14 ++++++--- .../cgp-macro-lib/src/derive_getter/parse.rs | 4 +-- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 31 ++++++++----------- crates/cgp-tests/src/tests/cgp_impl/basic.rs | 16 ++++++++++ 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs index 1cc4233f..0e784307 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use quote::quote; +use quote::{ToTokens, quote}; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Ident, ItemTrait, TraitItem, TypeParamBound, parse2}; @@ -9,6 +9,7 @@ use crate::derive_component::replace_self_receiver::replace_self_receiver; use crate::derive_component::replace_self_type::{ iter_parse_and_replace_self_type, parse_and_replace_self_type, }; +use crate::derive_component::to_snake_case_ident; use crate::parse::parse_is_provider_params; pub fn derive_provider_trait( @@ -89,7 +90,11 @@ pub fn derive_provider_trait( parse_and_replace_self_type(item, context_type, &local_assoc_types)?; if let TraitItem::Fn(func) = &mut replaced_item { - replace_self_receiver(&mut func.sig, context_type); + replace_self_receiver( + &mut func.sig, + &to_snake_case_ident(context_type), + context_type.to_token_stream(), + ); } *item = replaced_item; diff --git a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs index 175ddcb3..4e1efb4e 100644 --- a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs +++ b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs @@ -1,14 +1,14 @@ -use proc_macro2::Ident; +use proc_macro2::{Ident, TokenStream}; use syn::{FnArg, Signature, parse_quote}; -use crate::derive_component::snake_case::to_snake_case_ident; - -pub fn replace_self_receiver(sig: &mut Signature, replaced_type: &Ident) { +pub fn replace_self_receiver( + sig: &mut Signature, + replaced_var: &Ident, + replaced_type: TokenStream, +) { if let Some(arg) = sig.inputs.first_mut() && let FnArg::Receiver(receiver) = arg { - let replaced_var = to_snake_case_ident(replaced_type); - match (&receiver.reference, &receiver.mutability) { (None, None) => { *arg = parse_quote!(#replaced_var : #replaced_type); diff --git a/crates/cgp-macro-lib/src/derive_component/replace_self_type.rs b/crates/cgp-macro-lib/src/derive_component/replace_self_type.rs index 619fd6a6..bf8a6952 100644 --- a/crates/cgp-macro-lib/src/derive_component/replace_self_type.rs +++ b/crates/cgp-macro-lib/src/derive_component/replace_self_type.rs @@ -27,13 +27,17 @@ pub fn parse_and_replace_self_type( where T: ToTokens + Parse, { - let stream = replace_self_type(val.to_token_stream(), replaced_ident, local_assoc_types); + let stream = replace_self_type( + val.to_token_stream(), + replaced_ident.to_token_stream(), + local_assoc_types, + ); syn::parse2(stream) } pub fn replace_self_type( stream: TokenStream, - replaced_ident: &Ident, + replaced_ident: TokenStream, local_assoc_types: &Vec, ) -> TokenStream { let self_type = format_ident!("Self"); @@ -57,7 +61,7 @@ pub fn replace_self_type( Some(TokenTree::Ident(assoc_type)) if local_assoc_types.contains(assoc_type) => { - ident + ident.to_token_stream() } _ => replaced_ident, } @@ -68,14 +72,14 @@ pub fn replace_self_type( _ => replaced_ident, }; - result_stream.push(TokenTree::Ident(replaced)); + result_stream.extend(replaced); } else { result_stream.push(TokenTree::Ident(ident)); } } TokenTree::Group(group) => { let replaced_stream = - replace_self_type(group.stream(), replaced_ident, local_assoc_types); + replace_self_type(group.stream(), replaced_ident.clone(), local_assoc_types); let replaced_group = Group::new(group.delimiter(), replaced_stream); result_stream.push(TokenTree::Group(replaced_group)); diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index 49662858..9caff497 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -174,7 +174,7 @@ fn parse_receiver(context_ident: &Ident, arg: &FnArg) -> syn::Result<(ReceiverMo Type::Reference(ty) => { let receiver = parse2(replace_self_type( ty.elem.to_token_stream(), - context_ident, + context_ident.to_token_stream(), &Vec::new(), ))?; Ok((ReceiverMode::Type(receiver), ty.mutability)) @@ -191,7 +191,7 @@ fn parse_return_type(context_type: &Ident, return_type: &ReturnType) -> syn::Res match return_type { ReturnType::Type(_, ty) => parse2(replace_self_type( ty.to_token_stream(), - context_type, + context_type.to_token_stream(), &Vec::new(), )), _ => Err(Error::new( diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 565df905..99aa0c7b 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -97,12 +97,11 @@ pub fn transform_impl_trait( ) -> syn::Result { let context_type = item_impl.self_ty.as_ref(); - let (context_ident, use_refl) = - if let Some(ident) = parse2::(context_type.to_token_stream()).ok() { - (ident, false) - } else { - (Ident::new("__Context__", Span::call_site()), true) - }; + let context_var = if let Some(ident) = parse2::(context_type.to_token_stream()).ok() { + to_snake_case_ident(&ident) + } else { + Ident::new("__context__", Span::call_site()) + }; let local_assoc_types: Vec = item_impl .items @@ -118,7 +117,7 @@ pub fn transform_impl_trait( let raw_out_impl = replace_self_type( item_impl.to_token_stream(), - &context_ident, + context_type.to_token_stream(), &local_assoc_types, ); @@ -132,10 +131,10 @@ pub fn transform_impl_trait( Some(generics) => { generics .args - .insert(0, parse2(context_ident.to_token_stream())?); + .insert(0, parse2(context_type.to_token_stream())?); } None => { - provider_trait_path.generics = Some(parse2(quote! { < #context_ident > })?); + provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?); } } @@ -145,17 +144,13 @@ pub fn transform_impl_trait( For(Span::call_site()), )); - if use_refl { - let where_clause = out_impl.generics.make_where_clause(); - where_clause.predicates.push(parse2(quote! { - #context_ident: Refl - })?); - } - for item in out_impl.items.iter_mut() { if let ImplItem::Fn(item_fn) = item { - replace_self_receiver(&mut item_fn.sig, &context_ident); - let context_var = to_snake_case_ident(&context_ident); + replace_self_receiver( + &mut item_fn.sig, + &context_var, + context_type.to_token_stream(), + ); let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); item_fn.block = parse2(replaced_block)?; } diff --git a/crates/cgp-tests/src/tests/cgp_impl/basic.rs b/crates/cgp-tests/src/tests/cgp_impl/basic.rs index 50a5fdbb..d1d1de31 100644 --- a/crates/cgp-tests/src/tests/cgp_impl/basic.rs +++ b/crates/cgp-tests/src/tests/cgp_impl/basic.rs @@ -1,3 +1,5 @@ +use core::fmt::Display; + use cgp::prelude::*; #[cgp_component(FooProvider)] @@ -26,3 +28,17 @@ where format!("{}: {}", self.name(), value) } } + +pub struct Foo { + pub tag: Tag, +} + +#[cgp_impl(new WithFooTag: FooProvider)] +impl CanDoFoo for Foo +where + Tag: Display, +{ + fn foo(&self, value: u32) -> String { + format!("{}: {}", self.tag, value) + } +} From b366cde0c5d7d3c8e46b87d89c2fd24f384315bb Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 20:08:15 +0000 Subject: [PATCH 06/12] Remove Refl --- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/traits/mod.rs | 2 -- crates/cgp-component/src/traits/refl.rs | 7 ------- crates/cgp-core/src/prelude.rs | 4 ++-- 4 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 crates/cgp-component/src/traits/refl.rs diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 6a731f6f..3e9c86a9 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -8,5 +8,5 @@ mod traits; mod types; -pub use traits::{CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor, Refl}; +pub use traits::{CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor}; pub use types::{UseContext, UseDelegate, UseFields, WithContext, WithProvider}; diff --git a/crates/cgp-component/src/traits/mod.rs b/crates/cgp-component/src/traits/mod.rs index ceb106e6..16104b7e 100644 --- a/crates/cgp-component/src/traits/mod.rs +++ b/crates/cgp-component/src/traits/mod.rs @@ -2,10 +2,8 @@ mod can_use_component; mod delegate_component; mod has_provider; mod is_provider; -mod refl; pub use can_use_component::*; pub use delegate_component::DelegateComponent; pub use has_provider::HasCgpProvider; pub use is_provider::*; -pub use refl::*; diff --git a/crates/cgp-component/src/traits/refl.rs b/crates/cgp-component/src/traits/refl.rs deleted file mode 100644 index cb1e6cc3..00000000 --- a/crates/cgp-component/src/traits/refl.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub trait Refl { - type Type; -} - -impl Refl for T { - type Type = T; -} diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index f918fb98..e29326c2 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, HasCgpProvider, IsProviderFor, Refl, UseContext, - UseDelegate, UseFields, WithContext, WithProvider, + CanUseComponent, DelegateComponent, HasCgpProvider, IsProviderFor, UseContext, UseDelegate, + UseFields, WithContext, WithProvider, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::impls::{IsMut, IsNothing, IsPresent, IsRef, IsVoid, UseField}; From 794936cd80092c743c982b8b12c4fdf6c3460ae2 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 20:16:16 +0000 Subject: [PATCH 07/12] Test use #[cgp_impl] in cgp-anyhow-error --- crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs b/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs index addba759..fd865f41 100644 --- a/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs +++ b/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs @@ -7,8 +7,8 @@ use cgp_core::prelude::*; pub struct RaiseAnyhowError; -#[cgp_provider] -impl ErrorRaiser for RaiseAnyhowError +#[cgp_impl(RaiseAnyhowError: ErrorRaiser)] +impl CanRaiseError for Context where Context: HasErrorType, E: StdError + Send + Sync + 'static, @@ -18,8 +18,8 @@ where } } -#[cgp_provider] -impl ErrorWrapper for RaiseAnyhowError +#[cgp_impl(RaiseAnyhowError: ErrorWrapper)] +impl CanWrapError for Context where Context: HasErrorType, Detail: Display + Send + Sync + 'static, From faf97b963cc7dbdab7c4ddcee822a3c31df19279 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 15 Oct 2025 20:25:05 +0000 Subject: [PATCH 08/12] Use provider trait name directly instead --- .../src/impls/raise_anyhow_error.rs | 8 +-- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 23 ++------- crates/cgp-tests/src/tests/cgp_impl/basic.rs | 50 +++++++++++-------- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs b/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs index fd865f41..8c29d025 100644 --- a/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs +++ b/crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs @@ -7,8 +7,8 @@ use cgp_core::prelude::*; pub struct RaiseAnyhowError; -#[cgp_impl(RaiseAnyhowError: ErrorRaiser)] -impl CanRaiseError for Context +#[cgp_impl(RaiseAnyhowError)] +impl ErrorRaiser for Context where Context: HasErrorType, E: StdError + Send + Sync + 'static, @@ -18,8 +18,8 @@ where } } -#[cgp_impl(RaiseAnyhowError: ErrorWrapper)] -impl CanWrapError for Context +#[cgp_impl(RaiseAnyhowError)] +impl ErrorWrapper for Context where Context: HasErrorType, Detail: Display + Send + Sync + 'static, diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 99aa0c7b..f758746f 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -3,7 +3,7 @@ use quote::{ToTokens, format_ident, quote}; use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; -use syn::token::{Colon, For}; +use syn::token::For; use syn::{Error, Ident, ImplItem, ItemImpl, Type, parse2}; use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; @@ -24,12 +24,8 @@ pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result syn::Result syn::Result syn::Result { let context_type = item_impl.self_ty.as_ref(); @@ -125,7 +109,6 @@ pub fn transform_impl_trait( out_impl.self_ty = Box::new(provider_type.clone()); let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); - provider_trait_path.name = provider_trait_ident.clone(); match &mut provider_trait_path.generics { Some(generics) => { diff --git a/crates/cgp-tests/src/tests/cgp_impl/basic.rs b/crates/cgp-tests/src/tests/cgp_impl/basic.rs index d1d1de31..0efb002d 100644 --- a/crates/cgp-tests/src/tests/cgp_impl/basic.rs +++ b/crates/cgp-tests/src/tests/cgp_impl/basic.rs @@ -1,5 +1,3 @@ -use core::fmt::Display; - use cgp::prelude::*; #[cgp_component(FooProvider)] @@ -12,33 +10,41 @@ pub trait HasName { fn name(&self) -> &str; } -#[cgp_impl(new ValueToString: FooProvider)] -impl CanDoFoo for Context { +#[cgp_impl(new ValueToString)] +impl FooProvider for Context { fn foo(&self, value: u32) -> String { value.to_string() } } -#[cgp_impl(new WithNamePrefix: FooProvider)] -impl CanDoFoo for Context -where - Context: HasName, -{ - fn foo(&self, value: u32) -> String { - format!("{}: {}", self.name(), value) +pub mod inner { + use core::fmt::Display; + + use cgp::prelude::*; + + use super::{FooProvider, FooProviderComponent, HasName}; + + #[cgp_impl(new WithNamePrefix)] + impl FooProvider for Context + where + Context: HasName, + { + fn foo(&self, value: u32) -> String { + format!("{}: {}", self.name(), value) + } } -} -pub struct Foo { - pub tag: Tag, -} + pub struct Foo { + pub tag: Tag, + } -#[cgp_impl(new WithFooTag: FooProvider)] -impl CanDoFoo for Foo -where - Tag: Display, -{ - fn foo(&self, value: u32) -> String { - format!("{}: {}", self.tag, value) + #[cgp_impl(new WithFooTag)] + impl FooProvider for Foo + where + Tag: Display, + { + fn foo(&self, value: u32) -> String { + format!("{}: {}", self.tag, value) + } } } From 9fbc6bdd3b40851eab8ca2d4c80b59cfaad2d639 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 16 Oct 2025 07:49:39 +0000 Subject: [PATCH 09/12] Only replace self var when self receiver is used --- .../src/derive_component/provider_trait.rs | 4 +- .../derive_component/replace_self_receiver.rs | 49 ++++++++++++------- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 20 +++++--- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs index 0e784307..98ce49e9 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs @@ -5,7 +5,7 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Ident, ItemTrait, TraitItem, TypeParamBound, parse2}; -use crate::derive_component::replace_self_receiver::replace_self_receiver; +use crate::derive_component::replace_self_receiver::replace_self_receiver_in_signature; use crate::derive_component::replace_self_type::{ iter_parse_and_replace_self_type, parse_and_replace_self_type, }; @@ -90,7 +90,7 @@ pub fn derive_provider_trait( parse_and_replace_self_type(item, context_type, &local_assoc_types)?; if let TraitItem::Fn(func) = &mut replaced_item { - replace_self_receiver( + replace_self_receiver_in_signature( &mut func.sig, &to_snake_case_ident(context_type), context_type.to_token_stream(), diff --git a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs index 4e1efb4e..2a039827 100644 --- a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs +++ b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs @@ -1,7 +1,7 @@ use proc_macro2::{Ident, TokenStream}; -use syn::{FnArg, Signature, parse_quote}; +use syn::{parse_quote, FnArg, Receiver, Signature}; -pub fn replace_self_receiver( +pub fn replace_self_receiver_in_signature( sig: &mut Signature, replaced_var: &Ident, replaced_type: TokenStream, @@ -9,23 +9,34 @@ pub fn replace_self_receiver( if let Some(arg) = sig.inputs.first_mut() && let FnArg::Receiver(receiver) = arg { - match (&receiver.reference, &receiver.mutability) { - (None, None) => { - *arg = parse_quote!(#replaced_var : #replaced_type); - } - (Some((_and, None)), None) => { - *arg = parse_quote!(#replaced_var : & #replaced_type); - } - (Some((_and, Some(life))), None) => { - *arg = parse_quote!(#replaced_var : & #life #replaced_type); - } - (Some((_and, None)), Some(_mut)) => { - *arg = parse_quote!(#replaced_var : &mut #replaced_type); - } - (Some((_and, Some(life))), Some(_mut)) => { - *arg = parse_quote!(#replaced_var : & #life mut #replaced_type); - } - _ => {} + *arg = replace_self_receiver(receiver, replaced_var, replaced_type); + } +} + + +pub fn replace_self_receiver( + receiver: &mut Receiver, + replaced_var: &Ident, + replaced_type: TokenStream, +) -> FnArg { + match (&receiver.reference, &receiver.mutability) { + (None, None) => { + parse_quote!(#replaced_var : #replaced_type) + } + (Some((_and, None)), None) => { + parse_quote!(#replaced_var : & #replaced_type) + } + (Some((_and, Some(life))), None) => { + parse_quote!(#replaced_var : & #life #replaced_type) + } + (Some((_and, None)), Some(_mut)) => { + parse_quote!(#replaced_var : &mut #replaced_type) + } + (Some((_and, Some(life))), Some(_mut)) => { + parse_quote!(#replaced_var : & #life mut #replaced_type) + } + (None, Some(_mut)) => { + parse_quote!(#replaced_var : mut #replaced_type) } } } diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index f758746f..098be8ad 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -4,7 +4,7 @@ use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::token::For; -use syn::{Error, Ident, ImplItem, ItemImpl, Type, parse2}; +use syn::{parse2, Error, FnArg, Ident, ImplItem, ItemImpl, Type}; use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; use crate::derive_provider::{ @@ -129,13 +129,17 @@ pub fn transform_impl_trait( for item in out_impl.items.iter_mut() { if let ImplItem::Fn(item_fn) = item { - replace_self_receiver( - &mut item_fn.sig, - &context_var, - context_type.to_token_stream(), - ); - let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); - item_fn.block = parse2(replaced_block)?; + if let Some(arg) = item_fn.sig.inputs.first_mut() + && let FnArg::Receiver(receiver) = arg + { + *arg = replace_self_receiver(receiver, + &context_var, + context_type.to_token_stream(), + ); + + let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); + item_fn.block = parse2(replaced_block)?; + } } } From 45d050590b916544060dbf2448b605d5ba0378d1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 16 Oct 2025 07:52:42 +0000 Subject: [PATCH 10/12] Add __ to context variable to avoid name clash --- crates/cgp-macro-lib/src/derive_component/snake_case.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cgp-macro-lib/src/derive_component/snake_case.rs b/crates/cgp-macro-lib/src/derive_component/snake_case.rs index dcb69d96..ee48b75b 100644 --- a/crates/cgp-macro-lib/src/derive_component/snake_case.rs +++ b/crates/cgp-macro-lib/src/derive_component/snake_case.rs @@ -19,5 +19,5 @@ pub fn to_snake_case_str(val: &str) -> String { } pub fn to_snake_case_ident(val: &Ident) -> Ident { - Ident::new(&to_snake_case_str(&val.to_string()), Span::call_site()) + Ident::new(&format!("__{}__", to_snake_case_str(&val.to_string())), Span::call_site()) } From 38a997ee794cca8b2b6b6d59f53655c6d08a5cc1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 16 Oct 2025 20:10:43 +0000 Subject: [PATCH 11/12] Allow component name to be specified --- .../derive_component/replace_self_receiver.rs | 3 +- .../src/derive_component/snake_case.rs | 5 +++- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 29 +++++++++++++------ crates/cgp-tests/src/tests/cgp_impl/basic.rs | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs index 2a039827..425843c5 100644 --- a/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs +++ b/crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs @@ -1,5 +1,5 @@ use proc_macro2::{Ident, TokenStream}; -use syn::{parse_quote, FnArg, Receiver, Signature}; +use syn::{FnArg, Receiver, Signature, parse_quote}; pub fn replace_self_receiver_in_signature( sig: &mut Signature, @@ -13,7 +13,6 @@ pub fn replace_self_receiver_in_signature( } } - pub fn replace_self_receiver( receiver: &mut Receiver, replaced_var: &Ident, diff --git a/crates/cgp-macro-lib/src/derive_component/snake_case.rs b/crates/cgp-macro-lib/src/derive_component/snake_case.rs index ee48b75b..c7cc504e 100644 --- a/crates/cgp-macro-lib/src/derive_component/snake_case.rs +++ b/crates/cgp-macro-lib/src/derive_component/snake_case.rs @@ -19,5 +19,8 @@ pub fn to_snake_case_str(val: &str) -> String { } pub fn to_snake_case_ident(val: &Ident) -> Ident { - Ident::new(&format!("__{}__", to_snake_case_str(&val.to_string())), Span::call_site()) + Ident::new( + &format!("__{}__", to_snake_case_str(&val.to_string())), + Span::call_site(), + ) } diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 098be8ad..507d470b 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -3,8 +3,8 @@ use quote::{ToTokens, format_ident, quote}; use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; -use syn::token::For; -use syn::{parse2, Error, FnArg, Ident, ImplItem, ItemImpl, Type}; +use syn::token::{Colon, For}; +use syn::{Error, FnArg, Ident, ImplItem, ItemImpl, Type, parse2}; use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident}; use crate::derive_provider::{ @@ -27,9 +27,12 @@ pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result component_type.clone(), + None => derive_component_name_from_provider_impl(&provider_impl)?, + }; - let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_name, &provider_impl)?; + let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_type, &provider_impl)?; let provider_struct = if spec.new_struct { Some(derive_provider_struct(&provider_impl)?) @@ -49,6 +52,7 @@ pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result, } impl Parse for ImplProviderSpec { @@ -67,9 +71,17 @@ impl Parse for ImplProviderSpec { let provider_type = input.parse()?; + let component_type = if let Some(_colon) = input.parse::>()? { + let component_type: Type = input.parse()?; + Some(component_type) + } else { + None + }; + Ok(ImplProviderSpec { new_struct, provider_type, + component_type, }) } } @@ -132,12 +144,11 @@ pub fn transform_impl_trait( if let Some(arg) = item_fn.sig.inputs.first_mut() && let FnArg::Receiver(receiver) = arg { - *arg = replace_self_receiver(receiver, - &context_var, - context_type.to_token_stream(), - ); + *arg = + replace_self_receiver(receiver, &context_var, context_type.to_token_stream()); - let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); + let replaced_block = + replace_self_var(item_fn.block.to_token_stream(), &context_var); item_fn.block = parse2(replaced_block)?; } } diff --git a/crates/cgp-tests/src/tests/cgp_impl/basic.rs b/crates/cgp-tests/src/tests/cgp_impl/basic.rs index 0efb002d..b4cfc13f 100644 --- a/crates/cgp-tests/src/tests/cgp_impl/basic.rs +++ b/crates/cgp-tests/src/tests/cgp_impl/basic.rs @@ -38,7 +38,7 @@ pub mod inner { pub tag: Tag, } - #[cgp_impl(new WithFooTag)] + #[cgp_impl(new WithFooTag: FooProviderComponent)] impl FooProvider for Foo where Tag: Display, From 94253eefe9fd6777cc0eba724d4341fcaaf54c7d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 16 Oct 2025 20:25:09 +0000 Subject: [PATCH 12/12] Fix clippy --- .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 507d470b..b93fb094 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -93,7 +93,7 @@ pub fn transform_impl_trait( ) -> syn::Result { let context_type = item_impl.self_ty.as_ref(); - let context_var = if let Some(ident) = parse2::(context_type.to_token_stream()).ok() { + let context_var = if let Ok(ident) = parse2::(context_type.to_token_stream()) { to_snake_case_ident(&ident) } else { Ident::new("__context__", Span::call_site()) @@ -140,17 +140,14 @@ pub fn transform_impl_trait( )); for item in out_impl.items.iter_mut() { - if let ImplItem::Fn(item_fn) = item { - if let Some(arg) = item_fn.sig.inputs.first_mut() - && let FnArg::Receiver(receiver) = arg - { - *arg = - replace_self_receiver(receiver, &context_var, context_type.to_token_stream()); - - let replaced_block = - replace_self_var(item_fn.block.to_token_stream(), &context_var); - item_fn.block = parse2(replaced_block)?; - } + if let ImplItem::Fn(item_fn) = item + && let Some(arg) = item_fn.sig.inputs.first_mut() + && let FnArg::Receiver(receiver) = arg + { + *arg = replace_self_receiver(receiver, &context_var, context_type.to_token_stream()); + + let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); + item_fn.block = parse2(replaced_block)?; } } @@ -162,9 +159,9 @@ fn replace_self_var(stream: TokenStream, replaced_ident: &Ident) -> TokenStream let mut result_stream: Vec = Vec::new(); - let mut token_iter = stream.into_iter(); + let token_iter = stream.into_iter(); - while let Some(tree) = token_iter.next() { + for tree in token_iter { match tree { TokenTree::Ident(ident) => { if ident == self_ident {