Skip to content

Commit 3e24278

Browse files
authored
Automatically derive UseContext implementation inside #[cgp_component] (#88)
* Automatically derive UseContext implementation inside #[cgp_component] * Fix tests
1 parent 55882f2 commit 3e24278

File tree

13 files changed

+298
-66
lines changed

13 files changed

+298
-66
lines changed

crates/cgp-core/src/prelude.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub use cgp_async::{async_trait, Async, MaybeSend, MaybeStatic, MaybeSync};
22
pub use cgp_component::{
3-
CanUseComponent, DelegateComponent, HasProvider, IsProviderFor, UseFields, WithContext,
4-
WithProvider,
3+
CanUseComponent, DelegateComponent, HasProvider, IsProviderFor, UseContext, UseFields,
4+
WithContext, WithProvider,
55
};
66
pub use cgp_error::{
77
CanRaiseAsyncError, CanRaiseError, CanWrapAsyncError, CanWrapError, HasAsyncErrorType,

crates/cgp-error/src/traits/can_raise_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, UseDelegate};
1+
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, UseContext, UseDelegate};
22
use cgp_macro::{cgp_component, cgp_provider};
33

44
use crate::traits::has_error_type::HasErrorType;

crates/cgp-error/src/traits/can_wrap_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, UseDelegate};
1+
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, UseContext, UseDelegate};
22
use cgp_macro::{cgp_component, cgp_provider};
33

44
use crate::traits::HasErrorType;

crates/cgp-error/src/traits/has_error_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::fmt::Debug;
22

3-
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, WithProvider};
3+
use cgp_component::{DelegateComponent, HasProvider, IsProviderFor, UseContext, WithProvider};
44
use cgp_macro::cgp_type;
55
use cgp_type::{ProvideType, UseType};
66

crates/cgp-macro-lib/src/derive_component/delegate_fn.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ use alloc::vec::Vec;
22

33
use proc_macro2::TokenStream;
44
use quote::quote;
5-
use syn::{parse2, ImplItemFn, Signature, TypePath, Visibility};
5+
use syn::punctuated::Punctuated;
6+
use syn::token::Comma;
7+
use syn::{parse2, ImplItemFn, Signature, Visibility};
68

79
use crate::derive_component::signature_args::signature_to_args;
810

