Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions crates/cgp-macro-lib/src/cgp_fn/apply_type.rs
Original file line number Diff line number Diff line change
@@ -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<ItemImpl> {
let mut item_impl: ItemImpl = parse2(substitute_abstract_type(
&quote! { Self },
use_type_specs,
item_impl.to_token_stream(),
))?;

let bounds = derive_use_type_trait_bounds(&quote! { Self }, use_type_specs)?;
let bounds = Punctuated::<TypeParamBound, Plus>::from_iter(bounds);

item_impl
.generics
.make_where_clause()
.predicates
.push(parse2(quote! {
Self: #bounds
})?);

Ok(item_impl)
}
30 changes: 30 additions & 0 deletions crates/cgp-macro-lib/src/cgp_fn/bounds.rs
Original file line number Diff line number Diff line change
@@ -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<Punctuated<TypeParamBound, Plus>> {
let mut constraints: Punctuated<TypeParamBound, Plus> = 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)
}
14 changes: 2 additions & 12 deletions crates/cgp-macro-lib/src/cgp_fn/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenStream> {
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)?;

Expand Down
45 changes: 9 additions & 36 deletions crates/cgp-macro-lib/src/cgp_fn/item_impl.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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(
&quote! { Self },
&attributes.use_type,
item_impl.to_token_stream(),
))?;

let bounds = derive_use_type_trait_bounds(&quote! { Self }, &attributes.use_type)?;
let bounds = Punctuated::<TypeParamBound, Plus>::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)
Expand Down
5 changes: 5 additions & 0 deletions crates/cgp-macro-lib/src/cgp_fn/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod apply_type;
mod attributes;
mod bounds;
mod derive;
mod fn_body;
mod item_impl;
Expand All @@ -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::*;
Expand Down
14 changes: 12 additions & 2 deletions crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FnArg, Comma>,
) -> syn::Result<Vec<ImplicitArgField>> {
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,
Expand Down
1 change: 1 addition & 0 deletions crates/cgp-macro-lib/src/cgp_fn/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
40 changes: 40 additions & 0 deletions crates/cgp-macro-lib/src/cgp_impl/attributes.rs
Original file line number Diff line number Diff line change
@@ -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<Attribute>) -> syn::Result<ImplAttributes> {
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::<SimpleType, Comma>::parse_terminated)?;
parsed_attributes.uses.extend(uses);
} else if ident == "use_type" {
let use_type = attribute
.parse_args_with(Punctuated::<UseTypeSpec, Comma>::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<SimpleType>,
pub use_type: Vec<UseTypeSpec>,
}
73 changes: 73 additions & 0 deletions crates/cgp-macro-lib/src/cgp_impl/derive.rs
Original file line number Diff line number Diff line change
@@ -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<TokenStream> {
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<TypeParamBound, Plus> = 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
})
}
24 changes: 24 additions & 0 deletions crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<ImplicitArgField>> {
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)
}
11 changes: 11 additions & 0 deletions crates/cgp-macro-lib/src/cgp_impl/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
38 changes: 38 additions & 0 deletions crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs
Original file line number Diff line number Diff line change
@@ -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<ItemImpl> {
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,
)
}
}
}
Loading
Loading