diff --git a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs new file mode 100644 index 00000000..22542627 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs @@ -0,0 +1,30 @@ +use quote::{ToTokens, quote}; +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{ItemImpl, TypeParamBound, parse2}; + +use crate::cgp_fn::{UseTypeSpec, derive_use_type_trait_bounds, substitute_abstract_type}; + +pub fn apply_use_type_attributes_to_item_impl( + item_impl: &ItemImpl, + use_type_specs: &[UseTypeSpec], +) -> syn::Result { + let mut item_impl: ItemImpl = parse2(substitute_abstract_type( + "e! { Self }, + use_type_specs, + item_impl.to_token_stream(), + ))?; + + let bounds = derive_use_type_trait_bounds("e! { Self }, use_type_specs)?; + let bounds = Punctuated::::from_iter(bounds); + + item_impl + .generics + .make_where_clause() + .predicates + .push(parse2(quote! { + Self: #bounds + })?); + + Ok(item_impl) +} diff --git a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs new file mode 100644 index 00000000..76ec19c0 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs @@ -0,0 +1,30 @@ +use quote::ToTokens; +use syn::TypeParamBound; +use syn::punctuated::Punctuated; +use syn::token::Plus; + +use crate::cgp_fn::ImplicitArgField; +use crate::derive_getter::derive_getter_constraint; +use crate::symbol::symbol_from_string; + +pub fn build_implicit_args_bounds( + implicit_args: &[ImplicitArgField], +) -> syn::Result> { + let mut constraints: Punctuated = Punctuated::new(); + + for arg in implicit_args { + let field_symbol = symbol_from_string(&arg.field_name.to_string()); + + let constraint = derive_getter_constraint( + &arg.field_type, + &arg.field_mut, + &arg.field_mode, + field_symbol.to_token_stream(), + &None, + )?; + + constraints.push(constraint); + } + + Ok(constraints) +} diff --git a/crates/cgp-macro-lib/src/cgp_fn/derive.rs b/crates/cgp-macro-lib/src/cgp_fn/derive.rs index 85fe16af..14c1dd3f 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/derive.rs @@ -7,23 +7,13 @@ use syn::{Ident, ItemFn, Visibility}; use crate::cgp_fn::fn_body::inject_implicit_args; use crate::cgp_fn::item_impl::derive_item_impl; use crate::cgp_fn::item_trait::derive_item_trait; -use crate::cgp_fn::{extract_implicits_args, parse_function_attributes}; +use crate::cgp_fn::{extract_and_parse_implicit_args, parse_function_attributes}; pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result { - let receiver = match item_fn.sig.inputs.first() { - Some(syn::FnArg::Receiver(receiver)) => receiver.clone(), - _ => { - return Err(syn::Error::new_spanned( - &item_fn.sig.inputs, - "First argument must be self", - )); - } - }; - let visibility = item_fn.vis.clone(); item_fn.vis = Visibility::Inherited; - let implicit_args = extract_implicits_args(&receiver, &mut item_fn.sig.inputs)?; + let implicit_args = extract_and_parse_implicit_args(&mut item_fn.sig.inputs)?; let attributes = parse_function_attributes(&mut item_fn.attrs)?; diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs index 23be35c9..a8868f01 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs @@ -1,13 +1,12 @@ -use quote::{ToTokens, quote}; +use quote::quote; use syn::punctuated::Punctuated; use syn::token::Plus; use syn::{Generics, Ident, ItemFn, ItemImpl, TypeParamBound, parse2}; use crate::cgp_fn::{ - FunctionAttributes, ImplicitArgField, derive_use_type_trait_bounds, substitute_abstract_type, + FunctionAttributes, ImplicitArgField, apply_use_type_attributes_to_item_impl, + build_implicit_args_bounds, }; -use crate::derive_getter::derive_getter_constraint; -use crate::symbol::symbol_from_string; pub fn derive_item_impl( trait_ident: &Ident, @@ -49,43 +48,17 @@ pub fn derive_item_impl( } } - { + if !implicit_args.is_empty() { let where_clause = item_impl.generics.make_where_clause(); + let bounds = build_implicit_args_bounds(implicit_args)?; - for arg in implicit_args { - let field_symbol = symbol_from_string(&arg.field_name.to_string()); - - let constraint = derive_getter_constraint( - &arg.field_type, - &arg.field_mut, - &arg.field_mode, - field_symbol.to_token_stream(), - &None, - )?; - - where_clause.predicates.push(parse2(quote! { - Self: #constraint - })?); - } + where_clause.predicates.push(parse2(quote! { + Self: #bounds + })?); } if !attributes.use_type.is_empty() { - item_impl = parse2(substitute_abstract_type( - "e! { Self }, - &attributes.use_type, - item_impl.to_token_stream(), - ))?; - - let bounds = derive_use_type_trait_bounds("e! { Self }, &attributes.use_type)?; - let bounds = Punctuated::::from_iter(bounds); - - item_impl - .generics - .make_where_clause() - .predicates - .push(parse2(quote! { - Self: #bounds - })?); + item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?; } Ok(item_impl) diff --git a/crates/cgp-macro-lib/src/cgp_fn/mod.rs b/crates/cgp-macro-lib/src/cgp_fn/mod.rs index a33875ba..d4946081 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/mod.rs @@ -1,4 +1,6 @@ +mod apply_type; mod attributes; +mod bounds; mod derive; mod fn_body; mod item_impl; @@ -9,8 +11,11 @@ mod substitute_type; mod type_equality; mod use_type; +pub use apply_type::*; pub use attributes::*; +pub use bounds::*; pub use derive::*; +pub use fn_body::*; pub use parse_implicits::*; pub use spec::*; pub use substitute_type::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs b/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs index dbcd6253..e195ba9b 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs @@ -8,12 +8,22 @@ use syn::{Attribute, FnArg, Meta, Pat, PatIdent, PatType, Receiver}; use crate::cgp_fn::ImplicitArgField; use crate::derive_getter::parse_field_type; -pub fn extract_implicits_args( - receiver: &Receiver, +pub fn extract_and_parse_implicit_args( args: &mut Punctuated, ) -> syn::Result> { let implicit_fn_args = extract_implicit_args(args); + if implicit_fn_args.is_empty() { + return Ok(Vec::new()); + } + + let Some(FnArg::Receiver(receiver)) = args.first() else { + return Err(syn::Error::new_spanned( + &args, + "The first argument of a function with implicit arguments must be `self`", + )); + }; + if receiver.mutability.is_some() && implicit_fn_args.len() > 1 { return Err(syn::Error::new_spanned( &args, diff --git a/crates/cgp-macro-lib/src/cgp_fn/spec.rs b/crates/cgp-macro-lib/src/cgp_fn/spec.rs index 8b52c87e..a6bdd61d 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/spec.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/spec.rs @@ -5,6 +5,7 @@ use crate::cgp_fn::UseTypeSpec; use crate::derive_getter::FieldMode; use crate::parse::SimpleType; +#[derive(Clone, Eq, PartialEq)] pub struct ImplicitArgField { pub field_name: Ident, pub field_type: Type, diff --git a/crates/cgp-macro-lib/src/cgp_impl/attributes.rs b/crates/cgp-macro-lib/src/cgp_impl/attributes.rs new file mode 100644 index 00000000..a492d491 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/attributes.rs @@ -0,0 +1,40 @@ +use core::mem; + +use syn::Attribute; +use syn::punctuated::Punctuated; +use syn::token::Comma; + +use crate::cgp_fn::UseTypeSpec; +use crate::parse::SimpleType; + +pub fn parse_impl_attributes(attributes: &mut Vec) -> syn::Result { + let mut parsed_attributes = ImplAttributes::default(); + + let in_attributes = mem::take(attributes); + + for attribute in in_attributes.into_iter() { + if let Some(ident) = attribute.path().get_ident() { + if ident == "uses" { + let uses = + attribute.parse_args_with(Punctuated::::parse_terminated)?; + parsed_attributes.uses.extend(uses); + } else if ident == "use_type" { + let use_type = attribute + .parse_args_with(Punctuated::::parse_terminated)?; + parsed_attributes.use_type.extend(use_type); + } else { + attributes.push(attribute); + } + } else { + attributes.push(attribute); + } + } + + Ok(parsed_attributes) +} + +#[derive(Default)] +pub struct ImplAttributes { + pub uses: Vec, + pub use_type: Vec, +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs new file mode 100644 index 00000000..d8df9edb --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -0,0 +1,73 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{ItemImpl, TypeParamBound, parse2}; + +use crate::cgp_fn::{apply_use_type_attributes_to_item_impl, build_implicit_args_bounds}; +use crate::cgp_impl::attributes::parse_impl_attributes; +use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl, implicit_args}; +use crate::derive_provider::{ + derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, +}; + +pub fn derive_cgp_impl( + spec: ImplProviderSpec, + mut item_impl: ItemImpl, +) -> syn::Result { + let attributes = parse_impl_attributes(&mut item_impl.attrs)?; + + let implicit_args = implicit_args::extract_implicit_args_from_impl_items(&mut item_impl.items)?; + + if !implicit_args.is_empty() { + let where_clause = item_impl.generics.make_where_clause(); + let bounds = build_implicit_args_bounds(&implicit_args)?; + + where_clause.predicates.push(parse2(quote! { + Self: #bounds + })?); + } + + if !attributes.use_type.is_empty() { + item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?; + } + + if !attributes.uses.is_empty() { + let mut bounds: Punctuated = Punctuated::default(); + + for import in attributes.uses.iter() { + bounds.push(parse2(quote! { #import })?); + } + + item_impl + .generics + .make_where_clause() + .predicates + .push(parse2(quote! { + Self: #bounds + })?); + } + + let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; + + let component_type = match &spec.component_type { + Some(component_type) => component_type.clone(), + None => derive_component_name_from_provider_impl(&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)?) + } else { + None + }; + + Ok(quote! { + #provider_struct + + #provider_impl + + #is_provider_for_impl + }) +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs new file mode 100644 index 00000000..8f351bdc --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs @@ -0,0 +1,24 @@ +use syn::ImplItem; + +use crate::cgp_fn::{ImplicitArgField, extract_and_parse_implicit_args, inject_implicit_args}; + +pub fn extract_implicit_args_from_impl_items( + impl_items: &mut [ImplItem], +) -> syn::Result> { + let mut all_implicit_args = Vec::new(); + + for item in impl_items { + if let ImplItem::Fn(method) = item { + let implicit_args = extract_and_parse_implicit_args(&mut method.sig.inputs)?; + inject_implicit_args(&implicit_args, &mut method.block)?; + + for implicit_arg in implicit_args { + if !all_implicit_args.contains(&implicit_arg) { + all_implicit_args.push(implicit_arg); + } + } + } + } + + Ok(all_implicit_args) +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs new file mode 100644 index 00000000..8a286d1c --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -0,0 +1,11 @@ +mod attributes; +mod derive; +mod implicit_args; +mod provider_impl; +mod spec; +mod transform; + +pub use derive::*; +pub use provider_impl::*; +pub use spec::*; +pub use transform::*; diff --git a/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs b/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs new file mode 100644 index 00000000..c357ec8d --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs @@ -0,0 +1,38 @@ +use quote::ToTokens; +use syn::{ItemImpl, Type, parse_quote, parse2}; + +use crate::cgp_impl::transform_impl_trait; + +pub fn derive_provider_impl( + provider_type: &Type, + mut item_impl: ItemImpl, +) -> syn::Result { + match &item_impl.trait_ { + Some((_, path, _)) => { + let consumer_trait_path = parse2(path.to_token_stream())?; + let context_type = item_impl.self_ty.as_ref(); + transform_impl_trait( + &item_impl, + &consumer_trait_path, + provider_type, + context_type, + ) + } + None => { + let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; + let context_type = parse_quote! { __Context__ }; + + item_impl + .generics + .params + .insert(0, parse_quote! { __Context__ }); + + transform_impl_trait( + &item_impl, + &consumer_trait_path, + provider_type, + &context_type, + ) + } + } +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/spec.rs b/crates/cgp-macro-lib/src/cgp_impl/spec.rs new file mode 100644 index 00000000..5c7dcc34 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/spec.rs @@ -0,0 +1,41 @@ +use syn::parse::discouraged::Speculative; +use syn::parse::{Parse, ParseStream}; +use syn::token::Colon; +use syn::{Ident, Type}; + +pub struct ImplProviderSpec { + pub new_struct: bool, + pub provider_type: Type, + pub component_type: Option, +} + +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 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, + }) + } +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/transform.rs b/crates/cgp-macro-lib/src/cgp_impl/transform.rs new file mode 100644 index 00000000..7fda1cb8 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/transform.rs @@ -0,0 +1,76 @@ +use proc_macro2::Span; +use quote::{ToTokens, quote}; +use syn::token::For; +use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse2}; + +use crate::parse::SimpleType; +use crate::replace_self::{ + replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident, +}; + +pub fn transform_impl_trait( + item_impl: &ItemImpl, + consumer_trait_path: &SimpleType, + provider_type: &Type, + context_type: &Type, +) -> syn::Result { + 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()) + }; + + 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_type.to_token_stream(), + &local_assoc_types, + ); + + let mut out_impl: ItemImpl = parse2(raw_out_impl)?; + out_impl.self_ty = Box::new(provider_type.clone()); + + let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); + + match &mut provider_trait_path.generics { + Some(generics) => { + generics + .args + .insert(0, parse2(context_type.to_token_stream())?); + } + None => { + provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?); + } + } + + out_impl.trait_ = Some(( + None, + parse2(provider_trait_path.to_token_stream())?, + For(Span::call_site()), + )); + + for item in out_impl.items.iter_mut() { + 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)?; + } + } + + Ok(out_impl) +} diff --git a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs index 8fec551f..547b9c17 100644 --- a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs @@ -11,6 +11,7 @@ pub struct GetterField { pub receiver_mode: ReceiverMode, } +#[derive(Clone, Eq, PartialEq)] pub enum FieldMode { Reference, OptionRef, diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index b2652d0e..199b9029 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,173 +1,11 @@ -use proc_macro2::{Span, TokenStream}; -use quote::{ToTokens, quote}; -use syn::parse::discouraged::Speculative; -use syn::parse::{Parse, ParseStream}; -use syn::token::{Colon, For}; -use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse_quote, parse2}; +use proc_macro2::TokenStream; +use syn::{ItemImpl, parse2}; -use crate::derive_provider::{ - derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, -}; -use crate::parse::SimpleType; -use crate::replace_self::{ - replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident, -}; +use crate::cgp_impl::{ImplProviderSpec, derive_cgp_impl}; pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { let spec: ImplProviderSpec = parse2(attr)?; - let mut item_impl: ItemImpl = parse2(body)?; + let item_impl: ItemImpl = parse2(body)?; - let provider_impl = match &item_impl.trait_ { - Some((_, path, _)) => { - let consumer_trait_path = parse2(path.to_token_stream())?; - let context_type = item_impl.self_ty.as_ref(); - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - context_type, - )? - } - None => { - let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; - let context_type = parse_quote! { __Context__ }; - - item_impl - .generics - .params - .insert(0, parse_quote! { __Context__ }); - - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - &context_type, - )? - } - }; - - let component_type = match &spec.component_type { - Some(component_type) => component_type.clone(), - None => derive_component_name_from_provider_impl(&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)?) - } else { - None - }; - - Ok(quote! { - #provider_struct - - #provider_impl - - #is_provider_for_impl - }) -} - -pub struct ImplProviderSpec { - pub new_struct: bool, - pub provider_type: Type, - pub component_type: Option, -} - -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 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, - }) - } -} - -pub fn transform_impl_trait( - item_impl: &ItemImpl, - consumer_trait_path: &SimpleType, - provider_type: &Type, - context_type: &Type, -) -> syn::Result { - 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()) - }; - - 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_type.to_token_stream(), - &local_assoc_types, - ); - - let mut out_impl: ItemImpl = parse2(raw_out_impl)?; - out_impl.self_ty = Box::new(provider_type.clone()); - - let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); - - match &mut provider_trait_path.generics { - Some(generics) => { - generics - .args - .insert(0, parse2(context_type.to_token_stream())?); - } - None => { - provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?); - } - } - - out_impl.trait_ = Some(( - None, - parse2(provider_trait_path.to_token_stream())?, - For(Span::call_site()), - )); - - for item in out_impl.items.iter_mut() { - 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)?; - } - } - - Ok(out_impl) + derive_cgp_impl(spec, item_impl) } diff --git a/crates/cgp-macro-lib/src/lib.rs b/crates/cgp-macro-lib/src/lib.rs index 15fe3b25..ff9bd1fb 100644 --- a/crates/cgp-macro-lib/src/lib.rs +++ b/crates/cgp-macro-lib/src/lib.rs @@ -9,6 +9,7 @@ extern crate alloc; pub(crate) mod blanket_trait; pub(crate) mod cgp_fn; +pub(crate) mod cgp_impl; pub(crate) mod check_components; pub(crate) mod delegate_components; pub(crate) mod derive_builder; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs new file mode 100644 index 00000000..4df1090b --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs @@ -0,0 +1,40 @@ +use std::ops::Mul; + +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea: HasScalarType { + fn area(&self) -> Self::Scalar; +} + +#[cgp_impl(new RectangleArea)] +#[use_type(HasScalarType::Scalar)] +impl AreaCalculator +where + Scalar: Mul + Clone, +{ + fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { + width * height + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + ScalarTypeProviderComponent: + UseType, + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs new file mode 100644 index 00000000..38883ee0 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs @@ -0,0 +1 @@ +pub mod basic; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs new file mode 100644 index 00000000..a80440a0 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs @@ -0,0 +1,27 @@ +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleArea)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs new file mode 100644 index 00000000..d7692e2e --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs @@ -0,0 +1,33 @@ +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +#[uses(RectangleArea)] +impl AreaCalculator { + fn area(&self) -> f64 { + self.rectangle_area() + } +} + +#[cgp_fn] +pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + AreaCalculatorComponent: + RectangleAreaCalculator, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs new file mode 100644 index 00000000..65c0dea3 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs @@ -0,0 +1,2 @@ +pub mod basic; +pub mod import; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs index a1da2f99..ebe001d3 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs @@ -1,2 +1,4 @@ +pub mod abstract_types; pub mod basic; +pub mod implicit_args; pub mod implicit_context;