9-
pub fn derive_delegated_fn_impl(sig: &Signature, delegator: &TypePath) -> syn::Result<ImplItemFn> {
11+
pub fn derive_delegated_fn_impl(
12+
sig: &Signature,
13+
delegator: &TokenStream,
14+
) -> syn::Result<ImplItemFn> {
1015
let fn_name = &sig.ident;
1116

12-
let args = signature_to_args(sig);
17+
let args: Punctuated<_, Comma> = signature_to_args(sig).collect();
1318

1419
let await_expr: TokenStream = if sig.asyncness.is_some() {
1520
quote!( .await )

crates/cgp-macro-lib/src/derive_component/derive.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use proc_macro2::TokenStream;
2-
use quote::ToTokens;
3-
use syn::{ItemImpl, ItemStruct, ItemTrait};
2+
use quote::{quote, ToTokens};
3+
use syn::{parse2, ItemImpl, ItemStruct, ItemTrait};
44

55
use crate::derive_component::component_name::derive_component_name_struct;
66
use crate::derive_component::consumer_impl::derive_consumer_impl;
77
use crate::derive_component::provider_impl::derive_provider_impl;
88
use crate::derive_component::provider_trait::derive_provider_trait;
9+
use crate::derive_component::use_context_impl::derive_use_context_impl;
10+
use crate::derive_provider::derive_is_provider_for;
911
use crate::parse::ComponentSpec;
1012

1113
pub fn derive_component_with_ast(
@@ -15,12 +17,14 @@ pub fn derive_component_with_ast(
1517
let provider_name = &spec.provider_name;
1618
let context_type = &spec.context_type;
1719

18-
let component_struct =
19-
derive_component_name_struct(&spec.component_name, &spec.component_params)?;
20+
let component_name = &spec.component_name;
21+
let component_params = &spec.component_params;
22+
23+
let component_struct = derive_component_name_struct(component_name, component_params)?;
2024

2125
let provider_trait = derive_provider_trait(
22-
&spec.component_name,
23-
&spec.component_params,
26+
component_name,
27+
component_params,
2428
&consumer_trait,
2529
provider_name,
2630
context_type,
@@ -32,8 +36,17 @@ pub fn derive_component_with_ast(
3236
context_type,
3337
&consumer_trait,
3438
&provider_trait,
35-
&spec.component_name,
36-
&spec.component_params,
39+
component_name,
40+
component_params,
41+
)?;
42+
43+
let use_context_impl = derive_use_context_impl(context_type, &consumer_trait, &provider_trait)?;
44+
45+
let use_context_is_provider_impl = derive_is_provider_for(
46+
&parse2(quote! {
47+
#component_name < #component_params >
48+
})?,
49+
&use_context_impl,
3750
)?;
3851

3952
let derived = DerivedComponent {
@@ -42,6 +55,8 @@ pub fn derive_component_with_ast(
4255
provider_trait,
4356
consumer_impl,
4457
provider_impl,
58+
use_context_impl,
59+
use_context_is_provider_impl,
4560
};
4661

4762
Ok(derived)
@@ -53,6 +68,8 @@ pub struct DerivedComponent {
5368
pub provider_trait: ItemTrait,
5469
pub consumer_impl: ItemImpl,
5570
pub provider_impl: ItemImpl,
71+
pub use_context_impl: ItemImpl,
72+
pub use_context_is_provider_impl: ItemImpl,
5673
}
5774

5875
impl ToTokens for DerivedComponent {
@@ -62,5 +79,7 @@ impl ToTokens for DerivedComponent {
6279
self.provider_trait.to_tokens(tokens);
6380
self.consumer_impl.to_tokens(tokens);
6481
self.provider_impl.to_tokens(tokens);
82+
self.use_context_impl.to_tokens(tokens);
83+
self.use_context_is_provider_impl.to_tokens(tokens);
6584
}
6685
}

crates/cgp-macro-lib/src/derive_component/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod replace_self_receiver;
99
mod replace_self_type;
1010
mod signature_args;
1111
mod snake_case;
12+
mod use_context_impl;
1213

1314
pub use derive::*;
1415
pub use replace_self_type::*;

crates/cgp-macro-lib/src/derive_component/provider_impl.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use alloc::vec::Vec;
44
use proc_macro2::Span;
55
use quote::quote;
66
use syn::punctuated::Punctuated;
7+
use syn::spanned::Spanned;
78
use syn::token::{Brace, Comma, For, Impl, Plus};
89
use syn::{
9-
parse2, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Path, TraitItem, TypeParamBound,
10+
parse2, Error, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Path, TraitItem,
11+
TypeParamBound,
1012
};
1113

1214
use crate::derive_component::delegate_fn::derive_delegated_fn_impl;
@@ -54,24 +56,15 @@ pub fn derive_provider_impl(
5456
#provider_name < #provider_generic_args >
5557
})?;
5658

57-
match &mut impl_generics.where_clause {
58-
Some(where_clause) => {
59-
where_clause.predicates.push(parse2(quote! {
60-
#component_type : #delegate_constraint
61-
})?);
62-
63-
where_clause.predicates.push(parse2(quote! {
64-
#component_type :: Delegate : #provider_constraint
65-
})?);
66-
}
67-
_ => {
68-
impl_generics.where_clause = Some(parse2(quote! {
69-
where
70-
#component_type : #delegate_constraint,
71-
#component_type :: Delegate : #provider_constraint
72-
})?);
73-
}
74-
}
59+
let where_clause = impl_generics.make_where_clause();
60+
61+
where_clause.predicates.push(parse2(quote! {
62+
#component_type : #delegate_constraint
63+
})?);
64+
65+
where_clause.predicates.push(parse2(quote! {
66+
#component_type :: Delegate : #provider_constraint
67+
})?);
7568
}
7669

7770
impl_generics
@@ -82,15 +75,14 @@ pub fn derive_provider_impl(
8275
for trait_item in provider_trait.items.iter() {
8376
match &trait_item {
8477
TraitItem::Fn(trait_fn) => {
85-
let impl_fn = derive_delegated_fn_impl(
86-
&trait_fn.sig,
87-
&parse2(quote!(#component_type :: Delegate))?,
88-
)?;
78+
let impl_fn =
79+
derive_delegated_fn_impl(&trait_fn.sig, &quote!(#component_type :: Delegate))?;
8980

9081
impl_items.push(ImplItem::Fn(impl_fn))
9182
}
9283
TraitItem::Type(trait_type) => {
9384
let type_name = &trait_type.ident;
85+
9486
let type_generics = {
9587
let mut type_generics = trait_type.generics.clone();
9688
type_generics.where_clause = None;
@@ -113,7 +105,12 @@ pub fn derive_provider_impl(
113105

114106
impl_items.push(ImplItem::Type(impl_type));
115107
}
116-
_ => {}
108+
_ => {
109+
return Err(Error::new(
110+
trait_item.span(),
111+
format!("unsupported trait item: {trait_item:?}"),
112+
))
113+
}
117114
}
118115
}
119116

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
use proc_macro2::Span;
2-
use syn::punctuated::Punctuated;
3-
use syn::token::Comma;
42
use syn::{parse_quote, FnArg, Ident, Signature};
53

6-
pub fn signature_to_args(sig: &Signature) -> Punctuated<Ident, Comma> {
7-
let args = sig
8-
.inputs
9-
.iter()
10-
.map(|arg| -> Ident {
11-
match arg {
12-
FnArg::Receiver(_) => Ident::new("self", Span::call_site()),
13-
FnArg::Typed(pat) => {
14-
let ident_pat = &pat.pat;
15-
parse_quote!( #ident_pat )
16-
}
4+
pub fn signature_to_args(sig: &Signature) -> impl Iterator<Item = Ident> + '_ {
5+
sig.inputs.iter().map(|arg| -> Ident {
6+
match arg {
7+
FnArg::Receiver(_) => Ident::new("self", Span::call_site()),
8+
FnArg::Typed(pat) => {
9+
let ident_pat = &pat.pat;
10+
parse_quote!( #ident_pat )
1711
}
18-
})
19-
.collect();
20-
21-
args
12+
}
13+
})
2214
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use quote::quote;
2+
use syn::spanned::Spanned;
3+
use syn::token::{Brace, For, Impl};
4+
use syn::{parse2, Error, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Path, TraitItem};
5+
6+
use crate::derive_component::delegate_fn::derive_delegated_fn_impl;
7+
use crate::derive_component::delegate_type::derive_delegate_type_impl;
8+
use crate::parse::TypeGenerics;
9+
10+
pub fn derive_use_context_impl(
11+
context_type: &Ident,
12+
consumer_trait: &ItemTrait,
13+
provider_trait: &ItemTrait,
14+
) -> syn::Result<ItemImpl> {
15+
let consumer_trait_ident = &consumer_trait.ident;
16+
let provider_trait_ident = &provider_trait.ident;
17+
18+
let provider_generics = TypeGenerics::try_from(&provider_trait.generics)?.generics;
19+
20+
let consumer_generics = TypeGenerics::try_from(&consumer_trait.generics)?.generics;
21+
22+
let mut impl_generics = provider_trait.generics.clone();
23+
24+
let where_clause = impl_generics.make_where_clause();
25+
26+
where_clause.predicates.push(parse2(quote! {
27+
#context_type : #consumer_trait_ident #consumer_generics
28+
})?);
29+
30+
let mut impl_items: Vec<ImplItem> = Vec::new();
31+
32+
for trait_item in provider_trait.items.iter() {
33+
match &trait_item {
34+
TraitItem::Fn(trait_fn) => {
35+
let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, &quote!( #context_type ))?;
36+
37+
impl_items.push(ImplItem::Fn(impl_fn))
38+
}
39+
TraitItem::Type(trait_type) => {
40+
let type_name = &trait_type.ident;
41+
42+
let type_generics = {
43+
let mut type_generics = trait_type.generics.clone();
44+
type_generics.where_clause = None;
45+
46+
for param in &mut type_generics.params {
47+
if let GenericParam::Type(type_param) = param {
48+
type_param.bounds.clear();
49+
}
50+
}
51+
52+
type_generics
53+
};
54+
55+
let impl_type = derive_delegate_type_impl(
56+
trait_type,
57+
parse2(quote!(
58+
#context_type :: #type_name #type_generics
59+
))?,
60+
);
61+
62+
impl_items.push(ImplItem::Type(impl_type));
63+
}
64+
_ => {
65+
return Err(Error::new(
66+
trait_item.span(),
67+
format!("unsupported trait item: {trait_item:?}"),
68+
))
69+
}
70+
}
71+
}
72+
73+
let trait_path: Path = parse2(quote!( #provider_trait_ident #provider_generics ))?;
74+
75+
let item = ItemImpl {
76+
attrs: provider_trait.attrs.clone(),
77+
defaultness: None,
78+
unsafety: provider_trait.unsafety,
79+
impl_token: Impl::default(),
80+
generics: impl_generics,
81+
trait_: Some((None, trait_path, For::default())),
82+
self_ty: Box::new(parse2(quote!(UseContext))?),
83+
brace_token: Brace::default(),
84+
items: impl_items,
85+
};
86+
87+
Ok(item)
88+
}

0 commit comments

Comments
 (0)