diff --git a/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attribute.rs b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attribute.rs new file mode 100644 index 00000000..d209fee7 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attribute.rs @@ -0,0 +1,57 @@ +use syn::parse::{Parse, ParseStream}; +use syn::token::In; +use syn::{Generics, ItemImpl, Type, parse_quote}; + +use crate::types::ident::IdentWithTypeArgs; +use crate::types::path::UniPathOrType; + +pub struct DefaultImplAttribute { + pub key_type: UniPathOrType, + pub in_token: In, + pub namespace: IdentWithTypeArgs, +} + +impl DefaultImplAttribute { + pub fn to_item_impl( + &self, + provider_generics: &Generics, + provider_type: &Type, + ) -> syn::Result { + let key_type = &self.key_type; + let mut namespace_trait_path = self.namespace.clone(); + + namespace_trait_path + .type_args + .make_args() + .push(parse_quote!(__Components__)); + + let mut generics = provider_generics.clone(); + generics.params.push(parse_quote!(__Components__)); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + let item_impl = parse_quote! { + impl #impl_generics #namespace_trait_path for #key_type + #where_clause + { + type Delegate = #provider_type; + } + }; + + Ok(item_impl) + } +} + +impl Parse for DefaultImplAttribute { + fn parse(input: ParseStream) -> syn::Result { + let key_type = input.parse()?; + let in_token = input.parse()?; + let namespace = input.parse()?; + + Ok(Self { + key_type, + in_token, + namespace, + }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attributes.rs b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attributes.rs new file mode 100644 index 00000000..53a20d4c --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/attributes.rs @@ -0,0 +1,25 @@ +use syn::{Generics, ItemImpl, Type}; + +use crate::types::attributes::DefaultImplAttribute; + +#[derive(Default)] +pub struct DefaultImplAttributes { + pub attributes: Vec, +} + +impl DefaultImplAttributes { + pub fn to_item_impls( + &self, + provider_generics: &Generics, + provider_type: &Type, + ) -> syn::Result> { + let mut item_impls = Vec::new(); + + for attribute in &self.attributes { + let item_impl = attribute.to_item_impl(provider_generics, provider_type)?; + item_impls.push(item_impl); + } + + Ok(item_impls) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/attributes/default_impl/mod.rs b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/mod.rs new file mode 100644 index 00000000..ea33ebcb --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/attributes/default_impl/mod.rs @@ -0,0 +1,5 @@ +mod attribute; +mod attributes; + +pub use attribute::*; +pub use attributes::*; diff --git a/crates/macros/cgp-macro-core/src/types/attributes/impl_attributes.rs b/crates/macros/cgp-macro-core/src/types/attributes/impl_attributes.rs index cea9184b..2f246a48 100644 --- a/crates/macros/cgp-macro-core/src/types/attributes/impl_attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/attributes/impl_attributes.rs @@ -1,10 +1,11 @@ use syn::Attribute; +use syn::parse::Parse; use syn::punctuated::Punctuated; use syn::token::Comma; use crate::types::attributes::{ - UseProviderAttribute, UseProviderAttributes, UseTypeAttribute, UseTypeAttributes, - UsesAttributes, + DefaultImplAttribute, DefaultImplAttributes, UseProviderAttribute, UseProviderAttributes, + UseTypeAttribute, UseTypeAttributes, UsesAttributes, }; use crate::types::ident::IdentWithTypeArgs; @@ -13,6 +14,7 @@ pub struct ImplAttributes { pub uses: UsesAttributes, pub use_type: UseTypeAttributes, pub use_provider: UseProviderAttributes, + pub default_impls: DefaultImplAttributes, pub raw_attributes: Vec, } @@ -47,6 +49,15 @@ impl ImplAttributes { .attributes .extend(use_provider); } + "default_impl" => { + let default_impl = + attribute.parse_args_with(DefaultImplAttribute::parse)?; + + parsed_attributes + .default_impls + .attributes + .push(default_impl); + } _ => { parsed_attributes.raw_attributes.push(attribute.clone()); } diff --git a/crates/macros/cgp-macro-core/src/types/attributes/mod.rs b/crates/macros/cgp-macro-core/src/types/attributes/mod.rs index 72930efc..aaf3fee4 100644 --- a/crates/macros/cgp-macro-core/src/types/attributes/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/attributes/mod.rs @@ -1,9 +1,11 @@ +mod default_impl; mod function; mod impl_attributes; mod use_provider; mod use_type; mod uses; +pub use default_impl::*; pub use function::*; pub use impl_attributes::*; pub use use_provider::*; diff --git a/crates/macros/cgp-macro-core/src/types/cgp_impl/item.rs b/crates/macros/cgp-macro-core/src/types/cgp_impl/item.rs index 5d6a83fe..26c23029 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_impl/item.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_impl/item.rs @@ -31,14 +31,18 @@ impl ItemCgpImpl { .use_provider .add_type_param_bounds(&self_type, &mut item_impl.generics)?; - let (consumer_trait_path, context_type) = match &item_impl.trait_ { + let default_impls = attributes + .default_impls + .to_item_impls(&item_impl.generics, &self.args.provider_type)?; + + let (provider_trait_path, context_type) = match &item_impl.trait_ { Some((_, path, _)) => { - let consumer_trait_path = parse2(path.to_token_stream())?; + let provider_trait_path = parse2(path.to_token_stream())?; let context_type = item_impl.self_ty.as_ref().clone(); - (consumer_trait_path, context_type) + (provider_trait_path, context_type) } None => { - let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; + let provider_trait_path = parse2(item_impl.self_ty.to_token_stream())?; let context_type = parse_quote! { __Context__ }; item_impl @@ -46,7 +50,7 @@ impl ItemCgpImpl { .params .insert(0, parse_quote! { #context_type }); - (consumer_trait_path, context_type) + (provider_trait_path, context_type) } }; @@ -54,7 +58,8 @@ impl ItemCgpImpl { args: self.args.clone(), item_impl, context_type, - consumer_trait_path, + provider_trait_path, + default_impls, }) } } diff --git a/crates/macros/cgp-macro-core/src/types/cgp_impl/lowered.rs b/crates/macros/cgp-macro-core/src/types/cgp_impl/lowered.rs index bb1cf48e..0bfe97b3 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_impl/lowered.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_impl/lowered.rs @@ -17,7 +17,8 @@ pub struct LoweredCgpImpl { pub args: ImplArgs, pub item_impl: ItemImpl, pub context_type: Type, - pub consumer_trait_path: IdentWithTypeArgs, + pub provider_trait_path: IdentWithTypeArgs, + pub default_impls: Vec, } impl LoweredCgpImpl { @@ -53,7 +54,7 @@ impl LoweredCgpImpl { pub fn to_raw_item_impl(&self) -> syn::Result { let item_impl = &self.item_impl; let context_type = &self.context_type; - let consumer_trait_path = &self.consumer_trait_path; + let mut provider_trait_path = self.provider_trait_path.clone(); let provider_type = &self.args.provider_type; let context_ident = if let Ok(ident) = parse2::(context_type.to_token_stream()) { @@ -78,8 +79,6 @@ impl LoweredCgpImpl { out_impl.self_ty = Box::new(provider_type.clone()); - let mut provider_trait_path = consumer_trait_path.clone(); - provider_trait_path .type_args .make_args() diff --git a/crates/macros/cgp-macro-core/src/types/path/mod.rs b/crates/macros/cgp-macro-core/src/types/path/mod.rs index 7537b012..26f81c79 100644 --- a/crates/macros/cgp-macro-core/src/types/path/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/path/mod.rs @@ -3,9 +3,11 @@ mod path_head; mod path_head_or_type; mod prefix; mod unipath; +mod unipath_or_type; pub use path_element::*; pub use path_head::*; pub use path_head_or_type::*; pub use prefix::*; pub use unipath::*; +pub use unipath_or_type::*; diff --git a/crates/macros/cgp-macro-core/src/types/path/unipath_or_type.rs b/crates/macros/cgp-macro-core/src/types/path/unipath_or_type.rs new file mode 100644 index 00000000..80f4b720 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/path/unipath_or_type.rs @@ -0,0 +1,33 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::Type; +use syn::parse::{Parse, ParseStream}; +use syn::token::At; + +use crate::types::path::UniPath; + +pub enum UniPathOrType { + Type(Type), + Path(UniPath), +} + +impl Parse for UniPathOrType { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(At) { + let path = input.parse()?; + Ok(Self::Path(path)) + } else { + let ty = input.parse()?; + Ok(Self::Type(ty)) + } + } +} + +impl ToTokens for UniPathOrType { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + UniPathOrType::Type(ty) => ty.to_tokens(tokens), + UniPathOrType::Path(path) => path.to_tokens(tokens), + } + } +} diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/macros/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 16ee9fe1..7c2a2431 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,6 +1,6 @@ use cgp_macro_core::types::cgp_impl::{ImplArgs, ItemCgpImpl}; use proc_macro2::TokenStream; -use quote::ToTokens; +use quote::quote; use syn::{ItemImpl, parse2}; pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { @@ -10,7 +10,13 @@ pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { + fn show(&self, value: &T) -> String; +} + +#[cgp_impl(new ShowString)] +#[default_impl(String in DefaultImpls1)] +impl ShowImpl { + fn show(&self, value: &String) -> String { + value.clone() + } +} + +#[cgp_impl(new ShowWithDisplay)] +impl ShowImpl { + fn show(&self, value: &T) -> String { + value.to_string() + } +} + +cgp_namespace! { + new DefaultShowComponents { + [ + String, + u64, + ]: + ShowWithDisplay, + } +} + +cgp_namespace! { + new ExtendedNamespace: DefaultNamespace { + } +} + +#[cgp_impl(new ShowU32)] +#[default_impl(@test.ShowImplComponent.u32 in ExtendedNamespace)] +impl ShowImpl { + fn show(&self, value: &u32) -> String { + value.to_string() + } +} diff --git a/crates/tests/cgp-tests/src/namespaces/generics.rs b/crates/tests/cgp-tests/src/namespaces/generics.rs deleted file mode 100644 index b9ac9a27..00000000 --- a/crates/tests/cgp-tests/src/namespaces/generics.rs +++ /dev/null @@ -1,38 +0,0 @@ -use core::fmt::Display; - -use cgp::core::component::DefaultImpls1; -use cgp::prelude::*; - -#[cgp_component(ShowImpl)] -#[prefix(@test)] -pub trait Show { - fn show(&self, value: &T) -> String; -} - -#[cgp_impl(new ShowWithDisplay)] -// #[default_impl(DefaultNamespace1)] -impl ShowImpl { - fn show(&self, value: &T) -> String { - value.to_string() - } -} - -// cgp_namespace! { -// DefaultShow { -// T: -// @ShowWithDisplay, -// } -// } - -/* - cgp_namespace! { - DefaultNamespace1 { - String: - ShowWithDisplay, - } - } -*/ - -impl DefaultImpls1 for String { - type Delegate = ShowWithDisplay; -} diff --git a/crates/tests/cgp-tests/src/namespaces/mod.rs b/crates/tests/cgp-tests/src/namespaces/mod.rs index b9a1bced..c8002a89 100644 --- a/crates/tests/cgp-tests/src/namespaces/mod.rs +++ b/crates/tests/cgp-tests/src/namespaces/mod.rs @@ -1,2 +1,2 @@ +pub mod default_impls; pub mod extended; -pub mod generics; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_generics.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_generics.rs deleted file mode 100644 index 7d1b7d4e..00000000 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_generics.rs +++ /dev/null @@ -1,33 +0,0 @@ -use cgp::core::component::DefaultImpls1; -use cgp::prelude::*; -use cgp_tests::namespaces::generics::{ShowImplComponent, ShowWithDisplay}; - -pub struct App; - -delegate_components! { - App { - // use DefaultNamespace; - // for in DefaultNamespace { - // Component: Provider, - // } - namespace DefaultNamespace; - - for in DefaultImpls1 { - @test.ShowImplComponent.T: Provider, - } - - @test.ShowImplComponent.u64: - ShowWithDisplay, - - // namespace DefaultNamespace1 => @test.ShowImplComponent; - } -} - -check_components! { - App { - ShowImplComponent: [ - String, - u64, - ] - } -} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs new file mode 100644 index 00000000..dabdc5c9 --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs @@ -0,0 +1,75 @@ +use cgp::core::component::DefaultImpls1; +use cgp::prelude::*; +use cgp_tests::namespaces::default_impls::{ + DefaultShowComponents, ExtendedNamespace, ShowImplComponent, ShowWithDisplay, +}; + +pub struct AppA; + +delegate_components! { + AppA { + namespace DefaultNamespace; + + for in DefaultImpls1 { + @test.ShowImplComponent.T: Provider, + } + + @test.ShowImplComponent.u64: + ShowWithDisplay, + } +} + +check_components! { + AppA { + ShowImplComponent: [ + String, + u64, + ] + } +} + +pub struct AppB; + +delegate_components! { + AppB { + namespace DefaultNamespace; + + for in DefaultShowComponents { + @test.ShowImplComponent.T: Provider, + } + } +} + +check_components! { + AppB { + ShowImplComponent: [ + String, + u64, + ] + } +} + +pub struct AppC; + +delegate_components! { + AppC { + namespace ExtendedNamespace; + + for in DefaultImpls1 { + @test.ShowImplComponent.T: Provider, + } + + @test.ShowImplComponent.u64: + ShowWithDisplay, + } +} + +check_components! { + AppC { + ShowImplComponent: [ + String, + u64, + u32, + ] + } +} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs index 26d24110..42cb4ff2 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs +++ b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs @@ -1,5 +1,5 @@ pub mod basic; -pub mod default_generics; +pub mod default_impls; pub mod extended_namespace; pub mod multi_namespace; pub mod symbol_path;