Skip to content

Commit 147d5e6

Browse files
authored
Support derivation of UseDelegate inside #[cgp_component] (#106)
* Scaffold parsing of use_delegate param in #[cgp_component] * Draft implement derive_use_delegate_impl * Parse use_delegate spec into list of idents * Derive UseDelegate in main derive * deriving UseDelegate is working * Derive UseDelegate automatically * Generalize syntax of use_delegate * Allow multiple delegate derivations * Rename AST to DeriveDelegateSpec * Support arbitrary TokenStream value in component entry * Allow use of bracket as component spec value * Test use of derive_delegate
1 parent 178c712 commit 147d5e6

File tree

15 files changed

+387
-135
lines changed

15 files changed

+387
-135
lines changed
Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use cgp_component::{DelegateComponent, HasCgpProvider, IsProviderFor, UseContext, UseDelegate};
2-
use cgp_macro::{cgp_component, cgp_provider};
2+
use cgp_macro::cgp_component;
33

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

@@ -12,21 +12,9 @@ use crate::traits::has_error_type::HasErrorType;
1212
a [`Context::Error`](HasErrorType::Error) value.
1313
*/
1414
#[cgp_component {
15-
provider: ErrorRaiser
15+
provider: ErrorRaiser,
16+
derive_delegate: UseDelegate<SourceError>,
1617
}]
1718
pub trait CanRaiseError<SourceError>: HasErrorType {
1819
fn raise_error(error: SourceError) -> Self::Error;
1920
}
20-
21-
#[cgp_provider(ErrorRaiserComponent)]
22-
impl<Context, SourceError, Components, Delegate> ErrorRaiser<Context, SourceError>
23-
for UseDelegate<Components>
24-
where
25-
Context: HasErrorType,
26-
Components: DelegateComponent<SourceError, Delegate = Delegate>,
27-
Delegate: ErrorRaiser<Context, SourceError>,
28-
{
29-
fn raise_error(e: SourceError) -> Context::Error {
30-
Delegate::raise_error(e)
31-
}
32-
}
Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
11
use cgp_component::{DelegateComponent, HasCgpProvider, IsProviderFor, UseContext, UseDelegate};
2-
use cgp_macro::{cgp_component, cgp_provider};
2+
use cgp_macro::cgp_component;
33

44
use crate::traits::HasErrorType;
55

66
#[cgp_component {
77
provider: ErrorWrapper,
8+
derive_delegate: UseDelegate<Detail>,
89
}]
910
pub trait CanWrapError<Detail>: HasErrorType {
1011
fn wrap_error(error: Self::Error, detail: Detail) -> Self::Error;
1112
}
12-
13-
#[cgp_provider(ErrorWrapperComponent)]
14-
impl<Context, Detail, Components> ErrorWrapper<Context, Detail> for UseDelegate<Components>
15-
where
16-
Context: HasErrorType,
17-
Components: DelegateComponent<Detail>,
18-
Components::Delegate: ErrorWrapper<Context, Detail>,
19-
{
20-
fn wrap_error(error: Context::Error, detail: Detail) -> Context::Error {
21-
Components::Delegate::wrap_error(error, detail)
22-
}
23-
}

crates/cgp-handler/src/components/compute.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,12 @@ use core::marker::PhantomData;
33
use cgp_core::component::UseDelegate;
44
use cgp_core::prelude::*;
55

6-
#[cgp_component(Computer)]
6+
#[cgp_component {
7+
provider: Computer,
8+
derive_delegate: UseDelegate<Code>,
9+
}]
710
pub trait CanCompute<Code, Input> {
811
type Output;
912

1013
fn compute(&self, _tag: PhantomData<Code>, input: Input) -> Self::Output;
1114
}
12-
13-
#[cgp_provider]
14-
impl<Context, Code, Input, Components, Delegate> Computer<Context, Code, Input>
15-
for UseDelegate<Components>
16-
where
17-
Components: DelegateComponent<Code, Delegate = Delegate>,
18-
Delegate: Computer<Context, Code, Input>,
19-
{
20-
type Output = Delegate::Output;
21-
22-
fn compute(context: &Context, tag: PhantomData<Code>, input: Input) -> Self::Output {
23-
Delegate::compute(context, tag, input)
24-
}
25-
}

