Skip to content
2 changes: 1 addition & 1 deletion crates/cgp-core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use cgp_field::types::{
};
pub use cgp_macro::{
BuildField, CgpData, CgpRecord, CgpVariant, ExtractField, FromVariant, HasField, HasFields,
Product, Sum, Symbol, cgp_auto_getter, cgp_component, cgp_context, cgp_getter,
Product, Sum, Symbol, cgp_auto_getter, cgp_component, cgp_context, cgp_getter, cgp_impl,
cgp_new_provider, cgp_preset, cgp_provider, cgp_type, check_components,
delegate_and_check_components, delegate_components, product, re_export_imports, replace_with,
};
Expand Down
8 changes: 4 additions & 4 deletions crates/cgp-error-anyhow/src/impls/raise_anyhow_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use cgp_core::prelude::*;

pub struct RaiseAnyhowError;

#[cgp_provider]
impl<Context, E> ErrorRaiser<Context, E> for RaiseAnyhowError
#[cgp_impl(RaiseAnyhowError)]
impl<Context, E> ErrorRaiser<E> for Context
where
Context: HasErrorType<Error = Error>,
E: StdError + Send + Sync + 'static,
Expand All @@ -18,8 +18,8 @@ where
}
}

#[cgp_provider]
impl<Context, Detail> ErrorWrapper<Context, Detail> for RaiseAnyhowError
#[cgp_impl(RaiseAnyhowError)]
impl<Context, Detail> ErrorWrapper<Detail> for Context
where
Context: HasErrorType<Error = Error>,
Detail: Display + Send + Sync + 'static,
Expand Down
1 change: 1 addition & 0 deletions crates/cgp-macro-lib/src/derive_component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ mod use_context_impl;
mod use_delegate_impl;

pub use derive::*;
pub use replace_self_receiver::*;
pub use replace_self_type::*;
pub use snake_case::*;
11 changes: 8 additions & 3 deletions crates/cgp-macro-lib/src/derive_component/provider_trait.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use alloc::vec::Vec;

use quote::quote;
use quote::{ToTokens, quote};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Ident, ItemTrait, TraitItem, TypeParamBound, parse2};

use crate::derive_component::replace_self_receiver::replace_self_receiver;
use crate::derive_component::replace_self_receiver::replace_self_receiver_in_signature;
use crate::derive_component::replace_self_type::{
iter_parse_and_replace_self_type, parse_and_replace_self_type,
};
use crate::derive_component::to_snake_case_ident;
use crate::parse::parse_is_provider_params;

