cgp_macro_lib/entrypoints/
cgp_impl.rs1use proc_macro2::{Span, TokenStream};
2use quote::{ToTokens, quote};
3use syn::parse::discouraged::Speculative;
4use syn::parse::{Parse, ParseStream};
5use syn::token::{Colon, For};
6use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse_quote, parse2};
7
8use crate::derive_provider::{
9 derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct,
10};
11use crate::parse::SimpleType;
12use crate::replace_self::{
13 replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident,
14};
15
16pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
17 let spec: ImplProviderSpec = parse2(attr)?;
18 let mut item_impl: ItemImpl = parse2(body)?;
19
20 let provider_impl = match &item_impl.trait_ {
21 Some((_, path, _)) => {
22 let consumer_trait_path = parse2(path.to_token_stream())?;
23 let context_type = item_impl.self_ty.as_ref();
24 transform_impl_trait(
25 &item_impl,
26 &consumer_trait_path,
27 &spec.provider_type,
28 context_type,
29 )?
30 }
31 None => {
32 let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?;
33 let context_type = parse_quote! { __Context__ };
34
35 item_impl
36 .generics
37 .params
38 .insert(0, parse_quote! { __Context__ });
39
40 transform_impl_trait(
41 &item_impl,
42 &consumer_trait_path,
43 &spec.provider_type,
44 &context_type,
45 )?
46 }
47 };
48
49 let component_type = match &spec.component_type {
50 Some(component_type) => component_type.clone(),
51 None => derive_component_name_from_provider_impl(&provider_impl)?,
52 };
53
54 let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_type, &provider_impl)?;
55
56 let provider_struct = if spec.new_struct {
57 Some(derive_provider_struct(&provider_impl)?)
58 } else {
59 None
60 };
61
62 Ok(quote! {
63 #provider_struct
64
65 #provider_impl
66
67 #is_provider_for_impl
68 })
69}
70
71pub struct ImplProviderSpec {
72 pub new_struct: bool,
73 pub provider_type: Type,
74 pub component_type: Option<Type>,
75}
76
77impl Parse for ImplProviderSpec {
78 fn parse(input: ParseStream) -> syn::Result<Self> {
79 let new_struct = {
80 let fork = input.fork();
81 let new_ident: Option<Ident> = fork.parse().ok();
82 match new_ident {
83 Some(new_ident) if new_ident == "new" => {
84 input.advance_to(&fork);
85 true
86 }
87 _ => false,
88 }
89 };
90
91 let provider_type = input.parse()?;
92
93 let component_type = if let Some(_colon) = input.parse::<Option<Colon>>()? {
94 let component_type: Type = input.parse()?;
95 Some(component_type)
96 } else {
97 None
98 };
99
100 Ok(ImplProviderSpec {
101 new_struct,
102 provider_type,
103 component_type,
104 })
105 }
106}
107
108pub fn transform_impl_trait(
109 item_impl: &ItemImpl,
110 consumer_trait_path: &SimpleType,
111 provider_type: &Type,
112 context_type: &Type,
113) -> syn::Result<ItemImpl> {
114 let context_var = if let Ok(ident) = parse2::<Ident>(context_type.to_token_stream()) {
115 to_snake_case_ident(&ident)
116 } else {
117 Ident::new("__context__", Span::call_site())
118 };
119
120 let local_assoc_types: Vec<Ident> = item_impl
121 .items
122 .iter()
123 .filter_map(|item| {
124 if let ImplItem::Type(assoc_type) = item {
125 Some(assoc_type.ident.clone())
126 } else {
127 None
128 }
129 })
130 .collect();
131
132 let raw_out_impl = replace_self_type(
133 item_impl.to_token_stream(),
134 context_type.to_token_stream(),
135 &local_assoc_types,
136 );
137
138 let mut out_impl: ItemImpl = parse2(raw_out_impl)?;
139 out_impl.self_ty = Box::new(provider_type.clone());
140
141 let mut provider_trait_path: SimpleType = consumer_trait_path.clone();
142
143 match &mut provider_trait_path.generics {
144 Some(generics) => {
145 generics
146 .args
147 .insert(0, parse2(context_type.to_token_stream())?);
148 }
149 None => {
150 provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?);
151 }
152 }
153
154 out_impl.trait_ = Some((
155 None,
156 parse2(provider_trait_path.to_token_stream())?,
157 For(Span::call_site()),
158 ));
159
160 for item in out_impl.items.iter_mut() {
161 if let ImplItem::Fn(item_fn) = item
162 && let Some(arg) = item_fn.sig.inputs.first_mut()
163 && let FnArg::Receiver(receiver) = arg
164 {
165 *arg = replace_self_receiver(receiver, &context_var, context_type.to_token_stream());
166
167 let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var);
168 item_fn.block = parse2(replaced_block)?;
169 }
170 }
171
172 Ok(out_impl)
173}