crates/cgp-handler/src/components/handler.rs

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use core::marker::PhantomData;
33
use cgp_core::component::UseDelegate;
44
use cgp_core::prelude::*;
55

6-
#[cgp_component(Handler)]
6+
#[cgp_component {
7+
provider: Handler,
8+
derive_delegate: UseDelegate<Code>,
9+
}]
710
#[async_trait]
811
pub trait CanHandle<Code: Send, Input: Send>: HasAsyncErrorType {
912
type Output: Send;
@@ -14,24 +17,3 @@ pub trait CanHandle<Code: Send, Input: Send>: HasAsyncErrorType {
1417
input: Input,
1518
) -> Result<Self::Output, Self::Error>;
1619
}
17-
18-
#[cgp_provider]
19-
impl<Context, Code, Input, Components, Delegate> Handler<Context, Code, Input>
20-
for UseDelegate<Components>
21-
where
22-
Context: HasAsyncErrorType,
23-
Components: DelegateComponent<Code, Delegate = Delegate>,
24-
Delegate: Handler<Context, Code, Input>,
25-
Code: Send,
26-
Input: Send,
27-
{
28-
type Output = Delegate::Output;
29-
30-
async fn handle(
31-
context: &Context,
32-
tag: PhantomData<Code>,
33-
input: Input,
34-
) -> Result<Self::Output, Context::Error> {
35-
Delegate::handle(context, tag, input).await
36-
}
37-
}

crates/cgp-handler/src/components/produce.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,12 @@ use core::marker::PhantomData;
33
use cgp_core::component::UseDelegate;
44
use cgp_core::prelude::*;
55

6-
#[cgp_component(Producer)]
6+
#[cgp_component {
7+
provider: Producer,
8+
derive_delegate: UseDelegate<Code>,
9+
}]
710
pub trait CanProduce<Code> {
811
type Output;
912

1013
fn produce(&self, _tag: PhantomData<Code>) -> Self::Output;
1114
}
12-
13-
#[cgp_provider]
14-
impl<Context, Code, Components, Delegate> Producer<Context, Code> for UseDelegate<Components>
15-
where
16-
Context: HasAsyncErrorType,
17-
Components: DelegateComponent<Code, Delegate = Delegate>,
18-
Delegate: Producer<Context, Code>,
19-
Code: Send,
20-
{
21-
type Output = Delegate::Output;
22-
23-
fn produce(context: &Context, tag: PhantomData<Code>) -> Self::Output {
24-
Delegate::produce(context, tag)
25-
}
26-
}

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

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use proc_macro2::TokenStream;
2-
use quote::{quote, ToTokens};
2+
use quote::{quote, ToTokens, TokenStreamExt};
33
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;
99
use crate::derive_component::use_context_impl::derive_use_context_impl;
10+
use crate::derive_component::use_delegate_impl::derive_delegate_impl;
1011
use crate::derive_provider::derive_is_provider_for;
1112
use crate::parse::ComponentSpec;
1213

@@ -49,14 +50,34 @@ pub fn derive_component_with_ast(
4950
&use_context_impl,
5051
)?;
5152

