diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index bd4a4c07..be02ae26 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -54,8 +54,8 @@ pub fn derive_component_with_ast( )?; let mut item_impls = vec![ - consumer_impl, provider_impl, + consumer_impl, use_context_impl, use_context_is_provider_impl, ]; diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index e067a095..8dc02a9a 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -9,6 +9,7 @@ use crate::derive_getter::{ ContextArg, ReceiverMode, derive_getter_constraint, derive_getter_method, }; use crate::symbol::symbol_from_string; +use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_blanket_impl( context_type: &Ident, @@ -39,7 +40,7 @@ pub fn derive_blanket_impl( type #field_assoc_type_ident = #field_assoc_type_ident; }); - let field_constraints = &field_assoc_type.bounds; + let field_constraints = get_bounds_and_replace_self_assoc_type(field_assoc_type); generics.make_where_clause().predicates.push(parse2(quote! { #field_assoc_type_ident: #field_constraints diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index 661e98f4..cddc1977 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -9,6 +9,7 @@ use crate::derive_getter::{ ContextArg, ReceiverMode, derive_getter_constraint, derive_getter_method, }; use crate::parse::ComponentSpec; +use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_use_field_impl( spec: &ComponentSpec, @@ -43,7 +44,7 @@ pub fn derive_use_field_impl( type #field_assoc_type_ident = #field_assoc_type_ident; }); - let field_constraints = &field_assoc_type.bounds; + let field_constraints = get_bounds_and_replace_self_assoc_type(field_assoc_type); provider_generics .make_where_clause() 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 9ebdb81e..068122db 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -10,6 +10,7 @@ use crate::derive_getter::{ }; use crate::parse::ComponentSpec; use crate::symbol::symbol_from_string; +use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_use_fields_impl( spec: &ComponentSpec, @@ -36,7 +37,7 @@ pub fn derive_use_fields_impl( type #field_assoc_type_ident = #field_assoc_type_ident; }); - let field_constraints = &field_assoc_type.bounds; + let field_constraints = get_bounds_and_replace_self_assoc_type(field_assoc_type); provider_generics .make_where_clause() diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index 30a1d0d5..6f470d6a 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -5,6 +5,7 @@ use syn::{Generics, Ident, ItemImpl, ItemTrait, TraitItemType, parse_quote, pars use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ContextArg, FieldMode, ReceiverMode, derive_getter_method}; use crate::parse::ComponentSpec; +use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_with_provider_impl( spec: &ComponentSpec, @@ -50,7 +51,7 @@ pub fn derive_with_provider_impl( type #field_assoc_type_ident = #field_assoc_type_ident; }); - let field_constraints = &field_assoc_type.bounds; + let field_constraints = get_bounds_and_replace_self_assoc_type(field_assoc_type); provider_generics .make_where_clause() diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_type.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_type.rs index 4aac783a..8fbec5c0 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_type.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_type.rs @@ -7,7 +7,9 @@ use syn::{Ident, ItemTrait, parse_quote, parse2}; use crate::derive_component::derive_component_with_ast; use crate::parse::{ComponentSpec, Entries}; -use crate::type_component::{derive_type_alias, derive_type_providers, extract_item_type}; +use crate::type_component::{ + derive_type_alias, derive_type_providers, extract_item_type_from_trait, +}; pub fn cgp_type(attrs: TokenStream, body: TokenStream) -> syn::Result { let mut entries = if let Ok(provider_ident) = parse2::(attrs.clone()) { @@ -18,7 +20,7 @@ pub fn cgp_type(attrs: TokenStream, body: TokenStream) -> syn::Result syn::Result<&TraitItemType> { +pub fn extract_item_type_from_trait(consumer_trait: &ItemTrait) -> syn::Result<&TraitItemType> { if consumer_trait.items.len() != 1 { return Err(Error::new( consumer_trait.span(), @@ -67,11 +68,11 @@ pub fn derive_type_providers( ) -> syn::Result> { let context_name = &spec.context_type; - let component_name = { + let component_name: Type = { let name = &spec.component_name; let params = &spec.component_params; - parse2::(quote! { #name < #params > }) - }?; + parse2(quote! { #name < #params > })? + }; let provider_trait_name = &provider_trait.ident; @@ -85,7 +86,7 @@ pub fn derive_type_providers( let type_name = &item_type.ident; - let type_bounds = &item_type.bounds; + let type_bounds = get_bounds_and_replace_self_assoc_type(item_type); let use_type_impl: ItemImpl = parse2(quote! { impl< #type_name, #impl_generics_params > @@ -102,15 +103,15 @@ pub fn derive_type_providers( let use_type_is_provider_impl = derive_is_provider_for(&component_name, &use_type_impl)?; let with_provider_impl: ItemImpl = parse2(quote! { - impl< __Provider__, #impl_generics_params > + impl< __Provider__, #type_name, #impl_generics_params > #provider_trait_name #type_generics for WithProvider< __Provider__ > where - __Provider__: ProvideType< #context_name, #component_name >, - __Provider__::Type: #type_bounds, + __Provider__: ProvideType< #context_name, #component_name, Type = #type_name >, + #type_name: #type_bounds, #predicates { - type #type_name = __Provider__::Type; + type #type_name = #type_name; } })?; diff --git a/crates/cgp-macro-lib/src/type_component/mod.rs b/crates/cgp-macro-lib/src/type_component/mod.rs index 61eca8a7..8eaf91af 100644 --- a/crates/cgp-macro-lib/src/type_component/mod.rs +++ b/crates/cgp-macro-lib/src/type_component/mod.rs @@ -1,3 +1,5 @@ mod derive; +mod replace; pub use derive::*; +pub use replace::*; diff --git a/crates/cgp-macro-lib/src/type_component/replace.rs b/crates/cgp-macro-lib/src/type_component/replace.rs new file mode 100644 index 00000000..a97fcb01 --- /dev/null +++ b/crates/cgp-macro-lib/src/type_component/replace.rs @@ -0,0 +1,39 @@ +// pub fn replace_self_assoc_type + +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::visit_mut::{VisitMut, visit_type_mut, visit_type_param_bound_mut}; +use syn::{Ident, TraitItemType, Type, TypeParamBound, parse_quote}; + +pub fn get_bounds_and_replace_self_assoc_type( + item_type: &TraitItemType, +) -> Punctuated { + let mut bounds = item_type.bounds.clone(); + let mut visitor = ReplaceSelfAssocTypeVisitor { + type_ident: item_type.ident.clone(), + }; + + for bound in &mut bounds { + visit_type_param_bound_mut(&mut visitor, bound); + } + + bounds +} + +pub struct ReplaceSelfAssocTypeVisitor { + pub type_ident: Ident, +} + +impl VisitMut for ReplaceSelfAssocTypeVisitor { + fn visit_type_mut(&mut self, node: &mut Type) { + if let Type::Path(type_path) = node { + let type_ident = &self.type_ident; + if type_path == &parse_quote! { Self :: #type_ident } { + *node = parse_quote!(#type_ident); + return; + } + } + + visit_type_mut(self, node); + } +} diff --git a/crates/cgp-tests/tests/cgp_fn_tests/use_type.rs b/crates/cgp-tests/tests/cgp_fn_tests/use_type.rs index 662a8039..8f43135c 100644 --- a/crates/cgp-tests/tests/cgp_fn_tests/use_type.rs +++ b/crates/cgp-tests/tests/cgp_fn_tests/use_type.rs @@ -1,18 +1,56 @@ +use core::f64; use std::ops::Mul; use cgp::prelude::*; #[cgp_type] pub trait HasScalarType { - type Scalar; + type Scalar: Mul + Clone; } #[cgp_fn] -#[use_type(HasScalarType::Scalar)] -pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar -where - Scalar: Mul + Clone, -{ - let res: Scalar = width * height; - res +#[extend(HasScalarType)] +pub fn rectangle_area( + &self, + #[implicit] width: Self::Scalar, + #[implicit] height: Self::Scalar, +) -> Self::Scalar { + width * height +} + +#[derive(HasField)] +pub struct F32Rectangle { + pub width: f32, + pub height: f32, +} + +impl HasScalarType for F32Rectangle { + type Scalar = f32; +} + +#[derive(HasField)] +pub struct F64Rectangle { + pub width: f64, + pub height: f64, +} + +impl HasScalarType for F64Rectangle { + type Scalar = f64; +} + +#[test] +fn test_rectangle_area() { + let f32_rectangle = F32Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(f32_rectangle.rectangle_area(), 12.0); + + let f64_rectangle = F64Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(f64_rectangle.rectangle_area(), 12.0); } diff --git a/crates/cgp-tests/tests/component_tests/abstract_types/mod.rs b/crates/cgp-tests/tests/component_tests/abstract_types/mod.rs index e206b362..ba04dea3 100644 --- a/crates/cgp-tests/tests/component_tests/abstract_types/mod.rs +++ b/crates/cgp-tests/tests/component_tests/abstract_types/mod.rs @@ -1,3 +1,4 @@ pub mod basic; pub mod extend; pub mod foreign; +pub mod self_referential; diff --git a/crates/cgp-tests/tests/component_tests/abstract_types/self_referential.rs b/crates/cgp-tests/tests/component_tests/abstract_types/self_referential.rs new file mode 100644 index 00000000..ee7112b4 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/abstract_types/self_referential.rs @@ -0,0 +1,23 @@ +use core::ops::Mul; + +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar: Mul + Clone; +} + +pub struct App; + +delegate_components! { + App { + ScalarTypeProviderComponent: + UseType, + } +} + +check_components! { + CanUseApp for App { + ScalarTypeProviderComponent, + } +} diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs index 98e2c237..5661cac9 100644 --- a/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs @@ -1,2 +1,4 @@ pub mod auto_getter; pub mod getter; +pub mod self_referential; +pub mod self_referential_auto; diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs new file mode 100644 index 00000000..e3aa8502 --- /dev/null +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs @@ -0,0 +1,29 @@ +use core::ops::Mul; + +use cgp::prelude::*; + +#[cgp_getter] +pub trait HasScalar { + type Scalar: Mul + Clone; + + fn scalar(&self) -> &Self::Scalar; +} + +#[derive(HasField)] +pub struct App { + pub scalar: f64, +} + +delegate_components! { + App { + ScalarGetterComponent: + UseField, + } +} + +#[test] +fn test_auto_getter_scalar() { + let app = App { scalar: 2.0 }; + + assert_eq!(*app.scalar(), 2.0); +} diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs new file mode 100644 index 00000000..b054d784 --- /dev/null +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs @@ -0,0 +1,22 @@ +use core::ops::Mul; + +use cgp::prelude::*; + +#[cgp_auto_getter] +pub trait HasScalarType { + type Scalar: Mul + Clone; + + fn scalar(&self) -> &Self::Scalar; +} + +#[derive(HasField)] +pub struct App { + pub scalar: f64, +} + +#[test] +fn test_auto_getter_scalar() { + let app = App { scalar: 2.0 }; + + assert_eq!(*app.scalar(), 2.0); +}