pub fn derive_provider_trait(
Expand Down Expand Up @@ -89,7 +90,11 @@ pub fn derive_provider_trait(
parse_and_replace_self_type(item, context_type, &local_assoc_types)?;

if let TraitItem::Fn(func) = &mut replaced_item {
replace_self_receiver(func, context_type);
replace_self_receiver_in_signature(
&mut func.sig,
&to_snake_case_ident(context_type),
context_type.to_token_stream(),
);
}

*item = replaced_item;
Expand Down
58 changes: 34 additions & 24 deletions crates/cgp-macro-lib/src/derive_component/replace_self_receiver.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
use proc_macro2::Ident;
use syn::{FnArg, TraitItemFn, parse_quote};
use proc_macro2::{Ident, TokenStream};
use syn::{FnArg, Receiver, Signature, parse_quote};

use crate::derive_component::snake_case::to_snake_case_ident;

pub fn replace_self_receiver(func: &mut TraitItemFn, replaced_type: &Ident) {
if let Some(arg) = func.sig.inputs.first_mut()
pub fn replace_self_receiver_in_signature(
sig: &mut Signature,
replaced_var: &Ident,
replaced_type: TokenStream,
) {
if let Some(arg) = sig.inputs.first_mut()
&& let FnArg::Receiver(receiver) = arg
{
let replaced_var = to_snake_case_ident(replaced_type);
*arg = replace_self_receiver(receiver, replaced_var, replaced_type);
}
}

match (&receiver.reference, &receiver.mutability) {
(None, None) => {
*arg = parse_quote!(#replaced_var : #replaced_type);
}
(Some((_and, None)), None) => {
*arg = parse_quote!(#replaced_var : & #replaced_type);
}
(Some((_and, Some(life))), None) => {
*arg = parse_quote!(#replaced_var : & #life #replaced_type);
}
(Some((_and, None)), Some(_mut)) => {
*arg = parse_quote!(#replaced_var : &mut #replaced_type);
}
(Some((_and, Some(life))), Some(_mut)) => {
*arg = parse_quote!(#replaced_var : & #life mut #replaced_type);
}
_ => {}
pub fn replace_self_receiver(
receiver: &mut Receiver,
replaced_var: &Ident,
replaced_type: TokenStream,
) -> FnArg {
match (&receiver.reference, &receiver.mutability) {
(None, None) => {
parse_quote!(#replaced_var : #replaced_type)
}
(Some((_and, None)), None) => {
parse_quote!(#replaced_var : & #replaced_type)
}
(Some((_and, Some(life))), None) => {
parse_quote!(#replaced_var : & #life #replaced_type)
}
(Some((_and, None)), Some(_mut)) => {
parse_quote!(#replaced_var : &mut #replaced_type)
}
(Some((_and, Some(life))), Some(_mut)) => {
parse_quote!(#replaced_var : & #life mut #replaced_type)
}
(None, Some(_mut)) => {
parse_quote!(#replaced_var : mut #replaced_type)
}
}
}
14 changes: 9 additions & 5 deletions crates/cgp-macro-lib/src/derive_component/replace_self_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ pub fn parse_and_replace_self_type<T>(
where
T: ToTokens + Parse,
{
let stream = replace_self_type(val.to_token_stream(), replaced_ident, local_assoc_types);
let stream = replace_self_type(
val.to_token_stream(),
replaced_ident.to_token_stream(),
local_assoc_types,
);
syn::parse2(stream)
}

pub fn replace_self_type(
stream: TokenStream,
replaced_ident: &Ident,
replaced_ident: TokenStream,
local_assoc_types: &Vec<Ident>,
) -> TokenStream {
let self_type = format_ident!("Self");
Expand All @@ -57,7 +61,7 @@ pub fn replace_self_type(
Some(TokenTree::Ident(assoc_type))
if local_assoc_types.contains(assoc_type) =>
{
ident
ident.to_token_stream()
}
_ => replaced_ident,
}
Expand All @@ -68,14 +72,14 @@ pub fn replace_self_type(
_ => replaced_ident,
};

result_stream.push(TokenTree::Ident(replaced));
result_stream.extend(replaced);
} else {
result_stream.push(TokenTree::Ident(ident));
}
}
TokenTree::Group(group) => {
let replaced_stream =
replace_self_type(group.stream(), replaced_ident, local_assoc_types);
replace_self_type(group.stream(), replaced_ident.clone(), local_assoc_types);
let replaced_group = Group::new(group.delimiter(), replaced_stream);

result_stream.push(TokenTree::Group(replaced_group));
Expand Down
5 changes: 4 additions & 1 deletion crates/cgp-macro-lib/src/derive_component/snake_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ pub fn to_snake_case_str(val: &str) -> String {
}

pub fn to_snake_case_ident(val: &Ident) -> Ident {
Ident::new(&to_snake_case_str(&val.to_string()), Span::call_site())
Ident::new(
&format!("__{}__", to_snake_case_str(&val.to_string())),
Span::call_site(),
)
}
4 changes: 2 additions & 2 deletions crates/cgp-macro-lib/src/derive_getter/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fn parse_receiver(context_ident: &Ident, arg: &FnArg) -> syn::Result<(ReceiverMo
Type::Reference(ty) => {
let receiver = parse2(replace_self_type(
ty.elem.to_token_stream(),
context_ident,
context_ident.to_token_stream(),
&Vec::new(),
))?;
Ok((ReceiverMode::Type(receiver), ty.mutability))
Expand All @@ -191,7 +191,7 @@ fn parse_return_type(context_type: &Ident, return_type: &ReturnType) -> syn::Res
match return_type {
ReturnType::Type(_, ty) => parse2(replace_self_type(
ty.to_token_stream(),
context_type,
context_type.to_token_stream(),
&Vec::new(),
)),
_ => Err(Error::new(
Expand Down
187 changes: 187 additions & 0 deletions crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use proc_macro2::{Group, Span, TokenStream, TokenTree};
use quote::{ToTokens, format_ident, quote};
use syn::parse::discouraged::Speculative;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::token::{Colon, For};
use syn::{Error, FnArg, Ident, ImplItem, ItemImpl, Type, parse2};

use crate::derive_component::{replace_self_receiver, replace_self_type, to_snake_case_ident};
use crate::derive_provider::{
derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct,
};
use crate::parse::SimpleType;

pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
let spec: ImplProviderSpec = parse2(attr)?;
let item_impl: ItemImpl = parse2(body)?;

let consumer_trait_path = &item_impl
.trait_
.as_ref()
.ok_or_else(|| Error::new(item_impl.span(), "expect impl trait to contain path"))?
.1;

let consumer_trait_path: SimpleType = parse2(consumer_trait_path.to_token_stream())?;

let provider_impl =
transform_impl_trait(&item_impl, &consumer_trait_path, &spec.provider_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<Type>,
}

impl Parse for ImplProviderSpec {
fn parse(input: ParseStream) -> syn::Result<Self> {
let new_struct = {
let fork = input.fork();
let new_ident: Option<Ident> = 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::<Option<Colon>>()? {
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,
) -> syn::Result<ItemImpl> {
let context_type = item_impl.self_ty.as_ref();

let context_var = if let Ok(ident) = parse2::<Ident>(context_type.to_token_stream()) {
to_snake_case_ident(&ident)
} else {
Ident::new("__context__", Span::call_site())
};

let local_assoc_types: Vec<Ident> = 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)
}

fn replace_self_var(stream: TokenStream, replaced_ident: &Ident) -> TokenStream {
let self_ident = format_ident!("self");

let mut result_stream: Vec<TokenTree> = Vec::new();

let token_iter = stream.into_iter();

for tree in token_iter {
match tree {
TokenTree::Ident(ident) => {
if ident == self_ident {
result_stream.push(TokenTree::Ident(replaced_ident.clone()));
} else {
result_stream.push(TokenTree::Ident(ident));
}
}
TokenTree::Group(group) => {
let replaced_stream = replace_self_var(group.stream(), replaced_ident);
let replaced_group = Group::new(group.delimiter(), replaced_stream);

result_stream.push(TokenTree::Group(replaced_group));
}
TokenTree::Punct(punct) => {
result_stream.push(TokenTree::Punct(punct));
}
TokenTree::Literal(lit) => result_stream.push(TokenTree::Literal(lit)),
}
}

result_stream.into_iter().collect()
}
Loading
Loading