52-
let derived = DerivedComponent {
53-
component_struct,
54-
consumer_trait,
55-
provider_trait,
53+
let mut item_impls = vec![
5654
consumer_impl,
5755
provider_impl,
5856
use_context_impl,
5957
use_context_is_provider_impl,
58+
];
59+
60+
if !spec.use_delegate_spec.is_empty() {
61+
for spec in spec.use_delegate_spec.iter() {
62+
let use_delegate_impl = derive_delegate_impl(&provider_trait, spec)?;
63+
64+
let use_delegate_is_provider_impl = derive_is_provider_for(
65+
&parse2(quote! {
66+
#component_name < #component_params >
67+
})?,
68+
&use_delegate_impl,
69+
)?;
70+
71+
item_impls.push(use_delegate_impl);
72+
item_impls.push(use_delegate_is_provider_impl);
73+
}
74+
}
75+
76+
let derived = DerivedComponent {
77+
component_struct,
78+
consumer_trait,
79+
provider_trait,
80+
item_impls,
6081
};
6182

6283
Ok(derived)
@@ -66,20 +87,14 @@ pub struct DerivedComponent {
6687
pub component_struct: ItemStruct,
6788
pub consumer_trait: ItemTrait,
6889
pub provider_trait: ItemTrait,
69-
pub consumer_impl: ItemImpl,
70-
pub provider_impl: ItemImpl,
71-
pub use_context_impl: ItemImpl,
72-
pub use_context_is_provider_impl: ItemImpl,
90+
pub item_impls: Vec<ItemImpl>,
7391
}
7492

7593
impl ToTokens for DerivedComponent {
7694
fn to_tokens(&self, tokens: &mut TokenStream) {
7795
self.component_struct.to_tokens(tokens);
7896
self.consumer_trait.to_tokens(tokens);
7997
self.provider_trait.to_tokens(tokens);
80-
self.consumer_impl.to_tokens(tokens);
81-
self.provider_impl.to_tokens(tokens);
82-
self.use_context_impl.to_tokens(tokens);
83-
self.use_context_is_provider_impl.to_tokens(tokens);
98+
tokens.append_all(self.item_impls.iter());
8499
}
85100
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod replace_self_type;
1010
mod signature_args;
1111
mod snake_case;
1212
mod use_context_impl;
13+
mod use_delegate_impl;
1314

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

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

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use quote::quote;
33
use syn::spanned::Spanned;
44
use syn::token::{Brace, Eq, For, Impl};
55
use syn::{
6-
parse2, Error, GenericParam, Ident, ImplItem, ImplItemConst, ItemImpl, ItemTrait, Path,
7-
TraitItem, Visibility,
6+
parse2, Error, Ident, ImplItem, ImplItemConst, ItemImpl, ItemTrait, Path, TraitItem, Visibility,
87
};
98

109
use crate::derive_component::delegate_fn::derive_delegated_fn_impl;
@@ -34,7 +33,7 @@ pub fn derive_use_context_impl(
3433
let mut impl_items: Vec<ImplItem> = Vec::new();
3534

3635
for trait_item in provider_trait.items.iter() {
37-
match &trait_item {
36+
match trait_item {
3837
TraitItem::Fn(trait_fn) => {
3938
let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, &quote!( #context_type ))?;
4039

@@ -43,18 +42,7 @@ pub fn derive_use_context_impl(
4342
TraitItem::Type(trait_type) => {
4443
let type_name = &trait_type.ident;
4544

46-
let type_generics = {
47-
let mut type_generics = trait_type.generics.clone();
48-
type_generics.where_clause = None;
49-
50-
for param in &mut type_generics.params {
51-
if let GenericParam::Type(type_param) = param {
52-
type_param.bounds.clear();
53-
}
54-
}
55-
56-
type_generics
57-
};
45+
let type_generics = trait_type.generics.split_for_impl().1;
5846

5947
let impl_type = derive_delegate_type_impl(
6048
trait_type,
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use proc_macro2::Span;
2+
use quote::quote;
3+
use syn::spanned::Spanned;
4+
use syn::token::{Brace, Eq, For, Impl};
5+
use syn::{
6+
parse2, Error, Ident, ImplItem, ImplItemConst, ItemImpl, ItemTrait, Path, TraitItem, Visibility,
7+
};
8+
9+
use crate::derive_component::delegate_fn::derive_delegated_fn_impl;
10+
use crate::derive_component::delegate_type::derive_delegate_type_impl;
11+
use crate::parse::DeriveDelegateSpec;
12+
13+
pub fn derive_delegate_impl(
14+
provider_trait: &ItemTrait,
15+
spec: &DeriveDelegateSpec,
16+
) -> syn::Result<ItemImpl> {
17+
let provider_trait_ident = &provider_trait.ident;
18+
19+
let components_ident = Ident::new("__Components__", Span::call_site());
20+
let delegate_ident = Ident::new("__Delegate__", Span::call_site());
21+
22+
let wrapper_ident = &spec.wrapper;
23+
let use_delegate_params = &spec.params;
24+
25+
let generics = {
26+
let mut generics = provider_trait.generics.clone();
27+
28+
generics.params.push(parse2(quote!( #components_ident ))?);
29+
generics.params.push(parse2(quote!( #delegate_ident ))?);
30+
31+
let where_clause = generics.make_where_clause();
32+
33+
where_clause.predicates.push(parse2(quote! {
34+
#components_ident: DelegateComponent<
35+
( #use_delegate_params ),
36+
Delegate = #delegate_ident,
37+
>
38+
})?);
39+
40+
let type_generics = provider_trait.generics.split_for_impl().1;
41+
42+
where_clause.predicates.push(parse2(quote! {
43+
#delegate_ident : #provider_trait_ident #type_generics
44+
})?);
45+
46+
generics
47+
};
48+
49+
let (_, type_generics, _) = provider_trait.generics.split_for_impl();
50+
51+
let trait_path: Path = parse2(quote!( #provider_trait_ident #type_generics ))?;
52+
53+
let mut impl_items: Vec<ImplItem> = Vec::new();
54+
55+
for trait_item in provider_trait.items.iter() {
56+
match trait_item {
57+
TraitItem::Fn(trait_fn) => {
58+
let impl_fn = derive_delegated_fn_impl(&trait_fn.sig, &quote!( #delegate_ident ))?;
59+
60+
impl_items.push(ImplItem::Fn(impl_fn))
61+
}
62+
TraitItem::Type(trait_type) => {
63+
let type_name = &trait_type.ident;
64+
65+
let type_generics = trait_type.generics.split_for_impl().1;
66+
67+
let impl_type = derive_delegate_type_impl(
68+
trait_type,
69+
parse2(quote!(
70+
#delegate_ident :: #type_name #type_generics
71+
))?,
72+
);
73+
74+
impl_items.push(ImplItem::Type(impl_type));
75+
}
76+
TraitItem::Const(trait_item_const) => {
77+
let const_ident = &trait_item_const.ident;
78+
let (_, type_generics, _) = trait_item_const.generics.split_for_impl();
79+
80+
let impl_expr = parse2(quote! {
81+
#delegate_ident :: #const_ident #type_generics
82+
})?;
83+
84+
let impl_item_const = ImplItemConst {
85+
attrs: trait_item_const.attrs.clone(),
86+
vis: Visibility::Inherited,
87+
defaultness: None,
88+
const_token: trait_item_const.const_token,
89+
ident: trait_item_const.ident.clone(),
90+
generics: trait_item_const.generics.clone(),
91+
colon_token: trait_item_const.colon_token,
92+
ty: trait_item_const.ty.clone(),
93+
eq_token: Eq(Span::call_site()),
94+
expr: impl_expr,
95+
semi_token: trait_item_const.semi_token,
96+
};
97+
98+
impl_items.push(ImplItem::Const(impl_item_const));
99+
}
100+
_ => {
101+
return Err(Error::new(
102+
trait_item.span(),
103+
format!("unsupported trait item: {trait_item:?}"),
104+
));
105+
}
106+
}
107+
}
108+
109+
let provider_type = parse2(quote!(#wrapper_ident < #components_ident >))?;
110+
111+
let item = ItemImpl {
112+
attrs: provider_trait.attrs.clone(),
113+
defaultness: None,
114+
unsafety: provider_trait.unsafety,
115+
impl_token: Impl::default(),
116+
generics,
117+
trait_: Some((None, trait_path, For::default())),
118+
self_ty: Box::new(provider_type),
119+
brace_token: Brace::default(),
120+
items: impl_items,
121+
};
122+
123+
Ok(item)
124+
}

0 commit comments

Comments
 (0)