Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e45d714
Rename and move ImplArgs
soareschen May 26, 2026
87844e7
Use keyword in ImplArgs
soareschen May 26, 2026
0b69872
Refactor replace_provider_in_generics
soareschen May 26, 2026
97e582e
Rename replace_self_var to replace_self_value
soareschen May 29, 2026
7758bac
Implement replace_self_value_in_block using VisitorMut
soareschen May 29, 2026
40a63e7
Implement visitor-based replace_self_type
soareschen May 29, 2026
d7e7646
Use ReplaceSelfTypeVisitor in all other places
soareschen May 29, 2026
bd55d6e
Use visitors in more places
soareschen May 29, 2026
968dfd0
Simplify self replacements
soareschen May 29, 2026
c44afd2
Move replace self constructs
soareschen May 29, 2026
0965d58
Add IdentWithTypeArgs
soareschen May 30, 2026
c9fb625
Use IdentWithTypeArgs for namespace
soareschen May 30, 2026
5914fa2
Replacing SimpleType with IdentWithTypeArgs
soareschen May 30, 2026
6628353
Replace SimpleType with IdentWitTypeArgs
soareschen May 30, 2026
01ea319
Move cgp_impl attributes
soareschen May 31, 2026
f60428c
More refactoring
soareschen May 31, 2026
5670604
Refactor prepend_to_block
soareschen May 31, 2026
965e873
Accept tag Type in derive_getter_constraints
soareschen May 31, 2026
d05ce70
Remove field_assoc_type param from derive_getter_constraint
soareschen May 31, 2026
6e1e124
Replace derive_getter_constraint with HasFieldBound
soareschen Jun 3, 2026
b7e4caf
Use fully qualified HasField
soareschen Jun 3, 2026
12f9b22
Add to_type_param_bounds method
soareschen Jun 3, 2026
5c906f3
Move parse_field_type
soareschen Jun 3, 2026
82d50bf
Move implicit functions
soareschen Jun 3, 2026
ddf3df9
Implement SubstituteAbstractType visitor
soareschen Jun 3, 2026
6cf76eb
Reorganize UseTypeAttribute constructs
soareschen Jun 3, 2026
7488e80
Implement transform_item_trait and transform_item_impl for UseTypeAtt…
soareschen Jun 3, 2026
f9068f8
Move FunctionAttributes
soareschen Jun 4, 2026
f7dc688
Change extract_implicit_args_from_impl_items into method
soareschen Jun 4, 2026
92f8550
Implement add_type_param_bounds for ImplicitArgFields
soareschen Jun 4, 2026
9ffdabb
Add AddTypeParamBounds trait
soareschen Jun 4, 2026
54f6521
Turn derive_provider_bounds into method
soareschen Jun 4, 2026
3f0f058
Implement add_type_param_bounds for UseProviderAttributes
soareschen Jun 4, 2026
a453400
Implement lower for ItemCgpImpl
soareschen Jun 4, 2026
6f8bef4
Compute consumer trait path and context type inside lowering
soareschen Jun 4, 2026
0b2a8fa
Implement to_raw_item_impl method
soareschen Jun 4, 2026
123d6f5
Draft implement ItemCgpProvider
soareschen Jun 4, 2026
d24b605
Implement ItemCgpProvider
soareschen Jun 4, 2026
786cf71
Implement lowering for ItemCgpImpl
soareschen Jun 6, 2026
389027f
Use ItemCgpProvider in derive_cgp_impl
soareschen Jun 6, 2026
21dcdc1
Fully migrate to new cgp_impl implementation
soareschen Jun 6, 2026
b2a1521
Implement IsProviderFor
soareschen Jun 6, 2026
e07a18b
Use IsProviderFor to implement derive_is_provider_for
soareschen Jun 6, 2026
274b88f
Rename to ItemIsProviderFor
soareschen Jun 6, 2026
c0d7cd2
Fix clippy
soareschen Jun 6, 2026
a2a48b6
Fix clippy
soareschen Jun 6, 2026
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
3 changes: 3 additions & 0 deletions crates/cgp-macro-core/src/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ export_constructs! {
Cons,
Chars,
Symbol,
Index,
PathCons,
RedirectLookup,
DelegateComponent,
IsProviderFor,
HasField,
HasFieldMut,
}
3 changes: 3 additions & 0 deletions crates/cgp-macro-core/src/functions/field/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod parse;

pub use parse::*;
108 changes: 108 additions & 0 deletions crates/cgp-macro-core/src/functions/field/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use quote::{ToTokens, quote};
use syn::spanned::Spanned;
use syn::token::Mut;
use syn::{
Error, GenericArgument, PathArguments, PathSegment, Type, TypePath, parse_quote, parse2,
};

use crate::types::getter::FieldMode;

pub fn parse_field_type(
return_type: &Type,
receiver_mut: &Option<Mut>,
) -> syn::Result<(Type, FieldMode)> {
match &return_type {
Type::Reference(type_ref) => {
if type_ref.mutability.is_some() && receiver_mut.is_none() {
return Err(Error::new(
type_ref.span(),
format!(
"&mut self is required for mutable field reference `{}`",
type_ref.to_token_stream()
),
));
}

if type_ref.elem.as_ref() == &parse_quote! { str } {
// Special case to handle &str as String field

let field_type: Type = parse_quote! { String };

Ok((field_type, FieldMode::Str))
} else if let (Type::Slice(slice), None) = (type_ref.elem.as_ref(), receiver_mut) {
let field_type = slice.elem.as_ref().clone();

Ok((field_type, FieldMode::Slice))
} else {
let field_type = type_ref.elem.as_ref().clone();

Ok((field_type, FieldMode::Reference))
}
}
Type::Path(type_path) => {
if let Some(field_type) = try_parse_option_ref(type_path) {
Ok((
parse2(quote! { Option< #field_type > })?,
FieldMode::OptionRef,
))
} else if let (Some(field_type), None) = (try_parse_mref(type_path), receiver_mut) {
Ok((field_type.clone(), FieldMode::MRef))
} else {
Ok((return_type.clone(), FieldMode::Copy))
}
}
_ => Err(Error::new(
return_type.span(),
"return type must be a reference",
)),
}
}

fn try_parse_option_ref(type_path: &TypePath) -> Option<&Type> {
let segment = parse_single_segment_type_path(type_path).ok()?;

if segment.ident == "Option"
&& let PathArguments::AngleBracketed(args) = &segment.arguments
{
let [arg] = Vec::from_iter(args.args.iter()).try_into().ok()?;

if let GenericArgument::Type(Type::Reference(type_ref)) = arg {
return Some(type_ref.elem.as_ref());
}
}

None
}

pub fn parse_single_segment_type_path(type_path: &TypePath) -> syn::Result<&PathSegment> {
let [segment]: [&PathSegment; 1] = type_path
.path
.segments
.iter()
.collect::<Vec<_>>()
.try_into()
.map_err(|_| {
Error::new(
type_path.span(),
"type path must contain exactly one path segment",
)
})?;

Ok(segment)
}

fn try_parse_mref(type_path: &TypePath) -> Option<&Type> {
let segment = parse_single_segment_type_path(type_path).ok()?;

if segment.ident == "MRef"
&& let PathArguments::AngleBracketed(args) = &segment.arguments
{
let [arg1, arg2] = Vec::from_iter(args.args.iter()).try_into().ok()?;

if let (GenericArgument::Lifetime(_), GenericArgument::Type(ty)) = (arg1, arg2) {
return Some(ty);
}
}

None
}
3 changes: 3 additions & 0 deletions crates/cgp-macro-core/src/functions/generics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod merge_generics;

pub use merge_generics::*;
3 changes: 3 additions & 0 deletions crates/cgp-macro-core/src/functions/implicits/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod parse;

pub use parse::*;
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ use syn::token::Comma;
use syn::visit::{self, Visit};
use syn::{Attribute, FnArg, Meta, Pat, PatIdent, PatType, Receiver};

use crate::cgp_fn::ImplicitArgField;
use crate::derive_getter::parse_field_type;
use crate::functions::parse_field_type;
use crate::types::implicits::{ImplicitArgField, ImplicitArgFields};

pub fn extract_and_parse_implicit_args(
args: &mut Punctuated<FnArg, Comma>,
) -> syn::Result<Vec<ImplicitArgField>> {
) -> syn::Result<ImplicitArgFields> {
let implicit_fn_args = extract_implicit_args(args);

if implicit_fn_args.is_empty() {
return Ok(Vec::new());
return Ok(ImplicitArgFields::default());
}

let Some(FnArg::Receiver(receiver)) = args.first() else {
Expand All @@ -38,7 +38,7 @@ pub fn extract_and_parse_implicit_args(
implicit_args.push(spec);
}

Ok(implicit_args)
Ok(ImplicitArgFields::new(implicit_args))
}

pub fn parse_implicit_arg(receiver: &Receiver, arg: &PatType) -> syn::Result<ImplicitArgField> {
Expand Down
10 changes: 8 additions & 2 deletions crates/cgp-macro-core/src/functions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
mod merge_generics;
mod field;
mod generics;
mod implicits;
mod snake_case;

pub use merge_generics::*;
pub use field::*;
pub use generics::*;
pub use implicits::*;
pub use snake_case::*;
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use alloc::string::{String, ToString};

use proc_macro2::Span;
use syn::Ident;

Expand Down
1 change: 1 addition & 0 deletions crates/cgp-macro-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod functions;
pub mod macros;
pub mod traits;
pub mod types;
pub mod visitors;
28 changes: 28 additions & 0 deletions crates/cgp-macro-core/src/traits/bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use syn::punctuated::Punctuated;
use syn::token::Plus;
use syn::{Generics, Type, TypeParamBound, parse_quote};

pub trait ToTypeParamBounds {
fn to_type_param_bounds(&self) -> syn::Result<Punctuated<TypeParamBound, Plus>>;
}

pub trait AddTypeParamBounds {
fn add_type_param_bounds(&self, self_type: &Type, generics: &mut Generics) -> syn::Result<()>;
}

impl<T> AddTypeParamBounds for T
where
T: ToTypeParamBounds,
{
fn add_type_param_bounds(&self, self_type: &Type, generics: &mut Generics) -> syn::Result<()> {
let bounds = self.to_type_param_bounds()?;

if !bounds.is_empty() {
generics.make_where_clause().predicates.push(parse_quote! {
#self_type: #bounds
});
}

Ok(())
}
}
18 changes: 18 additions & 0 deletions crates/cgp-macro-core/src/traits/keyword.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use syn::Ident;
use syn::parse::ParseBuffer;

use crate::types::keyword::Keyword;

pub trait IsKeyword {
const IDENT: &'static str;
}
Expand All @@ -20,3 +22,19 @@ impl<'a> PeekKeyword for ParseBuffer<'a> {
}
}
}

pub trait ParseOptionalKeyword {
fn parse_optional_keyword<K: IsKeyword>(&self) -> syn::Result<Option<Keyword<K>>>;
}

impl<'a> ParseOptionalKeyword for ParseBuffer<'a> {
fn parse_optional_keyword<K: IsKeyword>(&self) -> syn::Result<Option<Keyword<K>>> {
let keyword = if self.peek_keyword::<K>() {
Some(self.parse()?)
} else {
None
};

Ok(keyword)
}
}
2 changes: 2 additions & 0 deletions crates/cgp-macro-core/src/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod bounds;
mod keyword;
mod to_type;

pub use bounds::*;
pub use keyword::*;
pub use to_type::*;
72 changes: 72 additions & 0 deletions crates/cgp-macro-core/src/types/attributes/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Attribute, GenericParam, TypeParamBound, WherePredicate};

use crate::types::attributes::{
UseProviderAttribute, UseProviderAttributes, UseTypeAttribute, UseTypeAttributes,
};
use crate::types::ident::IdentWithTypeArgs;

#[derive(Default)]
pub struct FunctionAttributes {
pub extend: Vec<TypeParamBound>,
pub extend_where: Vec<WherePredicate>,
pub uses: Vec<IdentWithTypeArgs>,
pub use_type: UseTypeAttributes,
pub use_provider: UseProviderAttributes,
pub impl_generics: Vec<GenericParam>,
pub raw_attributes: Vec<Attribute>,
}

impl FunctionAttributes {
pub fn parse(attributes: Vec<Attribute>) -> syn::Result<Self> {
let mut parsed_attributes = FunctionAttributes::default();

for attribute in attributes.into_iter() {
if let Some(ident) = attribute.path().get_ident() {
if ident == "extend" {
let extend_bound = attribute
.parse_args_with(Punctuated::<TypeParamBound, Comma>::parse_terminated)?;

parsed_attributes.extend.extend(extend_bound);
} else if ident == "extend_where" {
let where_predicates = attribute
.parse_args_with(Punctuated::<WherePredicate, Comma>::parse_terminated)?;

parsed_attributes.extend_where.extend(where_predicates);
} else if ident == "uses" {
let uses = attribute.parse_args_with(
Punctuated::<IdentWithTypeArgs, Comma>::parse_terminated,
)?;

parsed_attributes.uses.extend(uses);
} else if ident == "use_type" {
let use_type = attribute
.parse_args_with(Punctuated::<UseTypeAttribute, Comma>::parse_terminated)?;

parsed_attributes.use_type.attributes.extend(use_type);
} else if ident == "use_provider" {
let use_provider = attribute.parse_args_with(
Punctuated::<UseProviderAttribute, Comma>::parse_terminated,
)?;

parsed_attributes
.use_provider
.attributes
.extend(use_provider);
} else if ident == "impl_generics" {
let impl_generics = attribute
.parse_args_with(Punctuated::<GenericParam, Comma>::parse_terminated)?;

parsed_attributes.impl_generics.extend(impl_generics);
} else {
parsed_attributes.raw_attributes.push(attribute);
}
} else {
parsed_attributes.raw_attributes.push(attribute);
}
}

Ok(parsed_attributes)
}
}
61 changes: 61 additions & 0 deletions crates/cgp-macro-core/src/types/attributes/impl_attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use syn::Attribute;
use syn::punctuated::Punctuated;
use syn::token::Comma;

use crate::types::attributes::{
UseProviderAttribute, UseProviderAttributes, UseTypeAttribute, UseTypeAttributes,
UsesAttributes,
};
use crate::types::ident::IdentWithTypeArgs;

#[derive(Default)]
pub struct ImplAttributes {
pub uses: UsesAttributes,
pub use_type: UseTypeAttributes,
pub use_provider: UseProviderAttributes,
pub raw_attributes: Vec<Attribute>,
}

impl ImplAttributes {
pub fn parse(attributes: &Vec<Attribute>) -> syn::Result<ImplAttributes> {
let mut parsed_attributes = ImplAttributes::default();

for attribute in attributes {
if let Some(ident) = attribute.path().get_ident() {
match ident.to_string().as_ref() {
"uses" => {
let uses = attribute.parse_args_with(
Punctuated::<IdentWithTypeArgs, Comma>::parse_terminated,
)?;

parsed_attributes.uses.imports.extend(uses);
}
"use_type" => {
let use_type = attribute.parse_args_with(
Punctuated::<UseTypeAttribute, Comma>::parse_terminated,
)?;

parsed_attributes.use_type.attributes.extend(use_type);
}
"use_provider" => {
let use_provider = attribute.parse_args_with(
Punctuated::<UseProviderAttribute, Comma>::parse_terminated,
)?;

parsed_attributes
.use_provider
.attributes
.extend(use_provider);
}
_ => {
parsed_attributes.raw_attributes.push(attribute.clone());
}
};
} else {
parsed_attributes.raw_attributes.push(attribute.clone());
}
}

Ok(parsed_attributes)
}
}
11 changes: 11 additions & 0 deletions crates/cgp-macro-core/src/types/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod function;
mod impl_attributes;
mod use_provider;
mod use_type;
mod uses;

pub use function::*;
pub use impl_attributes::*;
pub use use_provider::*;
pub use use_type::*;
pub use uses::*;
Loading
Loading