diff --git a/crates/cgp-macro-core/src/exports.rs b/crates/cgp-macro-core/src/exports.rs index 36841735..292ecb57 100644 --- a/crates/cgp-macro-core/src/exports.rs +++ b/crates/cgp-macro-core/src/exports.rs @@ -5,8 +5,11 @@ export_constructs! { Cons, Chars, Symbol, + Index, PathCons, RedirectLookup, DelegateComponent, IsProviderFor, + HasField, + HasFieldMut, } diff --git a/crates/cgp-macro-core/src/functions/field/mod.rs b/crates/cgp-macro-core/src/functions/field/mod.rs new file mode 100644 index 00000000..4f42021d --- /dev/null +++ b/crates/cgp-macro-core/src/functions/field/mod.rs @@ -0,0 +1,3 @@ +mod parse; + +pub use parse::*; diff --git a/crates/cgp-macro-core/src/functions/field/parse.rs b/crates/cgp-macro-core/src/functions/field/parse.rs new file mode 100644 index 00000000..3064918d --- /dev/null +++ b/crates/cgp-macro-core/src/functions/field/parse.rs @@ -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, +) -> 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::>() + .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 +} diff --git a/crates/cgp-macro-core/src/functions/merge_generics.rs b/crates/cgp-macro-core/src/functions/generics/merge_generics.rs similarity index 100% rename from crates/cgp-macro-core/src/functions/merge_generics.rs rename to crates/cgp-macro-core/src/functions/generics/merge_generics.rs diff --git a/crates/cgp-macro-core/src/functions/generics/mod.rs b/crates/cgp-macro-core/src/functions/generics/mod.rs new file mode 100644 index 00000000..f5b39fc2 --- /dev/null +++ b/crates/cgp-macro-core/src/functions/generics/mod.rs @@ -0,0 +1,3 @@ +mod merge_generics; + +pub use merge_generics::*; diff --git a/crates/cgp-macro-core/src/functions/implicits/mod.rs b/crates/cgp-macro-core/src/functions/implicits/mod.rs new file mode 100644 index 00000000..4f42021d --- /dev/null +++ b/crates/cgp-macro-core/src/functions/implicits/mod.rs @@ -0,0 +1,3 @@ +mod parse; + +pub use parse::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs b/crates/cgp-macro-core/src/functions/implicits/parse.rs similarity index 93% rename from crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs rename to crates/cgp-macro-core/src/functions/implicits/parse.rs index e195ba9b..d62b3e25 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs +++ b/crates/cgp-macro-core/src/functions/implicits/parse.rs @@ -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, -) -> syn::Result> { +) -> syn::Result { 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 { @@ -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 { diff --git a/crates/cgp-macro-core/src/functions/mod.rs b/crates/cgp-macro-core/src/functions/mod.rs index f5b39fc2..1de543ee 100644 --- a/crates/cgp-macro-core/src/functions/mod.rs +++ b/crates/cgp-macro-core/src/functions/mod.rs @@ -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::*; diff --git a/crates/cgp-macro-lib/src/replace_self/snake_case.rs b/crates/cgp-macro-core/src/functions/snake_case.rs similarity index 94% rename from crates/cgp-macro-lib/src/replace_self/snake_case.rs rename to crates/cgp-macro-core/src/functions/snake_case.rs index 9a0e62ef..19b2b3d7 100644 --- a/crates/cgp-macro-lib/src/replace_self/snake_case.rs +++ b/crates/cgp-macro-core/src/functions/snake_case.rs @@ -1,5 +1,3 @@ -use alloc::string::{String, ToString}; - use proc_macro2::Span; use syn::Ident; diff --git a/crates/cgp-macro-core/src/lib.rs b/crates/cgp-macro-core/src/lib.rs index a6dbd9e7..aa5aeac3 100644 --- a/crates/cgp-macro-core/src/lib.rs +++ b/crates/cgp-macro-core/src/lib.rs @@ -5,3 +5,4 @@ pub mod functions; pub mod macros; pub mod traits; pub mod types; +pub mod visitors; diff --git a/crates/cgp-macro-core/src/traits/bounds.rs b/crates/cgp-macro-core/src/traits/bounds.rs new file mode 100644 index 00000000..4f3d1882 --- /dev/null +++ b/crates/cgp-macro-core/src/traits/bounds.rs @@ -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>; +} + +pub trait AddTypeParamBounds { + fn add_type_param_bounds(&self, self_type: &Type, generics: &mut Generics) -> syn::Result<()>; +} + +impl 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(()) + } +} diff --git a/crates/cgp-macro-core/src/traits/keyword.rs b/crates/cgp-macro-core/src/traits/keyword.rs index 125c7937..c7d3ef6e 100644 --- a/crates/cgp-macro-core/src/traits/keyword.rs +++ b/crates/cgp-macro-core/src/traits/keyword.rs @@ -1,6 +1,8 @@ use syn::Ident; use syn::parse::ParseBuffer; +use crate::types::keyword::Keyword; + pub trait IsKeyword { const IDENT: &'static str; } @@ -20,3 +22,19 @@ impl<'a> PeekKeyword for ParseBuffer<'a> { } } } + +pub trait ParseOptionalKeyword { + fn parse_optional_keyword(&self) -> syn::Result>>; +} + +impl<'a> ParseOptionalKeyword for ParseBuffer<'a> { + fn parse_optional_keyword(&self) -> syn::Result>> { + let keyword = if self.peek_keyword::() { + Some(self.parse()?) + } else { + None + }; + + Ok(keyword) + } +} diff --git a/crates/cgp-macro-core/src/traits/mod.rs b/crates/cgp-macro-core/src/traits/mod.rs index 9484894c..577220eb 100644 --- a/crates/cgp-macro-core/src/traits/mod.rs +++ b/crates/cgp-macro-core/src/traits/mod.rs @@ -1,5 +1,7 @@ +mod bounds; mod keyword; mod to_type; +pub use bounds::*; pub use keyword::*; pub use to_type::*; diff --git a/crates/cgp-macro-core/src/types/attributes/function.rs b/crates/cgp-macro-core/src/types/attributes/function.rs new file mode 100644 index 00000000..3e97e52e --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/function.rs @@ -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, + pub extend_where: Vec, + pub uses: Vec, + pub use_type: UseTypeAttributes, + pub use_provider: UseProviderAttributes, + pub impl_generics: Vec, + pub raw_attributes: Vec, +} + +impl FunctionAttributes { + pub fn parse(attributes: Vec) -> syn::Result { + 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::::parse_terminated)?; + + parsed_attributes.extend.extend(extend_bound); + } else if ident == "extend_where" { + let where_predicates = attribute + .parse_args_with(Punctuated::::parse_terminated)?; + + parsed_attributes.extend_where.extend(where_predicates); + } else if ident == "uses" { + let uses = attribute.parse_args_with( + Punctuated::::parse_terminated, + )?; + + parsed_attributes.uses.extend(uses); + } else if ident == "use_type" { + let use_type = attribute + .parse_args_with(Punctuated::::parse_terminated)?; + + parsed_attributes.use_type.attributes.extend(use_type); + } else if ident == "use_provider" { + let use_provider = attribute.parse_args_with( + Punctuated::::parse_terminated, + )?; + + parsed_attributes + .use_provider + .attributes + .extend(use_provider); + } else if ident == "impl_generics" { + let impl_generics = attribute + .parse_args_with(Punctuated::::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) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/impl_attributes.rs b/crates/cgp-macro-core/src/types/attributes/impl_attributes.rs new file mode 100644 index 00000000..cea9184b --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/impl_attributes.rs @@ -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, +} + +impl ImplAttributes { + pub fn parse(attributes: &Vec) -> syn::Result { + 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::::parse_terminated, + )?; + + parsed_attributes.uses.imports.extend(uses); + } + "use_type" => { + let use_type = attribute.parse_args_with( + Punctuated::::parse_terminated, + )?; + + parsed_attributes.use_type.attributes.extend(use_type); + } + "use_provider" => { + let use_provider = attribute.parse_args_with( + Punctuated::::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) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/mod.rs b/crates/cgp-macro-core/src/types/attributes/mod.rs new file mode 100644 index 00000000..72930efc --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/mod.rs @@ -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::*; diff --git a/crates/cgp-macro-core/src/types/attributes/use_provider/attribute.rs b/crates/cgp-macro-core/src/types/attributes/use_provider/attribute.rs new file mode 100644 index 00000000..5865f471 --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_provider/attribute.rs @@ -0,0 +1,62 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Colon, Plus}; +use syn::{Type, TypeParamBound, WherePredicate, parse_quote}; + +use crate::types::ident::IdentWithTypeArgs; + +pub struct UseProviderAttribute { + pub context_type: Type, + pub provider_type: Type, + pub colon: Colon, + pub provider_trait_bounds: Punctuated, +} + +impl UseProviderAttribute { + pub fn to_type_param_bounds( + &self, + context_type: &Type, + ) -> syn::Result> { + let mut bounds = Punctuated::::new(); + + for bound in &self.provider_trait_bounds { + let mut bound = bound.clone(); + bound + .type_args + .make_args() + .insert(0, parse_quote!(#context_type)); + + bounds.push(parse_quote!(#bound)); + } + + Ok(bounds) + } + + pub fn to_provider_bounds(&self, context_type: &Type) -> syn::Result { + let provider_type = &self.provider_type; + let bounds = self.to_type_param_bounds(context_type)?; + + let predicate = parse_quote! { + #provider_type: #bounds + }; + + Ok(predicate) + } +} + +impl Parse for UseProviderAttribute { + fn parse(input: ParseStream) -> syn::Result { + let context_type = parse_quote!(Self); + let provider_type = input.parse()?; + + let colon: Colon = input.parse()?; + let provider_trait_bounds = Punctuated::parse_terminated(input)?; + + Ok(Self { + context_type, + provider_type, + colon, + provider_trait_bounds, + }) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/use_provider/attributes.rs b/crates/cgp-macro-core/src/types/attributes/use_provider/attributes.rs new file mode 100644 index 00000000..dc4ae554 --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_provider/attributes.rs @@ -0,0 +1,26 @@ +use syn::{Generics, Type}; + +use crate::traits::AddTypeParamBounds; +use crate::types::attributes::UseProviderAttribute; + +#[derive(Default)] +pub struct UseProviderAttributes { + pub attributes: Vec, +} + +impl AddTypeParamBounds for UseProviderAttributes { + fn add_type_param_bounds(&self, self_type: &Type, generics: &mut Generics) -> syn::Result<()> { + if self.attributes.is_empty() { + return Ok(()); + } + + let where_clause = generics.make_where_clause(); + + for use_provider in &self.attributes { + let predicate = use_provider.to_provider_bounds(self_type)?; + where_clause.predicates.push(predicate); + } + + Ok(()) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/use_provider/mod.rs b/crates/cgp-macro-core/src/types/attributes/use_provider/mod.rs new file mode 100644 index 00000000..ea33ebcb --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_provider/mod.rs @@ -0,0 +1,5 @@ +mod attribute; +mod attributes; + +pub use attribute::*; +pub use attributes::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/use_type.rs b/crates/cgp-macro-core/src/types/attributes/use_type/attribute.rs similarity index 58% rename from crates/cgp-macro-lib/src/cgp_fn/use_type.rs rename to crates/cgp-macro-core/src/types/attributes/use_type/attribute.rs index f35c38fb..735aa0ec 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/use_type.rs +++ b/crates/cgp-macro-core/src/types/attributes/use_type/attribute.rs @@ -1,23 +1,17 @@ -use quote::ToTokens; use syn::parse::{Parse, ParseStream}; -use syn::token::{As, At, Brace, Colon, Comma, Eq, Gt, Lt}; -use syn::{Ident, Type, braced, parse_quote, parse2}; +use syn::token::{At, Brace, Colon, Comma, Gt, Lt}; +use syn::{Ident, Type, braced, parse_quote}; -use crate::parse::SimpleType; +use crate::types::attributes::UseTypeIdent; +use crate::types::ident::IdentWithTypeArgs; -pub struct UseTypeSpec { +pub struct UseTypeAttribute { pub context_type: Type, - pub trait_path: SimpleType, + pub trait_path: IdentWithTypeArgs, pub type_idents: Vec, } -pub struct UseTypeIdent { - pub type_ident: Ident, - pub as_alias: Option, - pub equals: Option, -} - -impl UseTypeSpec { +impl UseTypeAttribute { pub fn replace_ident(&self, ident: &Ident) -> Option { for type_ident in &self.type_idents { if type_ident.alias_ident() == ident { @@ -31,21 +25,14 @@ impl UseTypeSpec { } } -impl UseTypeIdent { - pub fn alias_ident(&self) -> &Ident { - self.as_alias.as_ref().unwrap_or(&self.type_ident) - } -} - -impl Parse for UseTypeSpec { +impl Parse for UseTypeAttribute { fn parse(input: ParseStream) -> syn::Result { let body; let (context_type, body) = if input.peek(At) { let _: At = input.parse()?; - let context_type: SimpleType = input.parse()?; - let context_type = parse2(context_type.into_token_stream())?; + let context_type: Type = input.parse::()?.into(); let _: Colon = input.parse()?; let _: Colon = input.parse()?; @@ -62,15 +49,12 @@ impl Parse for UseTypeSpec { let trait_path = if body.peek(Lt) { let _: Lt = body.parse()?; - let trait_path: SimpleType = body.parse()?; + let trait_path: IdentWithTypeArgs = body.parse()?; let _: Gt = body.parse()?; trait_path } else { let name: Ident = body.parse()?; - SimpleType { - name, - generics: None, - } + name.into() }; let _: Colon = body.parse()?; @@ -99,29 +83,3 @@ impl Parse for UseTypeSpec { }) } } - -impl Parse for UseTypeIdent { - fn parse(input: ParseStream) -> syn::Result { - let type_ident: Ident = input.parse()?; - - let as_alias = if input.peek(As) { - let _: As = input.parse()?; - Some(input.parse()?) - } else { - None - }; - - let equals = if input.peek(Eq) { - let _: Eq = input.parse()?; - Some(input.parse()?) - } else { - None - }; - - Ok(Self { - type_ident, - as_alias, - equals, - }) - } -} diff --git a/crates/cgp-macro-core/src/types/attributes/use_type/attributes.rs b/crates/cgp-macro-core/src/types/attributes/use_type/attributes.rs new file mode 100644 index 00000000..34cef7a4 --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_type/attributes.rs @@ -0,0 +1,64 @@ +use quote::ToTokens; +use syn::visit_mut::VisitMut; +use syn::{ItemImpl, ItemTrait, parse_quote, parse2}; + +use crate::types::attributes::UseTypeAttribute; +use crate::types::attributes::use_type::type_predicates::derive_use_type_predicates; +use crate::visitors::SubstituteAbstractType; + +#[derive(Default)] +pub struct UseTypeAttributes { + pub attributes: Vec, +} + +impl UseTypeAttributes { + pub fn substitute_abstract_types_in_item_trait(&self, item_trait: &mut ItemTrait) { + for type_spec in self.attributes.iter().rev() { + SubstituteAbstractType { type_spec }.visit_item_trait_mut(item_trait); + } + } + + pub fn substitute_abstract_types_in_item_impl(&self, item_impl: &mut ItemImpl) { + for type_spec in self.attributes.iter().rev() { + SubstituteAbstractType { type_spec }.visit_item_impl_mut(item_impl); + } + } + + pub fn transform_item_trait(&self, item_trait: &mut ItemTrait) -> syn::Result<()> { + if self.attributes.is_empty() { + return Ok(()); + } + + self.substitute_abstract_types_in_item_trait(item_trait); + + for use_type in self.attributes.iter() { + if use_type.context_type != parse_quote! { Self } { + continue; + } + + item_trait + .supertraits + .push(parse2(use_type.trait_path.to_token_stream())?); + } + + Ok(()) + } + + pub fn transform_item_impl(&self, item_impl: &mut ItemImpl) -> syn::Result<()> { + if self.attributes.is_empty() { + return Ok(()); + } + + self.substitute_abstract_types_in_item_impl(item_impl); + + let predicates = derive_use_type_predicates(&self.attributes)?; + + item_impl + .generics + .make_where_clause() + .predicates + .extend(predicates); + + Ok(()) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/use_type/ident.rs b/crates/cgp-macro-core/src/types/attributes/use_type/ident.rs new file mode 100644 index 00000000..cc3edaf2 --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_type/ident.rs @@ -0,0 +1,41 @@ +use syn::parse::{Parse, ParseStream}; +use syn::token::{As, Eq}; +use syn::{Ident, Type}; + +pub struct UseTypeIdent { + pub type_ident: Ident, + pub as_alias: Option, + pub equals: Option, +} + +impl UseTypeIdent { + pub fn alias_ident(&self) -> &Ident { + self.as_alias.as_ref().unwrap_or(&self.type_ident) + } +} + +impl Parse for UseTypeIdent { + fn parse(input: ParseStream) -> syn::Result { + let type_ident: Ident = input.parse()?; + + let as_alias = if input.peek(As) { + let _: As = input.parse()?; + Some(input.parse()?) + } else { + None + }; + + let equals = if input.peek(Eq) { + let _: Eq = input.parse()?; + Some(input.parse()?) + } else { + None + }; + + Ok(Self { + type_ident, + as_alias, + equals, + }) + } +} diff --git a/crates/cgp-macro-core/src/types/attributes/use_type/mod.rs b/crates/cgp-macro-core/src/types/attributes/use_type/mod.rs new file mode 100644 index 00000000..be818cb8 --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/use_type/mod.rs @@ -0,0 +1,8 @@ +mod attribute; +mod attributes; +mod ident; +mod type_predicates; + +pub use attribute::*; +pub use attributes::*; +pub use ident::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/type_predicates.rs b/crates/cgp-macro-core/src/types/attributes/use_type/type_predicates.rs similarity index 89% rename from crates/cgp-macro-lib/src/cgp_fn/type_predicates.rs rename to crates/cgp-macro-core/src/types/attributes/use_type/type_predicates.rs index 5dd9c7e7..a10f868e 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/type_predicates.rs +++ b/crates/cgp-macro-core/src/types/attributes/use_type/type_predicates.rs @@ -4,9 +4,9 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Ident, Type, WherePredicate, parse_quote, parse2}; -use crate::cgp_fn::{UseTypeIdent, UseTypeSpec}; +use crate::types::attributes::{UseTypeAttribute, UseTypeIdent}; -pub fn derive_use_type_predicates(specs: &[UseTypeSpec]) -> syn::Result> { +pub fn derive_use_type_predicates(specs: &[UseTypeAttribute]) -> syn::Result> { let mut predicates = Vec::new(); for use_type in specs.iter() { @@ -43,7 +43,7 @@ pub fn derive_use_type_predicates(specs: &[UseTypeSpec]) -> syn::Result syn::Result> { +fn find_type_alias(specs: &[UseTypeAttribute], context_type: &Type) -> syn::Result> { let Ok(context_ident) = parse2::(context_type.to_token_stream()) else { return Ok(None); }; @@ -67,9 +67,9 @@ fn find_type_alias(specs: &[UseTypeSpec], context_type: &Type) -> syn::Result syn::Result> { let mut equalities = Vec::new(); @@ -86,8 +86,8 @@ pub fn find_type_equalities( fn forbid_same_alias( current_ident: &UseTypeIdent, - current_spec: &UseTypeSpec, - specs: &[UseTypeSpec], + current_spec: &UseTypeAttribute, + specs: &[UseTypeAttribute], ) -> syn::Result<()> { for spec in specs.iter() { if core::ptr::eq(spec, current_spec) { @@ -110,8 +110,8 @@ fn forbid_same_alias( fn find_type_equality( current_ident: &UseTypeIdent, - current_spec: &UseTypeSpec, - specs: &[UseTypeSpec], + current_spec: &UseTypeAttribute, + specs: &[UseTypeAttribute], ) -> syn::Result> { if let Some(equal_target) = current_ident.equals.clone() { for spec in specs.iter() { diff --git a/crates/cgp-macro-core/src/types/attributes/uses.rs b/crates/cgp-macro-core/src/types/attributes/uses.rs new file mode 100644 index 00000000..cc088b7d --- /dev/null +++ b/crates/cgp-macro-core/src/types/attributes/uses.rs @@ -0,0 +1,23 @@ +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{TypeParamBound, parse_quote}; + +use crate::traits::ToTypeParamBounds; +use crate::types::ident::IdentWithTypeArgs; + +#[derive(Default)] +pub struct UsesAttributes { + pub imports: Vec, +} + +impl ToTypeParamBounds for UsesAttributes { + fn to_type_param_bounds(&self) -> syn::Result> { + let mut bounds: Punctuated = Punctuated::default(); + + for import in &self.imports { + bounds.push(parse_quote! { #import }); + } + + Ok(bounds) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_impl/args.rs b/crates/cgp-macro-core/src/types/cgp_impl/args.rs new file mode 100644 index 00000000..89427b21 --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_impl/args.rs @@ -0,0 +1,35 @@ +use syn::Type; +use syn::parse::{Parse, ParseStream}; +use syn::token::Colon; + +use crate::traits::ParseOptionalKeyword; +use crate::types::keyword::Keyword; +use crate::types::keywords::New; + +#[derive(Clone)] +pub struct ImplArgs { + pub new: Option>, + pub provider_type: Type, + pub component_type: Option, +} + +impl Parse for ImplArgs { + fn parse(input: ParseStream) -> syn::Result { + let new = input.parse_optional_keyword()?; + + let provider_type = input.parse()?; + + let component_type = if let Some(_colon) = input.parse::>()? { + let component_type: Type = input.parse()?; + Some(component_type) + } else { + None + }; + + Ok(ImplArgs { + new, + provider_type, + component_type, + }) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_impl/item.rs b/crates/cgp-macro-core/src/types/cgp_impl/item.rs new file mode 100644 index 00000000..5d6a83fe --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_impl/item.rs @@ -0,0 +1,60 @@ +use quote::ToTokens; +use syn::{ItemImpl, Type, parse_quote, parse2}; + +use crate::traits::AddTypeParamBounds; +use crate::types::attributes::ImplAttributes; +use crate::types::cgp_impl::{ImplArgs, LoweredCgpImpl}; +use crate::types::implicits::ImplicitArgFields; + +pub struct ItemCgpImpl { + pub args: ImplArgs, + pub item_impl: ItemImpl, +} + +impl ItemCgpImpl { + pub fn lower(&self) -> syn::Result { + let mut item_impl = self.item_impl.clone(); + + let attributes = ImplAttributes::parse(&item_impl.attrs)?; + item_impl.attrs = attributes.raw_attributes; + + let self_type: Type = parse_quote!(Self); + + let implicit_args = ImplicitArgFields::extract_from_impl_items(&mut item_impl.items)?; + implicit_args.add_type_param_bounds(&self_type, &mut item_impl.generics)?; + attributes + .uses + .add_type_param_bounds(&self_type, &mut item_impl.generics)?; + + attributes.use_type.transform_item_impl(&mut item_impl)?; + attributes + .use_provider + .add_type_param_bounds(&self_type, &mut item_impl.generics)?; + + let (consumer_trait_path, context_type) = match &item_impl.trait_ { + Some((_, path, _)) => { + let consumer_trait_path = parse2(path.to_token_stream())?; + let context_type = item_impl.self_ty.as_ref().clone(); + (consumer_trait_path, 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_type }); + + (consumer_trait_path, context_type) + } + }; + + Ok(LoweredCgpImpl { + args: self.args.clone(), + item_impl, + context_type, + consumer_trait_path, + }) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_impl/lowered.rs b/crates/cgp-macro-core/src/types/cgp_impl/lowered.rs new file mode 100644 index 00000000..bb1cf48e --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_impl/lowered.rs @@ -0,0 +1,113 @@ +use proc_macro2::Span; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::token::For; +use syn::visit_mut::VisitMut; +use syn::{Error, Ident, ImplItem, ItemImpl, Type, parse_quote, parse2}; + +use crate::functions::to_snake_case_ident; +use crate::types::cgp_impl::{CgpProviderOrBareImpl, ImplArgs}; +use crate::types::cgp_provider::{ItemCgpProvider, ProviderArgs}; +use crate::types::ident::IdentWithTypeArgs; +use crate::visitors::{ + ReplaceSelfReceiverVisitor, ReplaceSelfTypeVisitor, ReplaceSelfValueVisitor, +}; + +pub struct LoweredCgpImpl { + pub args: ImplArgs, + pub item_impl: ItemImpl, + pub context_type: Type, + pub consumer_trait_path: IdentWithTypeArgs, +} + +impl LoweredCgpImpl { + pub fn lower(&self) -> syn::Result { + if self.args.provider_type == parse_quote!(Self) { + if self.item_impl.trait_.is_none() { + return Err(Error::new( + self.item_impl.span(), + "Expected context type to be specified", + )); + } + + Ok(CgpProviderOrBareImpl::Bare(Box::new( + self.item_impl.clone(), + ))) + } else { + let provider_impl = self.to_raw_item_impl()?; + + let item_cgp_provider = ItemCgpProvider { + args: ProviderArgs { + new: self.args.new.clone(), + component_type: self.args.component_type.clone(), + }, + item_impl: provider_impl, + }; + + let lowered = item_cgp_provider.lower()?; + + Ok(CgpProviderOrBareImpl::Provider(Box::new(lowered))) + } + } + + pub fn to_raw_item_impl(&self) -> syn::Result { + let item_impl = &self.item_impl; + let context_type = &self.context_type; + let consumer_trait_path = &self.consumer_trait_path; + let provider_type = &self.args.provider_type; + + let context_ident = if let Ok(ident) = parse2::(context_type.to_token_stream()) { + to_snake_case_ident(&ident) + } else { + Ident::new("__context__", Span::call_site()) + }; + + let local_assoc_types: Vec = item_impl + .items + .iter() + .filter_map(|item| { + if let ImplItem::Type(assoc_type) = item { + Some(assoc_type.ident.clone()) + } else { + None + } + }) + .collect(); + + let mut out_impl = item_impl.clone(); + + out_impl.self_ty = Box::new(provider_type.clone()); + + let mut provider_trait_path = consumer_trait_path.clone(); + + provider_trait_path + .type_args + .make_args() + .insert(0, parse_quote!(#context_type)); + + out_impl.trait_ = Some(( + None, + parse2(provider_trait_path.to_token_stream())?, + For(Span::call_site()), + )); + + ReplaceSelfTypeVisitor { + replaced_type: context_type, + skip_assoc_types: &local_assoc_types, + } + .visit_item_impl_mut(&mut out_impl); + + ReplaceSelfReceiverVisitor { + replaced_ident: &context_ident, + replaced_type: context_type, + } + .visit_item_impl_mut(&mut out_impl); + + ReplaceSelfValueVisitor { + replaced_ident: &context_ident, + } + .visit_item_impl_mut(&mut out_impl); + + Ok(out_impl) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_impl/mod.rs b/crates/cgp-macro-core/src/types/cgp_impl/mod.rs new file mode 100644 index 00000000..23c3960e --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_impl/mod.rs @@ -0,0 +1,9 @@ +mod args; +mod item; +mod lowered; +mod provider_or_bare; + +pub use args::*; +pub use item::*; +pub use lowered::*; +pub use provider_or_bare::*; diff --git a/crates/cgp-macro-core/src/types/cgp_impl/provider_or_bare.rs b/crates/cgp-macro-core/src/types/cgp_impl/provider_or_bare.rs new file mode 100644 index 00000000..34b38b80 --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_impl/provider_or_bare.rs @@ -0,0 +1,21 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::ItemImpl; + +use crate::types::cgp_provider::LoweredCgpProvider; + +pub enum CgpProviderOrBareImpl { + Bare(Box), + Provider(Box), +} + +impl ToTokens for CgpProviderOrBareImpl { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + CgpProviderOrBareImpl::Bare(item_impl) => item_impl.to_tokens(tokens), + CgpProviderOrBareImpl::Provider(item_cgp_provider) => { + item_cgp_provider.to_tokens(tokens) + } + } + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_provider/args.rs b/crates/cgp-macro-core/src/types/cgp_provider/args.rs new file mode 100644 index 00000000..7bb08435 --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_provider/args.rs @@ -0,0 +1,30 @@ +use syn::Type; +use syn::parse::{Parse, ParseStream}; + +use crate::traits::ParseOptionalKeyword; +use crate::types::keyword::Keyword; +use crate::types::keywords::New; + +#[derive(Clone)] +pub struct ProviderArgs { + pub new: Option>, + pub component_type: Option, +} + +impl Parse for ProviderArgs { + fn parse(input: ParseStream) -> syn::Result { + let new = input.parse_optional_keyword()?; + + let component_type = if !input.is_empty() { + let component_type: Type = input.parse()?; + Some(component_type) + } else { + None + }; + + Ok(ProviderArgs { + new, + component_type, + }) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_provider/item.rs b/crates/cgp-macro-core/src/types/cgp_provider/item.rs new file mode 100644 index 00000000..d522e59e --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_provider/item.rs @@ -0,0 +1,76 @@ +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{Error, Ident, ItemImpl, ItemStruct, Type, parse_quote, parse2}; + +use crate::types::cgp_provider::{LoweredCgpProvider, ProviderArgs}; +use crate::types::ident::{IdentWithTypeArgs, IdentWithTypeGenerics}; +use crate::types::is_provider_for::ItemIsProviderFor; + +pub struct ItemCgpProvider { + pub args: ProviderArgs, + pub item_impl: ItemImpl, +} + +impl ItemCgpProvider { + pub fn lower(&self) -> syn::Result { + let provider_struct = self.to_provider_struct()?; + + let is_provider_for_impl = ItemIsProviderFor { + component_type: self.component_type()?, + item_impl: self.item_impl.clone(), + } + .lower()?; + + Ok(LoweredCgpProvider { + item_impl: self.item_impl.clone(), + is_provider_for_impl, + provider_struct, + }) + } + + pub fn component_type(&self) -> syn::Result { + let item_impl = &self.item_impl; + + let (_, provider_trait_path, _) = item_impl.trait_.as_ref().ok_or_else(|| { + Error::new(item_impl.span(), "expect provider trait name to be present") + })?; + + let provider_trait: IdentWithTypeArgs = parse2(provider_trait_path.to_token_stream())?; + + let component_ident = Ident::new( + &format!("{}Component", provider_trait.ident), + provider_trait.span(), + ); + + parse2(component_ident.to_token_stream()) + } + + pub fn to_provider_struct(&self) -> syn::Result> { + if self.args.new.is_none() { + return Ok(None); + } + + let provider_impl = &self.item_impl; + + let impl_self_type = &provider_impl.self_ty; + + let provider_type: IdentWithTypeGenerics = parse_quote!( #impl_self_type ); + + let provider_name = &provider_type.ident; + let type_generics_params = &provider_type.type_generics.params; + + let provider_struct = if type_generics_params.is_empty() { + parse_quote! { + pub struct #provider_name; + } + } else { + parse_quote! { + pub struct #provider_name<#type_generics_params>( + pub ::core::marker::PhantomData<(#type_generics_params)> + ); + } + }; + + Ok(Some(provider_struct)) + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_provider/lower.rs b/crates/cgp-macro-core/src/types/cgp_provider/lower.rs new file mode 100644 index 00000000..10fb4496 --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_provider/lower.rs @@ -0,0 +1,17 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{ItemImpl, ItemStruct}; + +pub struct LoweredCgpProvider { + pub item_impl: ItemImpl, + pub is_provider_for_impl: ItemImpl, + pub provider_struct: Option, +} + +impl ToTokens for LoweredCgpProvider { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.item_impl.to_tokens(tokens); + self.is_provider_for_impl.to_tokens(tokens); + self.provider_struct.to_tokens(tokens); + } +} diff --git a/crates/cgp-macro-core/src/types/cgp_provider/mod.rs b/crates/cgp-macro-core/src/types/cgp_provider/mod.rs new file mode 100644 index 00000000..1634b662 --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_provider/mod.rs @@ -0,0 +1,9 @@ +mod args; +mod item; +mod lower; +mod provider_impl_args; + +pub use args::*; +pub use item::*; +pub use lower::*; +pub use provider_impl_args::*; diff --git a/crates/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs b/crates/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs new file mode 100644 index 00000000..e6d31c3f --- /dev/null +++ b/crates/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs @@ -0,0 +1,77 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::token::Comma; +use syn::{Error, GenericArgument, Lifetime, Type}; + +use crate::types::generics::GenericArguments; + +pub struct ProviderImplArgs { + pub context_type: Type, + pub impl_args: Punctuated, +} + +pub enum ProviderImplArg { + Type(Type), + Life(Lifetime), +} + +impl ProviderImplArgs { + pub fn from_generic_args(generic_args: &GenericArguments) -> syn::Result { + let mut impl_args: Punctuated = Punctuated::new(); + let mut context_type: Option = None; + + if let Some(args) = &generic_args.args { + for arg in &args.args { + match arg { + GenericArgument::Lifetime(life) => { + impl_args.push(ProviderImplArg::Life(life.clone())); + } + GenericArgument::Type(ty) => { + if context_type.is_none() { + context_type = Some(ty.clone()); + } else { + impl_args.push(ProviderImplArg::Type(ty.clone())); + } + } + _ => { + return Err(Error::new( + arg.span(), + format!("unsupported type argument: {:?}", arg), + )); + } + } + } + } + + let context_type = context_type.ok_or_else(|| Error::new( + generic_args.span(), + "provider impl should contain trait path containing at least one generic type parameter", + ))?; + + Ok(Self { + context_type, + impl_args, + }) + } +} + +impl ToTokens for ProviderImplArgs { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.impl_args.to_tokens(tokens); + } +} + +impl ToTokens for ProviderImplArg { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + ProviderImplArg::Type(ty) => { + ty.to_tokens(tokens); + } + ProviderImplArg::Life(life) => { + tokens.extend(quote!(Life<#life>)); + } + } + } +} diff --git a/crates/cgp-macro-core/src/types/delegate_component/statement/eval.rs b/crates/cgp-macro-core/src/types/delegate_component/statement/eval.rs index 8a47c5b5..dfc8530e 100644 --- a/crates/cgp-macro-core/src/types/delegate_component/statement/eval.rs +++ b/crates/cgp-macro-core/src/types/delegate_component/statement/eval.rs @@ -1,7 +1,7 @@ use syn::{Generics, Ident, Type, parse_quote}; use crate::types::delegate_component::{EvalDelegateEntry, EvaluatedDelegateEntry}; -use crate::types::generics::TypeGenerics; +use crate::types::ident::IdentWithTypeArgs; pub trait EvalForEntries { fn eval_for_entries(&self, table_type: &Type) -> syn::Result>; @@ -16,8 +16,7 @@ pub struct EvaluatedForEntry { pub table_type: Type, pub for_key: Ident, pub for_value: Ident, - pub namespace_ident: Ident, - pub namespace_generics: TypeGenerics, + pub namespace: IdentWithTypeArgs, pub mapping_key: Type, pub mapping_value: Type, } @@ -47,15 +46,13 @@ impl EvalDelegateEntry for EvaluatedForEntry { let table_type = &self.table_type; let namespace_trait: Type = { - let namespace_ident = &self.namespace_ident; - let mut namespace_generics = self.namespace_generics.clone(); + let namespace_ident = &self.namespace.ident; + let mut namespace_generics = self.namespace.type_args.clone(); + let namespace_generic_args = &mut namespace_generics.make_args(); - namespace_generics - .generics - .params - .push(parse_quote!(#table_type)); + namespace_generic_args.push(parse_quote!(#table_type)); - namespace_generics.generics.params.push(parse_quote! { + namespace_generic_args.push(parse_quote! { Delegate = #mapping_value }); diff --git a/crates/cgp-macro-core/src/types/delegate_component/statement/for_loop.rs b/crates/cgp-macro-core/src/types/delegate_component/statement/for_loop.rs index 511c633a..d483312c 100644 --- a/crates/cgp-macro-core/src/types/delegate_component/statement/for_loop.rs +++ b/crates/cgp-macro-core/src/types/delegate_component/statement/for_loop.rs @@ -8,7 +8,7 @@ use crate::types::delegate_component::{ EvaluatedDelegateEntry, EvaluatedForEntry, NormalDelegateMapping, eval_delegate_entries_via_for, }; -use crate::types::ident_type::IdentType; +use crate::types::ident::IdentWithTypeArgs; #[derive(Debug, Clone)] pub struct ForDelegateStatement { @@ -19,7 +19,7 @@ pub struct ForDelegateStatement { pub value: Ident, pub gt: Gt, pub in_token: In, - pub namespace: IdentType, + pub namespace: IdentWithTypeArgs, pub where_clause: Option, pub mappings: Punctuated, } @@ -71,8 +71,7 @@ impl EvalForEntries for ForDelegateStatement { table_type: table_type.clone(), for_key: self.key.clone(), for_value: self.value.clone(), - namespace_ident: self.namespace.ident.clone(), - namespace_generics: self.namespace.generics.clone(), + namespace: self.namespace.clone(), mapping_key: key.key, mapping_value: value_type.clone(), }; diff --git a/crates/cgp-macro-core/src/types/delegate_component/statement/namespace.rs b/crates/cgp-macro-core/src/types/delegate_component/statement/namespace.rs index 81074ae3..04736773 100644 --- a/crates/cgp-macro-core/src/types/delegate_component/statement/namespace.rs +++ b/crates/cgp-macro-core/src/types/delegate_component/statement/namespace.rs @@ -6,7 +6,6 @@ use crate::types::delegate_component::{ EvalDelegateEntries, EvalForEntries, EvalForEntry, EvaluatedDelegateEntry, EvaluatedForEntry, eval_delegate_entries_via_for, }; -use crate::types::generics::TypeGenerics; use crate::types::keyword::Keyword; use crate::types::keywords::Namespace; @@ -40,8 +39,7 @@ impl EvalForEntry for NamespaceDelegateStatement { for_value: parse_quote!(__Value__), mapping_key: parse_quote!(__Key__), mapping_value: parse_quote!(__Value__), - namespace_ident: self.ident.clone(), - namespace_generics: TypeGenerics::default(), + namespace: self.ident.clone().into(), }; Ok(entry) diff --git a/crates/cgp-macro-core/src/types/delegate_component/table/main.rs b/crates/cgp-macro-core/src/types/delegate_component/table/main.rs index 6a5bb9db..103fb1d7 100644 --- a/crates/cgp-macro-core/src/types/delegate_component/table/main.rs +++ b/crates/cgp-macro-core/src/types/delegate_component/table/main.rs @@ -3,10 +3,10 @@ use quote::ToTokens; use syn::parse::{Parse, ParseStream}; use syn::{ItemImpl, ItemStruct, Type, braced, parse2}; -use crate::traits::PeekKeyword; +use crate::traits::ParseOptionalKeyword; use crate::types::delegate_component::{DelegateEntries, ExtractInnerDelegateTables}; use crate::types::generics::ImplGenerics; -use crate::types::ident_type::IdentType; +use crate::types::ident::IdentWithTypeGenerics; use crate::types::keyword::Keyword; use crate::types::keywords::New; use crate::types::provider_struct::ProviderStruct; @@ -27,11 +27,7 @@ impl Parse for DelegateTable { fn parse(input: ParseStream) -> syn::Result { let impl_generics = input.parse()?; - let new = if input.peek_keyword::() { - Some(input.parse()?) - } else { - None - }; + let new = input.parse_optional_keyword()?; let table_type = input.parse()?; @@ -57,11 +53,11 @@ impl DelegateTable { let mut item_structs = Vec::new(); if self.new.is_some() { - let struct_type: IdentType = parse2(self.table_type.to_token_stream())?; + let struct_type: IdentWithTypeGenerics = parse2(self.table_type.to_token_stream())?; item_structs.push( ProviderStruct { ident: struct_type.ident, - generics: struct_type.generics.generics, + generics: struct_type.type_generics.generics, } .to_item_struct()?, ); diff --git a/crates/cgp-macro-core/src/types/field/field_name.rs b/crates/cgp-macro-core/src/types/field/field_name.rs new file mode 100644 index 00000000..5e8ea365 --- /dev/null +++ b/crates/cgp-macro-core/src/types/field/field_name.rs @@ -0,0 +1,37 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::Ident; + +use crate::types::field::{Index, Symbol}; + +pub enum FieldName { + Ident(Symbol), + Index(Index), +} + +impl From for FieldName { + fn from(value: Ident) -> Self { + Self::Ident(Symbol::new(value)) + } +} + +impl From for FieldName { + fn from(value: Symbol) -> Self { + Self::Ident(value) + } +} + +impl From for FieldName { + fn from(value: Index) -> Self { + Self::Index(value) + } +} + +impl ToTokens for FieldName { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Ident(symbol) => symbol.to_tokens(tokens), + Self::Index(index) => index.to_tokens(tokens), + } + } +} diff --git a/crates/cgp-macro-core/src/types/field/has_field_bound.rs b/crates/cgp-macro-core/src/types/field/has_field_bound.rs new file mode 100644 index 00000000..7c62fa4a --- /dev/null +++ b/crates/cgp-macro-core/src/types/field/has_field_bound.rs @@ -0,0 +1,43 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; +use syn::Type; +use syn::token::Mut; + +use crate::exports::{HasField, HasFieldMut}; +use crate::types::getter::FieldMode; + +pub struct HasFieldBound { + pub field_type: Type, + pub field_mut: Option, + pub field_mode: FieldMode, + pub tag_type: Type, +} + +impl ToTokens for HasFieldBound { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + field_type, + field_mut, + field_mode, + tag_type, + } = self; + + let output = if field_mut.is_none() { + if let FieldMode::Slice = field_mode { + quote! { + #HasField< #tag_type, Value: AsRef< [ #field_type ] > + 'static > + } + } else { + quote! { + #HasField< #tag_type, Value = #field_type > + } + } + } else { + quote! { + #HasFieldMut< #tag_type, Value = #field_type > + } + }; + + tokens.extend(output); + } +} diff --git a/crates/cgp-macro-core/src/types/field/index.rs b/crates/cgp-macro-core/src/types/field/index.rs new file mode 100644 index 00000000..d5b4b187 --- /dev/null +++ b/crates/cgp-macro-core/src/types/field/index.rs @@ -0,0 +1,20 @@ +use proc_macro2::{Span, TokenStream}; +use quote::{ToTokens, quote}; +use syn::LitInt; + +use crate::exports::Index as Delta; + +pub struct Index { + pub index: usize, + pub span: Span, +} + +impl ToTokens for Index { + fn to_tokens(&self, tokens: &mut TokenStream) { + let index = LitInt::new(&self.index.to_string(), self.span); + + tokens.extend(quote! { + #Delta<#index> + }); + } +} diff --git a/crates/cgp-macro-core/src/types/field/mod.rs b/crates/cgp-macro-core/src/types/field/mod.rs new file mode 100644 index 00000000..acb83f01 --- /dev/null +++ b/crates/cgp-macro-core/src/types/field/mod.rs @@ -0,0 +1,9 @@ +mod field_name; +mod has_field_bound; +mod index; +mod symbol; + +pub use field_name::*; +pub use has_field_bound::*; +pub use index::*; +pub use symbol::*; diff --git a/crates/cgp-macro-core/src/types/symbol.rs b/crates/cgp-macro-core/src/types/field/symbol.rs similarity index 100% rename from crates/cgp-macro-core/src/types/symbol.rs rename to crates/cgp-macro-core/src/types/field/symbol.rs diff --git a/crates/cgp-macro-core/src/types/generics/arguments.rs b/crates/cgp-macro-core/src/types/generics/arguments.rs new file mode 100644 index 00000000..f0284ea9 --- /dev/null +++ b/crates/cgp-macro-core/src/types/generics/arguments.rs @@ -0,0 +1,49 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Comma, Lt}; +use syn::{AngleBracketedGenericArguments, GenericArgument, parse_quote}; + +use crate::types::generics::TypeGenerics; + +#[derive(Debug, Clone, Default)] +pub struct GenericArguments { + pub args: Option, +} + +impl GenericArguments { + pub fn make_args(&mut self) -> &mut Punctuated { + &mut self.args.get_or_insert_with(|| parse_quote!(<>)).args + } +} + +impl Parse for GenericArguments { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Lt) { + let args = input.parse()?; + Ok(Self { args: Some(args) }) + } else { + Ok(Self { args: None }) + } + } +} + +impl ToTokens for GenericArguments { + fn to_tokens(&self, tokens: &mut TokenStream) { + if let Some(args) = &self.args { + args.to_tokens(tokens); + } + } +} + +impl From for GenericArguments { + fn from(generics: TypeGenerics) -> Self { + if generics.params.is_empty() { + Self { args: None } + } else { + let args = parse_quote!(#generics); + Self { args: Some(args) } + } + } +} diff --git a/crates/cgp-macro-core/src/types/generics/mod.rs b/crates/cgp-macro-core/src/types/generics/mod.rs index 9d6eafc8..85037d78 100644 --- a/crates/cgp-macro-core/src/types/generics/mod.rs +++ b/crates/cgp-macro-core/src/types/generics/mod.rs @@ -1,5 +1,7 @@ +mod arguments; mod impl_generics; mod type_generics; +pub use arguments::*; pub use impl_generics::*; pub use type_generics::*; diff --git a/crates/cgp-macro-core/src/types/getter/field_mode.rs b/crates/cgp-macro-core/src/types/getter/field_mode.rs new file mode 100644 index 00000000..f2fdaa1a --- /dev/null +++ b/crates/cgp-macro-core/src/types/getter/field_mode.rs @@ -0,0 +1,9 @@ +#[derive(Clone, Eq, PartialEq)] +pub enum FieldMode { + Reference, + OptionRef, + MRef, + Str, + Copy, + Slice, +} diff --git a/crates/cgp-macro-core/src/types/getter/get_field_expr.rs b/crates/cgp-macro-core/src/types/getter/get_field_expr.rs new file mode 100644 index 00000000..97a6e3b8 --- /dev/null +++ b/crates/cgp-macro-core/src/types/getter/get_field_expr.rs @@ -0,0 +1,31 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; +use syn::Expr; +use syn::token::Mut; + +use crate::types::field::FieldName; + +pub struct GetFieldExpr { + pub receiver: Expr, + pub field_mut: Option, + pub field_name: FieldName, +} + +impl ToTokens for GetFieldExpr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let receiver = &self.receiver; + let field_name = &self.field_name; + + let method = if self.field_mut.is_none() { + quote!(get_field) + } else { + quote!(get_field_mut) + }; + + tokens.extend(quote! { + #receiver.#method( + ::core::marker::PhantomData::<#field_name> + ) + }) + } +} diff --git a/crates/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs b/crates/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs new file mode 100644 index 00000000..3eb02e31 --- /dev/null +++ b/crates/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs @@ -0,0 +1,60 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; + +use crate::types::getter::{FieldMode, GetFieldExpr}; + +pub struct GetFieldWithModeExpr { + pub get_field: GetFieldExpr, + pub field_mode: FieldMode, +} + +impl ToTokens for GetFieldWithModeExpr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let call_expr = &self.get_field; + + let expr = match self.field_mode { + FieldMode::Reference => { + quote!(#call_expr) + } + FieldMode::OptionRef => { + if call_expr.field_mut.is_none() { + quote! { + #call_expr .as_ref() + } + } else { + quote! { + #call_expr .as_mut() + } + } + } + FieldMode::MRef => { + quote! { + MRef::Ref( #call_expr ) + } + } + FieldMode::Str => { + if call_expr.field_mut.is_none() { + quote! { + #call_expr .as_str() + } + } else { + quote! { + #call_expr .as_mut_str() + } + } + } + FieldMode::Copy => { + quote! { + #call_expr .clone() + } + } + FieldMode::Slice => { + quote! { + #call_expr .as_ref() + } + } + }; + + tokens.extend(expr); + } +} diff --git a/crates/cgp-macro-core/src/types/getter/mod.rs b/crates/cgp-macro-core/src/types/getter/mod.rs new file mode 100644 index 00000000..5d40e8a8 --- /dev/null +++ b/crates/cgp-macro-core/src/types/getter/mod.rs @@ -0,0 +1,7 @@ +mod field_mode; +mod get_field_expr; +mod get_field_with_mode_expr; + +pub use field_mode::*; +pub use get_field_expr::*; +pub use get_field_with_mode_expr::*; diff --git a/crates/cgp-macro-core/src/types/ident/ident_with_args.rs b/crates/cgp-macro-core/src/types/ident/ident_with_args.rs new file mode 100644 index 00000000..3d32de1c --- /dev/null +++ b/crates/cgp-macro-core/src/types/ident/ident_with_args.rs @@ -0,0 +1,43 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse::{Parse, ParseStream}; +use syn::{Ident, Type, parse_quote}; + +use crate::types::generics::GenericArguments; + +#[derive(Debug, Clone)] +pub struct IdentWithTypeArgs { + pub ident: Ident, + pub type_args: GenericArguments, +} + +impl Parse for IdentWithTypeArgs { + fn parse(input: ParseStream) -> syn::Result { + let ident = input.parse()?; + let type_args = input.parse()?; + + Ok(Self { ident, type_args }) + } +} + +impl ToTokens for IdentWithTypeArgs { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.ident.to_tokens(tokens); + self.type_args.to_tokens(tokens); + } +} + +impl From for IdentWithTypeArgs { + fn from(ident: Ident) -> Self { + Self { + ident, + type_args: GenericArguments::default(), + } + } +} + +impl From for Type { + fn from(value: IdentWithTypeArgs) -> Self { + parse_quote!(#value) + } +} diff --git a/crates/cgp-macro-core/src/types/ident/ident_with_type_generics.rs b/crates/cgp-macro-core/src/types/ident/ident_with_type_generics.rs new file mode 100644 index 00000000..f2c6a36a --- /dev/null +++ b/crates/cgp-macro-core/src/types/ident/ident_with_type_generics.rs @@ -0,0 +1,31 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::Ident; +use syn::parse::{Parse, ParseStream}; + +use crate::types::generics::TypeGenerics; + +#[derive(Debug, Clone)] +pub struct IdentWithTypeGenerics { + pub ident: Ident, + pub type_generics: TypeGenerics, +} + +impl Parse for IdentWithTypeGenerics { + fn parse(input: ParseStream) -> syn::Result { + let ident = input.parse()?; + let type_generics = input.parse()?; + + Ok(Self { + ident, + type_generics, + }) + } +} + +impl ToTokens for IdentWithTypeGenerics { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.ident.to_tokens(tokens); + self.type_generics.to_tokens(tokens); + } +} diff --git a/crates/cgp-macro-core/src/types/ident/mod.rs b/crates/cgp-macro-core/src/types/ident/mod.rs new file mode 100644 index 00000000..db89db13 --- /dev/null +++ b/crates/cgp-macro-core/src/types/ident/mod.rs @@ -0,0 +1,5 @@ +pub mod ident_with_args; +pub mod ident_with_type_generics; + +pub use ident_with_args::*; +pub use ident_with_type_generics::*; diff --git a/crates/cgp-macro-core/src/types/ident_type.rs b/crates/cgp-macro-core/src/types/ident_type.rs deleted file mode 100644 index 2578807c..00000000 --- a/crates/cgp-macro-core/src/types/ident_type.rs +++ /dev/null @@ -1,19 +0,0 @@ -use syn::Ident; -use syn::parse::{Parse, ParseStream}; - -use crate::types::generics::TypeGenerics; - -#[derive(Debug, Clone)] -pub struct IdentType { - pub ident: Ident, - pub generics: TypeGenerics, -} - -impl Parse for IdentType { - fn parse(input: ParseStream) -> syn::Result { - let ident = input.parse()?; - let generics = input.parse()?; - - Ok(Self { ident, generics }) - } -} diff --git a/crates/cgp-macro-core/src/types/implicits/arg_field.rs b/crates/cgp-macro-core/src/types/implicits/arg_field.rs new file mode 100644 index 00000000..da7ff56a --- /dev/null +++ b/crates/cgp-macro-core/src/types/implicits/arg_field.rs @@ -0,0 +1,48 @@ +use syn::token::Mut; +use syn::{Ident, Stmt, Type, parse_quote}; + +use crate::types::field::{FieldName, HasFieldBound}; +use crate::types::getter::{FieldMode, GetFieldExpr, GetFieldWithModeExpr}; + +#[derive(Clone, Eq, PartialEq)] +pub struct ImplicitArgField { + pub field_name: Ident, + pub field_type: Type, + pub field_mut: Option, + pub field_mode: FieldMode, + pub arg_type: Type, +} + +impl ImplicitArgField { + pub fn to_has_field_bound(&self) -> syn::Result { + let field_name = FieldName::from(self.field_name.clone()); + let tag_type = parse_quote!(#field_name); + + Ok(HasFieldBound { + field_type: self.field_type.clone(), + field_mut: self.field_mut, + field_mode: self.field_mode.clone(), + tag_type, + }) + } + + pub fn to_statement(&self) -> syn::Result { + let field_name = &self.field_name; + let arg_type = &self.arg_type; + + let get_field_expr = GetFieldWithModeExpr { + field_mode: self.field_mode.clone(), + get_field: GetFieldExpr { + receiver: parse_quote!(self), + field_mut: self.field_mut, + field_name: self.field_name.clone().into(), + }, + }; + + let statement = parse_quote! { + let #field_name: #arg_type = #get_field_expr; + }; + + Ok(statement) + } +} diff --git a/crates/cgp-macro-core/src/types/implicits/arg_fields.rs b/crates/cgp-macro-core/src/types/implicits/arg_fields.rs new file mode 100644 index 00000000..d79e8e8f --- /dev/null +++ b/crates/cgp-macro-core/src/types/implicits/arg_fields.rs @@ -0,0 +1,65 @@ +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{Block, ImplItem, TypeParamBound, parse_quote}; + +use crate::functions::extract_and_parse_implicit_args; +use crate::traits::ToTypeParamBounds; +use crate::types::implicits::ImplicitArgField; + +#[derive(Default)] +pub struct ImplicitArgFields { + pub fields: Vec, +} + +impl ImplicitArgFields { + pub fn new(fields: Vec) -> Self { + Self { fields } + } +} + +impl ToTypeParamBounds for ImplicitArgFields { + fn to_type_param_bounds(&self) -> syn::Result> { + let mut constraints: Punctuated = Punctuated::new(); + + for field in &self.fields { + let constraint = field.to_has_field_bound()?; + constraints.push(parse_quote!(#constraint)); + } + + Ok(constraints) + } +} + +impl ImplicitArgFields { + pub fn prepend_to_block(&self, block: &mut Block) -> syn::Result<()> { + let block_statements = core::mem::take(&mut block.stmts); + + for field in &self.fields { + let statement = field.to_statement()?; + block.stmts.push(statement); + } + + block.stmts.extend(block_statements); + + Ok(()) + } + + pub fn extract_from_impl_items(impl_items: &mut [ImplItem]) -> syn::Result { + let mut all_fields = 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)?; + implicit_args.prepend_to_block(&mut method.block)?; + + for implicit_arg in implicit_args.fields { + if !all_fields.contains(&implicit_arg) { + all_fields.push(implicit_arg); + } + } + } + } + + Ok(ImplicitArgFields { fields: all_fields }) + } +} diff --git a/crates/cgp-macro-core/src/types/implicits/mod.rs b/crates/cgp-macro-core/src/types/implicits/mod.rs new file mode 100644 index 00000000..4564e970 --- /dev/null +++ b/crates/cgp-macro-core/src/types/implicits/mod.rs @@ -0,0 +1,5 @@ +mod arg_field; +mod arg_fields; + +pub use arg_field::*; +pub use arg_fields::*; diff --git a/crates/cgp-macro-core/src/types/is_provider_for.rs b/crates/cgp-macro-core/src/types/is_provider_for.rs new file mode 100644 index 00000000..e793de78 --- /dev/null +++ b/crates/cgp-macro-core/src/types/is_provider_for.rs @@ -0,0 +1,63 @@ +use std::collections::BTreeMap; + +use proc_macro2::Span; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::token::For; +use syn::{Error, ItemImpl, Path, Type, parse_quote, parse2}; + +use crate::types::cgp_provider::ProviderImplArgs; +use crate::types::ident::IdentWithTypeArgs; +use crate::visitors::replace_provider_in_generics; + +pub fn derive_is_provider_for( + component_type: &Type, + item_impl: &ItemImpl, +) -> syn::Result { + ItemIsProviderFor { + component_type: component_type.clone(), + item_impl: item_impl.clone(), + } + .lower() +} + +pub struct ItemIsProviderFor { + pub component_type: Type, + pub item_impl: ItemImpl, +} + +impl ItemIsProviderFor { + pub fn lower(&self) -> syn::Result { + let component_type = &self.component_type; + let item_impl = &self.item_impl; + + let (_, provider_path, _) = item_impl.trait_.as_ref().ok_or_else(|| { + Error::new(item_impl.span(), "provider impl should contain trait path") + })?; + + let IdentWithTypeArgs { + ident: provider_ident, + type_args: provider_generics, + } = parse2(provider_path.to_token_stream())?; + + let impl_args = ProviderImplArgs::from_generic_args(&provider_generics)?; + let context_type = &impl_args.context_type; + + let is_provider_path: Path = + parse_quote!( IsProviderFor < #component_type, #context_type, ( #impl_args ) > ); + + let mut is_provider_impl = item_impl.clone(); + + is_provider_impl.attrs.clear(); + is_provider_impl.items.clear(); + is_provider_impl.defaultness = None; + is_provider_impl.unsafety = None; + + is_provider_impl.trait_ = Some((None, is_provider_path, For(Span::call_site()))); + + let provider_map = BTreeMap::from([(provider_ident.clone(), component_type.clone())]); + replace_provider_in_generics(&provider_map, &mut is_provider_impl.generics); + + Ok(is_provider_impl) + } +} diff --git a/crates/cgp-macro-core/src/types/mod.rs b/crates/cgp-macro-core/src/types/mod.rs index 166a4e6d..fa03e43d 100644 --- a/crates/cgp-macro-core/src/types/mod.rs +++ b/crates/cgp-macro-core/src/types/mod.rs @@ -1,9 +1,15 @@ +pub mod attributes; +pub mod cgp_impl; +pub mod cgp_provider; pub mod delegate_component; +pub mod field; pub mod generics; -pub mod ident_type; +pub mod getter; +pub mod ident; +pub mod implicits; +pub mod is_provider_for; pub mod keyword; pub mod keywords; pub mod namespace; pub mod path; pub mod provider_struct; -pub mod symbol; diff --git a/crates/cgp-macro-core/src/types/namespace/ident.rs b/crates/cgp-macro-core/src/types/namespace/ident.rs deleted file mode 100644 index 2aa80364..00000000 --- a/crates/cgp-macro-core/src/types/namespace/ident.rs +++ /dev/null @@ -1,18 +0,0 @@ -use syn::Ident; -use syn::parse::{Parse, ParseStream}; - -use crate::types::generics::TypeGenerics; - -pub struct NamespaceIdent { - pub ident: Ident, - pub generics: TypeGenerics, -} - -impl Parse for NamespaceIdent { - fn parse(input: ParseStream) -> syn::Result { - let ident = input.parse()?; - let generics = input.parse()?; - - Ok(Self { ident, generics }) - } -} diff --git a/crates/cgp-macro-core/src/types/namespace/inherit.rs b/crates/cgp-macro-core/src/types/namespace/inherit.rs index 7852a286..e565c5ab 100644 --- a/crates/cgp-macro-core/src/types/namespace/inherit.rs +++ b/crates/cgp-macro-core/src/types/namespace/inherit.rs @@ -1,29 +1,27 @@ use syn::{Generics, Ident, Type, parse_quote}; use crate::types::delegate_component::{EvalForEntry, EvaluatedForEntry}; -use crate::types::generics::TypeGenerics; +use crate::types::ident::IdentWithTypeArgs; #[derive(Debug, Clone)] pub struct InheritNamespaceStatement { - pub ident: Ident, - pub type_generics: TypeGenerics, + pub namespace: IdentWithTypeArgs, pub local_table_ident: Ident, } impl EvalForEntry for InheritNamespaceStatement { fn eval_for_entry(&self, table_type: &Type) -> syn::Result { - let namespace_ident = self.ident.clone(); let local_table_ident = &self.local_table_ident; - let mut namespace_where_generics = self.type_generics.clone(); - - namespace_where_generics - .params + let mut namespace_constraint = self.namespace.clone(); + namespace_constraint + .type_args + .make_args() .push(parse_quote!(#local_table_ident)); let mut generics = Generics::default(); generics.make_where_clause().predicates.push(parse_quote! { - __Key__: #namespace_ident #namespace_where_generics + __Key__: #namespace_constraint }); let for_entry = EvaluatedForEntry { @@ -33,8 +31,7 @@ impl EvalForEntry for InheritNamespaceStatement { for_value: parse_quote!(__Value__), mapping_key: parse_quote!(__Key__), mapping_value: parse_quote!(__Value__), - namespace_ident, - namespace_generics: self.type_generics.clone(), + namespace: self.namespace.clone(), }; Ok(for_entry) diff --git a/crates/cgp-macro-core/src/types/namespace/mod.rs b/crates/cgp-macro-core/src/types/namespace/mod.rs index d8f487b1..d3292a9d 100644 --- a/crates/cgp-macro-core/src/types/namespace/mod.rs +++ b/crates/cgp-macro-core/src/types/namespace/mod.rs @@ -1,9 +1,7 @@ mod eval; -mod ident; mod inherit; mod table; pub use eval::*; -pub use ident::*; pub use inherit::*; pub use table::*; diff --git a/crates/cgp-macro-core/src/types/namespace/table.rs b/crates/cgp-macro-core/src/types/namespace/table.rs index d84b9400..2d5e4c58 100644 --- a/crates/cgp-macro-core/src/types/namespace/table.rs +++ b/crates/cgp-macro-core/src/types/namespace/table.rs @@ -2,12 +2,12 @@ use syn::parse::{Parse, ParseStream}; use syn::token::Colon; use syn::{Error, Ident, ItemImpl, ItemStruct, ItemTrait, Type, braced, parse_quote}; -use crate::traits::PeekKeyword; +use crate::traits::ParseOptionalKeyword; use crate::types::delegate_component::{ DelegateEntries, EvalDelegateEntries, EvalDelegateEntry, EvalForEntry, }; use crate::types::generics::ImplGenerics; -use crate::types::ident_type::IdentType; +use crate::types::ident::{IdentWithTypeArgs, IdentWithTypeGenerics}; use crate::types::keyword::Keyword; use crate::types::keywords::New; use crate::types::namespace::{EvaluatedNamespaceTable, InheritNamespaceStatement}; @@ -15,8 +15,8 @@ use crate::types::namespace::{EvaluatedNamespaceTable, InheritNamespaceStatement pub struct NamespaceTable { pub impl_generics: ImplGenerics, pub new: Option>, - pub namespace_type: IdentType, - pub parent_namespace: Option<(Colon, IdentType)>, + pub namespace: IdentWithTypeGenerics, + pub parent_namespace: Option<(Colon, IdentWithTypeArgs)>, pub entries: DelegateEntries, } @@ -24,11 +24,7 @@ impl Parse for NamespaceTable { fn parse(input: ParseStream) -> syn::Result { let impl_generics = input.parse()?; - let new = if input.peek_keyword::() { - Some(input.parse()?) - } else { - None - }; + let new = input.parse_optional_keyword()?; let namespace_type = input.parse()?; let parent_namespace = if input.peek(Colon) { @@ -49,7 +45,7 @@ impl Parse for NamespaceTable { Ok(Self { impl_generics, new, - namespace_type, + namespace: namespace_type, parent_namespace, entries, }) @@ -58,8 +54,8 @@ impl Parse for NamespaceTable { impl NamespaceTable { pub fn build_namespace_trait(&self) -> syn::Result { - let namespace_ident = &self.namespace_type.ident; - let mut namespace_generics = self.namespace_type.generics.clone(); + let namespace_ident = &self.namespace.ident; + let mut namespace_generics = self.namespace.type_generics.clone(); namespace_generics.params.push(parse_quote!(__Table__)); let namespace_trait: Type = parse_quote!( #namespace_ident #namespace_generics ); @@ -117,7 +113,7 @@ impl NamespaceTable { )); } - let namespace_ident = self.namespace_type.ident.clone(); + let namespace_ident = self.namespace.ident.clone(); let table_type: Type = parse_quote!(__Table__); @@ -131,8 +127,7 @@ impl NamespaceTable { }; let for_entry = InheritNamespaceStatement { - ident: parent_namespace.ident.clone(), - type_generics: parent_namespace.generics.clone(), + namespace: parent_namespace.clone(), local_table_ident: namespace_struct_ident, } .eval_for_entry(&table_type)?; diff --git a/crates/cgp-macro-core/src/types/path/path_element.rs b/crates/cgp-macro-core/src/types/path/path_element.rs index 2e5d8afc..75936d08 100644 --- a/crates/cgp-macro-core/src/types/path/path_element.rs +++ b/crates/cgp-macro-core/src/types/path/path_element.rs @@ -4,7 +4,7 @@ use syn::parse::{Parse, ParseStream}; use syn::{Ident, Type, parse2}; use crate::traits::ToType; -use crate::types::symbol::Symbol; +use crate::types::field::Symbol; #[derive(Debug, Clone)] pub enum PathElement { @@ -23,7 +23,7 @@ impl Parse for PathElement { && path_char.is_ascii_lowercase() && !is_primitive_type(&path_str) { - Self::Symbol(Symbol { ident: path_ident }) + Self::Symbol(Symbol::new(path_ident)) } else { Self::Type(ty) } diff --git a/crates/cgp-macro-core/src/visitors/mod.rs b/crates/cgp-macro-core/src/visitors/mod.rs new file mode 100644 index 00000000..8a4dbdcd --- /dev/null +++ b/crates/cgp-macro-core/src/visitors/mod.rs @@ -0,0 +1,7 @@ +mod replace_provider; +mod replace_self; +mod substitute_abstract_type; + +pub use replace_provider::*; +pub use replace_self::*; +pub use substitute_abstract_type::*; diff --git a/crates/cgp-macro-lib/src/derive_provider/replace_constraint.rs b/crates/cgp-macro-core/src/visitors/replace_provider.rs similarity index 66% rename from crates/cgp-macro-lib/src/derive_provider/replace_constraint.rs rename to crates/cgp-macro-core/src/visitors/replace_provider.rs index 3cf89bea..f7bbf9b4 100644 --- a/crates/cgp-macro-lib/src/derive_provider/replace_constraint.rs +++ b/crates/cgp-macro-core/src/visitors/replace_provider.rs @@ -2,35 +2,32 @@ use std::collections::BTreeMap; use syn::punctuated::Punctuated; use syn::token::{Comma, Plus}; +use syn::visit_mut::{VisitMut, visit_generics_mut}; use syn::{ - GenericArgument, GenericParam, Generics, Ident, PathArguments, Type, TypeParamBound, - WherePredicate, parse_quote, + GenericArgument, Generics, Ident, PathArguments, PredicateType, Type, TypeParam, + TypeParamBound, parse_quote, }; pub fn replace_provider_in_generics(provider_map: &BTreeMap, generics: &mut Generics) { - for param in &mut generics.params { - if let GenericParam::Type(type_param) = param { - replace_provider_in_type_params(provider_map, &mut type_param.bounds); - } - } + let mut visitor = ReplaceProviderVisitor { provider_map }; + visit_generics_mut(&mut visitor, generics); +} - if let Some(where_clause) = &mut generics.where_clause { - replace_provider_in_where_predicate(provider_map, &mut where_clause.predicates); - } +struct ReplaceProviderVisitor<'a> { + provider_map: &'a BTreeMap, } -pub fn replace_provider_in_where_predicate( - provider_map: &BTreeMap, - predicates: &mut Punctuated, -) { - for predicate in predicates.iter_mut() { - if let WherePredicate::Type(type_predicate) = predicate { - replace_provider_in_type_params(provider_map, &mut type_predicate.bounds); - } +impl<'a> VisitMut for ReplaceProviderVisitor<'a> { + fn visit_type_param_mut(&mut self, type_param: &mut TypeParam) { + replace_provider_in_type_params(self.provider_map, &mut type_param.bounds); + } + + fn visit_predicate_type_mut(&mut self, type_predicate: &mut PredicateType) { + replace_provider_in_type_params(self.provider_map, &mut type_predicate.bounds); } } -pub fn replace_provider_in_type_params( +fn replace_provider_in_type_params( provider_map: &BTreeMap, type_params: &mut Punctuated, ) { diff --git a/crates/cgp-macro-lib/src/replace_self/mod.rs b/crates/cgp-macro-core/src/visitors/replace_self/mod.rs similarity index 56% rename from crates/cgp-macro-lib/src/replace_self/mod.rs rename to crates/cgp-macro-core/src/visitors/replace_self/mod.rs index 630527d2..0cf5c5a6 100644 --- a/crates/cgp-macro-lib/src/replace_self/mod.rs +++ b/crates/cgp-macro-core/src/visitors/replace_self/mod.rs @@ -1,9 +1,7 @@ mod replace_self_receiver; mod replace_self_type; -mod replace_self_var; -mod snake_case; +mod replace_self_value; pub use replace_self_receiver::*; pub use replace_self_type::*; -pub use replace_self_var::*; -pub use snake_case::*; +pub use replace_self_value::*; diff --git a/crates/cgp-macro-core/src/visitors/replace_self/replace_self_receiver.rs b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_receiver.rs new file mode 100644 index 00000000..7fdf0bce --- /dev/null +++ b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_receiver.rs @@ -0,0 +1,45 @@ +use proc_macro2::Ident; +use syn::visit_mut::VisitMut; +use syn::{FnArg, Receiver, Signature, Type, parse_quote}; + +pub struct ReplaceSelfReceiverVisitor<'a> { + pub replaced_ident: &'a Ident, + pub replaced_type: &'a Type, +} + +impl<'a> VisitMut for ReplaceSelfReceiverVisitor<'a> { + fn visit_signature_mut(&mut self, sig: &mut Signature) { + if let Some(arg) = sig.inputs.first_mut() + && let FnArg::Receiver(receiver) = arg + { + *arg = replace_self_receiver(receiver, self.replaced_ident, self.replaced_type); + } + } +} + +pub fn replace_self_receiver( + receiver: &mut Receiver, + replaced_ident: &Ident, + replaced_type: &Type, +) -> FnArg { + match (&receiver.reference, &receiver.mutability) { + (None, None) => { + parse_quote!(#replaced_ident : #replaced_type) + } + (Some((_and, None)), None) => { + parse_quote!(#replaced_ident : & #replaced_type) + } + (Some((_and, Some(life))), None) => { + parse_quote!(#replaced_ident : & #life #replaced_type) + } + (Some((_and, None)), Some(_mut)) => { + parse_quote!(#replaced_ident : &mut #replaced_type) + } + (Some((_and, Some(life))), Some(_mut)) => { + parse_quote!(#replaced_ident : & #life mut #replaced_type) + } + (None, Some(_mut)) => { + parse_quote!(#replaced_ident : mut #replaced_type) + } + } +} diff --git a/crates/cgp-macro-core/src/visitors/replace_self/replace_self_type.rs b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_type.rs new file mode 100644 index 00000000..891514b0 --- /dev/null +++ b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_type.rs @@ -0,0 +1,125 @@ +use itertools::Itertools; +use proc_macro2::{Group, Ident, TokenStream, TokenTree}; +use quote::{ToTokens, format_ident}; +use syn::visit_mut::{self, VisitMut}; +use syn::{Macro, Path, Type}; + +pub struct ReplaceSelfTypeVisitor<'a> { + pub replaced_type: &'a Type, + pub skip_assoc_types: &'a Vec, +} + +impl<'a> ReplaceSelfTypeVisitor<'a> { + fn replace_self_in_path(&self, path: &mut Path) { + let Some(first) = path.segments.first() else { + return; + }; + if first.ident != "Self" { + return; + } + if path.segments.len() >= 2 && self.skip_assoc_types.contains(&path.segments[1].ident) { + return; + } + if let Type::Path(replaced) = self.replaced_type + && replaced.qself.is_none() + { + let rest: Vec<_> = path.segments.iter().skip(1).cloned().collect(); + let mut new_path = replaced.path.clone(); + new_path.segments.extend(rest); + *path = new_path; + } + } +} + +impl VisitMut for ReplaceSelfTypeVisitor<'_> { + fn visit_type_mut(&mut self, ty: &mut Type) { + // Handle standalone `Self` type — replaced_type may not be a path (e.g. a reference), + // so we must replace the whole Type node here rather than going through visit_path_mut. + if let Type::Path(type_path) = ty + && type_path.qself.is_none() + && type_path.path.segments.len() == 1 + && type_path.path.segments[0].ident == "Self" + { + *ty = self.replaced_type.clone(); + return; + } + visit_mut::visit_type_mut(self, ty); + } + + fn visit_path_mut(&mut self, path: &mut Path) { + // Handles Self::Foo in type paths (multi-segment) and Self in expression/struct paths. + // Single-segment Self in type position is already handled by visit_type_mut above. + self.replace_self_in_path(path); + visit_mut::visit_path_mut(self, path); + } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + mac.tokens = replace_self_type_in_token_stream( + core::mem::take(&mut mac.tokens), + self.replaced_type.to_token_stream(), + self.skip_assoc_types, + ); + } +} + +pub fn replace_self_type_in_token_stream( + stream: TokenStream, + replaced_ident: TokenStream, + local_assoc_types: &Vec, +) -> TokenStream { + let self_type = format_ident!("Self"); + + let mut result_stream: Vec = Vec::new(); + + let mut token_iter = stream.into_iter().multipeek(); + + while let Some(tree) = token_iter.next() { + match tree { + TokenTree::Ident(ident) => { + if ident == self_type { + let replaced_ident = replaced_ident.clone(); + + // Do not replace self if it is an associated type expression that refers to local associated type + let replaced = match token_iter.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + match token_iter.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + match token_iter.peek() { + Some(TokenTree::Ident(assoc_type)) + if local_assoc_types.contains(assoc_type) => + { + ident.to_token_stream() + } + _ => replaced_ident, + } + } + _ => replaced_ident, + } + } + _ => replaced_ident, + }; + + result_stream.extend(replaced); + } else { + result_stream.push(TokenTree::Ident(ident)); + } + } + TokenTree::Group(group) => { + let replaced_stream = replace_self_type_in_token_stream( + group.stream(), + replaced_ident.clone(), + local_assoc_types, + ); + 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() +} diff --git a/crates/cgp-macro-core/src/visitors/replace_self/replace_self_value.rs b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_value.rs new file mode 100644 index 00000000..9e144a5a --- /dev/null +++ b/crates/cgp-macro-core/src/visitors/replace_self/replace_self_value.rs @@ -0,0 +1,66 @@ +use proc_macro2::{Group, TokenStream, TokenTree}; +use quote::format_ident; +use syn::visit_mut::{self, VisitMut}; +use syn::{Expr, Ident, ItemFn, Macro, Path}; + +pub struct ReplaceSelfValueVisitor<'a> { + pub replaced_ident: &'a Ident, +} + +impl VisitMut for ReplaceSelfValueVisitor<'_> { + fn visit_expr_mut(&mut self, expr: &mut Expr) { + match expr { + Expr::Path(expr_path) + if expr_path.qself.is_none() && expr_path.path.is_ident("self") => + { + expr_path.path = Path::from(self.replaced_ident.clone()); + } + _ => visit_mut::visit_expr_mut(self, expr), + } + } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + // Macro bodies are opaque to VisitMut, so fall back to token-level replacement. + mac.tokens = replace_self_value_in_token_stream(mac.tokens.clone(), self.replaced_ident); + } + + fn visit_item_fn_mut(&mut self, _: &mut ItemFn) { + // Nested fn items don't capture `self` from the outer scope; stop recursion. + } +} + +pub fn replace_self_value_in_token_stream( + stream: TokenStream, + replaced_ident: &Ident, +) -> TokenStream { + let self_ident = format_ident!("self"); + + let mut result_stream: Vec = 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_value_in_token_stream(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() +} diff --git a/crates/cgp-macro-core/src/visitors/substitute_abstract_type.rs b/crates/cgp-macro-core/src/visitors/substitute_abstract_type.rs new file mode 100644 index 00000000..59dbfd39 --- /dev/null +++ b/crates/cgp-macro-core/src/visitors/substitute_abstract_type.rs @@ -0,0 +1,28 @@ +use syn::visit_mut::VisitMut; +use syn::{PathArguments, Type, TypePath, parse_quote, visit_mut}; + +use crate::types::attributes::UseTypeAttribute; + +pub struct SubstituteAbstractType<'a> { + pub type_spec: &'a UseTypeAttribute, +} + +impl VisitMut for SubstituteAbstractType<'_> { + fn visit_type_mut(&mut self, ty: &mut Type) { + if let Type::Path(TypePath { qself: None, path }) = ty + && path.leading_colon.is_none() + && path.segments.len() == 1 + { + let segment = &path.segments[0]; + if matches!(segment.arguments, PathArguments::None) + && let Some(replacement_ident) = self.type_spec.replace_ident(&segment.ident) + { + let trait_path = &self.type_spec.trait_path; + let context_type = &self.type_spec.context_type; + *ty = parse_quote! { <#context_type as #trait_path>::#replacement_ident }; + return; + } + } + visit_mut::visit_type_mut(self, ty); + } +} diff --git a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs deleted file mode 100644 index d958f69a..00000000 --- a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs +++ /dev/null @@ -1,24 +0,0 @@ -use quote::ToTokens; -use syn::{ItemImpl, parse2}; - -use crate::cgp_fn::{UseTypeSpec, derive_use_type_predicates, substitute_abstract_types}; - -pub fn apply_use_type_attributes_to_item_impl( - item_impl: &ItemImpl, - use_type_specs: &[UseTypeSpec], -) -> syn::Result { - let mut item_impl: ItemImpl = parse2(substitute_abstract_types( - use_type_specs, - item_impl.to_token_stream(), - ))?; - - let predicates = derive_use_type_predicates(use_type_specs)?; - - item_impl - .generics - .make_where_clause() - .predicates - .extend(predicates); - - Ok(item_impl) -} diff --git a/crates/cgp-macro-lib/src/cgp_fn/attributes.rs b/crates/cgp-macro-lib/src/cgp_fn/attributes.rs deleted file mode 100644 index 9f6e30f6..00000000 --- a/crates/cgp-macro-lib/src/cgp_fn/attributes.rs +++ /dev/null @@ -1,56 +0,0 @@ -use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::{Attribute, GenericParam, TypeParamBound, WherePredicate}; - -use crate::cgp_fn::{FunctionAttributes, UseTypeSpec}; -use crate::cgp_impl::UseProviderSpec; -use crate::parse::SimpleType; - -pub fn parse_function_attributes( - attributes: Vec, -) -> syn::Result<(FunctionAttributes, Vec)> { - let mut parsed_attributes = FunctionAttributes::default(); - let mut rest_attributes = Vec::new(); - - 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::::parse_terminated)?; - - parsed_attributes.extend.extend(extend_bound); - } else if ident == "extend_where" { - let where_predicates = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - - parsed_attributes.extend_where.extend(where_predicates); - } else if ident == "uses" { - let uses = - attribute.parse_args_with(Punctuated::::parse_terminated)?; - - parsed_attributes.uses.extend(uses); - } else if ident == "use_type" { - let use_type = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - - parsed_attributes.use_type.extend(use_type); - } else if ident == "use_provider" { - let use_provider = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - - parsed_attributes.use_provider.extend(use_provider); - } else if ident == "impl_generics" { - let impl_generics = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - - parsed_attributes.impl_generics.extend(impl_generics); - } else { - rest_attributes.push(attribute); - } - } else { - rest_attributes.push(attribute); - } - } - - Ok((parsed_attributes, rest_attributes)) -} diff --git a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs deleted file mode 100644 index 17b90993..00000000 --- a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs +++ /dev/null @@ -1,30 +0,0 @@ -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> { - let mut constraints: Punctuated = 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) -} diff --git a/crates/cgp-macro-lib/src/cgp_fn/derive.rs b/crates/cgp-macro-lib/src/cgp_fn/derive.rs index 6850c04f..1462bec2 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/derive.rs @@ -1,29 +1,27 @@ use core::mem; +use cgp_macro_core::functions::extract_and_parse_implicit_args; +use cgp_macro_core::types::attributes::FunctionAttributes; use proc_macro2::TokenStream; use quote::quote; 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_and_parse_implicit_args, parse_function_attributes}; pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result { let visibility = item_fn.vis.clone(); item_fn.vis = Visibility::Inherited; let implicit_args = extract_and_parse_implicit_args(&mut item_fn.sig.inputs)?; + implicit_args.prepend_to_block(&mut item_fn.block)?; - let (attributes, trait_attributes) = - parse_function_attributes(core::mem::take(&mut item_fn.attrs))?; - - inject_implicit_args(&implicit_args, &mut item_fn.block)?; + let attributes = FunctionAttributes::parse(core::mem::take(&mut item_fn.attrs))?; let generics = mem::take(&mut item_fn.sig.generics); let mut item_trait = derive_item_trait(trait_ident, &item_fn, &generics, &attributes)?; - item_trait.attrs.extend(trait_attributes.clone()); + item_trait.attrs.extend(attributes.raw_attributes.clone()); let mut item_impl = derive_item_impl( trait_ident, @@ -33,7 +31,7 @@ pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result syn::Result<()> { - for arg in args.iter().rev() { - inject_implicit_arg(arg, body)?; - } - Ok(()) -} - -pub fn inject_implicit_arg(arg: &ImplicitArgField, body: &mut Block) -> syn::Result<()> { - let field_name = &arg.field_name; - let arg_type = &arg.arg_type; - - let field_symbol = symbol_from_string(&field_name.to_string())?; - - let call_expr = if arg.field_mut.is_none() { - quote! { - self.get_field(::core::marker::PhantomData::< #field_symbol >) - } - } else { - quote! { - self.get_field_mut(::core::marker::PhantomData::< #field_symbol >) - } - }; - - let call_expr = extend_call_expr(call_expr, &arg.field_mode, &arg.field_mut); - - let statement = parse2(quote! { - let #field_name: #arg_type = #call_expr; - })?; - - body.stmts.insert(0, statement); - - Ok(()) -} diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs index d17b80f0..36acbabe 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs @@ -1,23 +1,22 @@ +use cgp_macro_core::traits::AddTypeParamBounds; +use cgp_macro_core::types::attributes::FunctionAttributes; +use cgp_macro_core::types::implicits::ImplicitArgFields; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Plus; -use syn::{Generics, Ident, ItemFn, ItemImpl, TypeParamBound, parse_quote, parse2}; - -use crate::cgp_fn::{ - FunctionAttributes, ImplicitArgField, apply_use_type_attributes_to_item_impl, - build_implicit_args_bounds, -}; -use crate::cgp_impl::derive_provider_bounds; +use syn::{Generics, Ident, ItemFn, ItemImpl, Type, TypeParamBound, parse_quote, parse2}; pub fn derive_item_impl( trait_ident: &Ident, item_fn: &ItemFn, - implicit_args: &[ImplicitArgField], + implicit_args: &ImplicitArgFields, generics: &Generics, attributes: &FunctionAttributes, ) -> syn::Result { let type_generics = generics.split_for_impl().1; + let self_type: Type = parse_quote!(Self); + let mut item_impl: ItemImpl = parse2(quote! { impl #trait_ident #type_generics for __Context__ { #item_fn @@ -62,27 +61,12 @@ pub fn derive_item_impl( .extend(attributes.extend_where.clone()); } - if !implicit_args.is_empty() { - let where_clause = item_impl.generics.make_where_clause(); - let bounds = build_implicit_args_bounds(implicit_args)?; + implicit_args.add_type_param_bounds(&self_type, &mut item_impl.generics)?; - 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.use_provider.is_empty() { - let where_clause = item_impl.generics.make_where_clause(); - - for spec in attributes.use_provider.iter() { - let provider_bounds = derive_provider_bounds(&parse_quote! { Self }, spec)?; - where_clause.predicates.push(provider_bounds); - } - } + attributes.use_type.transform_item_impl(&mut item_impl)?; + attributes + .use_provider + .add_type_param_bounds(&self_type, &mut item_impl.generics)?; Ok(item_impl) } diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_trait.rs b/crates/cgp-macro-lib/src/cgp_fn/item_trait.rs index eb953910..5bc46e72 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_trait.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_trait.rs @@ -1,7 +1,6 @@ -use quote::{ToTokens, quote}; -use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, parse_quote, parse2}; - -use crate::cgp_fn::{FunctionAttributes, UseTypeSpec, substitute_abstract_types}; +use cgp_macro_core::types::attributes::FunctionAttributes; +use quote::quote; +use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, parse2}; pub fn derive_item_trait( trait_ident: &Ident, @@ -35,31 +34,7 @@ pub fn derive_item_trait( .extend(attributes.extend_where.clone()); } - if !attributes.use_type.is_empty() { - item_trait = expand_use_type_attributes_on_trait(&item_trait, &attributes.use_type)?; - } - - Ok(item_trait) -} - -pub fn expand_use_type_attributes_on_trait( - item_trait: &ItemTrait, - use_type_specs: &[UseTypeSpec], -) -> syn::Result { - let mut item_trait: ItemTrait = parse2(substitute_abstract_types( - use_type_specs, - item_trait.to_token_stream(), - ))?; - - for use_type in use_type_specs.iter() { - if use_type.context_type != parse_quote! { Self } { - continue; - } - - item_trait - .supertraits - .push(parse2(use_type.trait_path.to_token_stream())?); - } + attributes.use_type.transform_item_trait(&mut item_trait)?; Ok(item_trait) } diff --git a/crates/cgp-macro-lib/src/cgp_fn/mod.rs b/crates/cgp-macro-lib/src/cgp_fn/mod.rs index 6845f094..939c781c 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/mod.rs @@ -1,24 +1,5 @@ -mod apply_type; -mod attributes; -mod bounds; mod derive; -mod fn_body; mod item_impl; mod item_trait; -mod parse_implicits; -mod spec; -mod substitute_type; -mod type_predicates; -mod use_type; -pub use apply_type::*; -pub use attributes::*; -pub use bounds::*; pub use derive::*; -pub use fn_body::*; -pub use item_trait::*; -pub use parse_implicits::*; -pub use spec::*; -pub use substitute_type::*; -pub use type_predicates::*; -pub use use_type::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/spec.rs b/crates/cgp-macro-lib/src/cgp_fn/spec.rs deleted file mode 100644 index 3dc2ad94..00000000 --- a/crates/cgp-macro-lib/src/cgp_fn/spec.rs +++ /dev/null @@ -1,26 +0,0 @@ -use syn::token::Mut; -use syn::{GenericParam, Ident, Type, TypeParamBound, WherePredicate}; - -use crate::cgp_fn::UseTypeSpec; -use crate::cgp_impl::UseProviderSpec; -use crate::derive_getter::FieldMode; -use crate::parse::SimpleType; - -#[derive(Clone, Eq, PartialEq)] -pub struct ImplicitArgField { - pub field_name: Ident, - pub field_type: Type, - pub field_mut: Option, - pub field_mode: FieldMode, - pub arg_type: Type, -} - -#[derive(Default)] -pub struct FunctionAttributes { - pub extend: Vec, - pub extend_where: Vec, - pub uses: Vec, - pub use_type: Vec, - pub use_provider: Vec, - pub impl_generics: Vec, -} diff --git a/crates/cgp-macro-lib/src/cgp_fn/substitute_type.rs b/crates/cgp-macro-lib/src/cgp_fn/substitute_type.rs deleted file mode 100644 index 5e3214a5..00000000 --- a/crates/cgp-macro-lib/src/cgp_fn/substitute_type.rs +++ /dev/null @@ -1,65 +0,0 @@ -use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::quote; - -use crate::cgp_fn::UseTypeSpec; - -pub fn substitute_abstract_types(type_specs: &[UseTypeSpec], body: TokenStream) -> TokenStream { - let mut out = body; - for spec in type_specs.iter().rev() { - out = substitute_abstract_type(spec, out); - } - out -} - -pub fn substitute_abstract_type(type_specs: &UseTypeSpec, body: TokenStream) -> TokenStream { - let mut out = TokenStream::new(); - let mut last_token_was_colon = false; - let mut last_two_tokens_was_colon = false; - - for token_tree in body.into_iter() { - let token_is_colon = if let TokenTree::Punct(punct) = &token_tree - && punct.as_char() == ':' - { - true - } else { - false - }; - - match token_tree { - TokenTree::Group(group) => { - let new_stream = substitute_abstract_type(type_specs, group.stream()); - out.extend([TokenTree::Group(Group::new(group.delimiter(), new_stream))]); - } - TokenTree::Ident(ident) => { - if !last_two_tokens_was_colon - && let Some(replacement_ident) = type_specs.replace_ident(&ident) - { - let trait_path = &type_specs.trait_path; - let context_type = &type_specs.context_type; - - out.extend(quote! { - < #context_type as #trait_path > :: #replacement_ident - }); - } else { - out.extend([TokenTree::Ident(ident)]); - } - } - TokenTree::Punct(punct) => { - out.extend([TokenTree::Punct(punct)]); - } - TokenTree::Literal(literal) => { - out.extend([TokenTree::Literal(literal)]); - } - } - - if token_is_colon { - last_two_tokens_was_colon = last_token_was_colon; - last_token_was_colon = true; - } else { - last_two_tokens_was_colon = false; - last_token_was_colon = false; - } - } - - out -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/attributes.rs b/crates/cgp-macro-lib/src/cgp_impl/attributes.rs deleted file mode 100644 index 7b81e3a3..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/attributes.rs +++ /dev/null @@ -1,46 +0,0 @@ -use core::mem; - -use syn::Attribute; -use syn::punctuated::Punctuated; -use syn::token::Comma; - -use crate::cgp_fn::UseTypeSpec; -use crate::cgp_impl::use_provider::UseProviderSpec; -use crate::parse::SimpleType; - -pub fn parse_impl_attributes(attributes: &mut Vec) -> syn::Result { - 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::::parse_terminated)?; - parsed_attributes.uses.extend(uses); - } else if ident == "use_type" { - let use_type = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - parsed_attributes.use_type.extend(use_type); - } else if ident == "use_provider" { - let use_provider = attribute - .parse_args_with(Punctuated::::parse_terminated)?; - parsed_attributes.use_provider.extend(use_provider); - } else { - attributes.push(attribute); - } - } else { - attributes.push(attribute); - } - } - - Ok(parsed_attributes) -} - -#[derive(Default)] -pub struct ImplAttributes { - pub uses: Vec, - pub use_type: Vec, - pub use_provider: Vec, -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs deleted file mode 100644 index d0460e67..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ /dev/null @@ -1,98 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Plus; -use syn::{Error, ItemImpl, TypeParamBound, parse_quote, 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::provider_bounds::derive_provider_bounds; -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 { - 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 = 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 - })?); - } - - if !attributes.use_provider.is_empty() { - let where_clause = item_impl.generics.make_where_clause(); - - for spec in attributes.use_provider.iter() { - let provider_bounds = derive_provider_bounds(&parse_quote! { Self }, spec)?; - where_clause.predicates.push(provider_bounds); - } - } - - if spec.provider_type == parse_quote! { Self } { - if item_impl.trait_.is_none() { - return Err(Error::new( - item_impl.span(), - "Expected context type to be specified", - )); - } - - Ok(quote! { - #item_impl - }) - } else { - let (_context_type, 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 - }) - } -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs deleted file mode 100644 index 8f351bdc..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs +++ /dev/null @@ -1,24 +0,0 @@ -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> { - 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) -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs deleted file mode 100644 index 79e49bc4..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod attributes; -mod derive; -mod implicit_args; -mod provider_bounds; -mod provider_impl; -mod spec; -mod transform; -mod use_provider; - -pub use derive::*; -pub use provider_bounds::*; -pub use provider_impl::*; -pub use spec::*; -pub use transform::*; -pub use use_provider::*; diff --git a/crates/cgp-macro-lib/src/cgp_impl/provider_bounds.rs b/crates/cgp-macro-lib/src/cgp_impl/provider_bounds.rs deleted file mode 100644 index b518a4ef..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/provider_bounds.rs +++ /dev/null @@ -1,41 +0,0 @@ -use quote::{ToTokens, quote}; -use syn::punctuated::Punctuated; -use syn::token::Plus; -use syn::{Type, TypeParamBound, WherePredicate, parse_quote, parse2}; - -use crate::cgp_impl::use_provider::UseProviderSpec; - -pub fn derive_provider_bounds( - context_type: &Type, - spec: &UseProviderSpec, -) -> syn::Result { - let context_type = if spec.context_type == parse_quote! { Self } { - context_type - } else { - &spec.context_type - }; - - let provider_type = &spec.provider_type; - let mut bounds = Punctuated::::new(); - - for bound in &spec.provider_trait_bounds { - let trait_ident = &bound.name; - let mut m_generics = bound.generics.clone(); - - let generics = m_generics.get_or_insert_with(|| parse_quote!(<>)); - generics - .args - .insert(0, parse2(context_type.to_token_stream())?); - - let trait_bound = parse2(quote! { - #trait_ident #generics - })?; - bounds.push(trait_bound); - } - - let predicate = parse2(quote! { - #provider_type: #bounds - })?; - - Ok(predicate) -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs b/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs deleted file mode 100644 index 1d5374df..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs +++ /dev/null @@ -1,42 +0,0 @@ -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<(Type, 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(); - let item_trait = transform_impl_trait( - &item_impl, - &consumer_trait_path, - provider_type, - context_type, - )?; - - Ok((context_type.clone(), item_trait)) - } - 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__ }); - - let item_trait = transform_impl_trait( - &item_impl, - &consumer_trait_path, - provider_type, - &context_type, - )?; - - Ok((context_type, item_trait)) - } - } -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/spec.rs b/crates/cgp-macro-lib/src/cgp_impl/spec.rs deleted file mode 100644 index 5c7dcc34..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/spec.rs +++ /dev/null @@ -1,41 +0,0 @@ -use syn::parse::discouraged::Speculative; -use syn::parse::{Parse, ParseStream}; -use syn::token::Colon; -use syn::{Ident, Type}; - -pub struct ImplProviderSpec { - pub new_struct: bool, - pub provider_type: Type, - pub component_type: Option, -} - -impl Parse for ImplProviderSpec { - fn parse(input: ParseStream) -> syn::Result { - let new_struct = { - let fork = input.fork(); - let new_ident: Option = 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::>()? { - let component_type: Type = input.parse()?; - Some(component_type) - } else { - None - }; - - Ok(ImplProviderSpec { - new_struct, - provider_type, - component_type, - }) - } -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/transform.rs b/crates/cgp-macro-lib/src/cgp_impl/transform.rs deleted file mode 100644 index 57d804dc..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/transform.rs +++ /dev/null @@ -1,80 +0,0 @@ -use proc_macro2::Span; -use quote::{ToTokens, quote}; -use syn::token::For; -use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse2}; - -use crate::parse::SimpleType; -use crate::replace_self::{ - replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident, -}; - -pub fn transform_impl_trait( - item_impl: &ItemImpl, - consumer_trait_path: &SimpleType, - provider_type: &Type, - context_type: &Type, -) -> syn::Result { - let context_var = if let Ok(ident) = parse2::(context_type.to_token_stream()) { - to_snake_case_ident(&ident) - } else { - Ident::new("__context__", Span::call_site()) - }; - - let local_assoc_types: Vec = 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 = parse2(replace_self_type( - consumer_trait_path.to_token_stream(), - context_type.to_token_stream(), - &local_assoc_types, - ))?; - - 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) -} diff --git a/crates/cgp-macro-lib/src/cgp_impl/use_provider.rs b/crates/cgp-macro-lib/src/cgp_impl/use_provider.rs deleted file mode 100644 index 7f4d4225..00000000 --- a/crates/cgp-macro-lib/src/cgp_impl/use_provider.rs +++ /dev/null @@ -1,28 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::token::{Colon, Plus}; -use syn::{Type, parse_quote}; - -use crate::parse::SimpleType; - -pub struct UseProviderSpec { - pub context_type: Type, - pub provider_type: Type, - pub provider_trait_bounds: Punctuated, -} - -impl Parse for UseProviderSpec { - fn parse(input: ParseStream) -> syn::Result { - let context_type = parse_quote!(Self); - let provider_type = input.parse()?; - - let _: Colon = input.parse()?; - let provider_trait_bounds = Punctuated::parse_terminated(input)?; - - Ok(Self { - context_type, - provider_type, - provider_trait_bounds, - }) - } -} diff --git a/crates/cgp-macro-lib/src/derive_component/attributes.rs b/crates/cgp-macro-lib/src/derive_component/attributes.rs index 13d2a8f3..0ca2e2b4 100644 --- a/crates/cgp-macro-lib/src/derive_component/attributes.rs +++ b/crates/cgp-macro-lib/src/derive_component/attributes.rs @@ -1,12 +1,12 @@ use core::mem; +use cgp_macro_core::types::attributes::{UseTypeAttribute, UseTypeAttributes}; use syn::parse::Parse; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Attribute, TypeParamBound}; use crate::attributes::UseNamespaceAttribute; -use crate::cgp_fn::UseTypeSpec; pub fn parse_component_attributes( attributes: &mut Vec, @@ -23,7 +23,7 @@ pub fn parse_component_attributes( parsed_attributes.extend.extend(extend_bound); } else if ident == "use_type" { let use_type_specs = attribute - .parse_args_with(Punctuated::::parse_terminated)?; + .parse_args_with(Punctuated::::parse_terminated)?; for use_type_spec in use_type_specs.iter() { for type_ident in use_type_spec.type_idents.iter() { @@ -36,7 +36,7 @@ pub fn parse_component_attributes( } } - parsed_attributes.use_type.extend(use_type_specs); + parsed_attributes.use_type.attributes.extend(use_type_specs); } else if ident == "prefix" { let namespace_specs = attribute.parse_args_with(UseNamespaceAttribute::parse)?; parsed_attributes.namespace.push(namespace_specs); @@ -54,6 +54,6 @@ pub fn parse_component_attributes( #[derive(Default)] pub struct ComponentAttributes { pub extend: Vec, - pub use_type: Vec, + pub use_type: UseTypeAttributes, pub namespace: Vec, } diff --git a/crates/cgp-macro-lib/src/derive_component/derive.rs b/crates/cgp-macro-lib/src/derive_component/derive.rs index 1f024499..740dee69 100644 --- a/crates/cgp-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-macro-lib/src/derive_component/derive.rs @@ -1,3 +1,4 @@ +use cgp_macro_core::types::is_provider_for::derive_is_provider_for; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt, quote}; use syn::{ItemImpl, ItemStruct, ItemTrait, parse2}; @@ -12,7 +13,6 @@ use crate::derive_component::provider_impl::derive_provider_impl; use crate::derive_component::provider_trait::derive_provider_trait; use crate::derive_component::use_context_impl::derive_use_context_impl; use crate::derive_component::use_delegate_impl::derive_delegate_impl; -use crate::derive_provider::derive_is_provider_for; use crate::parse::ComponentSpec; pub fn derive_component_with_ast( diff --git a/crates/cgp-macro-lib/src/derive_component/preprocess.rs b/crates/cgp-macro-lib/src/derive_component/preprocess.rs index 26d14ca6..8702e446 100644 --- a/crates/cgp-macro-lib/src/derive_component/preprocess.rs +++ b/crates/cgp-macro-lib/src/derive_component/preprocess.rs @@ -1,6 +1,5 @@ use syn::ItemTrait; -use crate::cgp_fn::expand_use_type_attributes_on_trait; use crate::derive_component::attributes::ComponentAttributes; pub fn preprocess_consumer_trait( @@ -9,10 +8,7 @@ pub fn preprocess_consumer_trait( ) -> syn::Result<()> { consumer_trait.supertraits.extend(attributes.extend.clone()); - if !attributes.use_type.is_empty() { - *consumer_trait = - expand_use_type_attributes_on_trait(consumer_trait, &attributes.use_type)?; - } + attributes.use_type.transform_item_trait(consumer_trait)?; Ok(()) } diff --git a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs index 0e6a1f3f..93084d0c 100644 --- a/crates/cgp-macro-lib/src/derive_component/provider_trait.rs +++ b/crates/cgp-macro-lib/src/derive_component/provider_trait.rs @@ -1,33 +1,36 @@ use alloc::vec::Vec; -use quote::{ToTokens, quote}; +use cgp_macro_core::functions::to_snake_case_ident; +use cgp_macro_core::visitors::{ + ReplaceSelfReceiverVisitor, ReplaceSelfTypeVisitor, ReplaceSelfValueVisitor, +}; +use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; -use syn::{Ident, ItemTrait, TraitItem, TypeParamBound, parse2}; +use syn::visit_mut::VisitMut; +use syn::{Ident, ItemTrait, TraitItem, Type, TypeParamBound, parse_quote, parse2}; use crate::parse::parse_is_provider_params; -use crate::replace_self::{ - iter_parse_and_replace_self_type, parse_and_replace_self_type, - replace_self_receiver_in_signature, replace_self_var, to_snake_case_ident, -}; pub fn derive_provider_trait( component_name: &Ident, component_params: &Punctuated, consumer_trait: &ItemTrait, provider_name: &Ident, - context_type: &Ident, + context_type_ident: &Ident, ) -> syn::Result { let mut provider_trait = consumer_trait.clone(); provider_trait.ident = provider_name.clone(); + let context_type: Type = parse_quote!(#context_type_ident); + // Add generic parameter `Context` to the front of generics { provider_trait .generics .params - .insert(0, parse2(quote!(#context_type))?); + .insert(0, parse2(quote!(#context_type_ident))?); } let local_assoc_types: Vec = provider_trait @@ -43,69 +46,44 @@ pub fn derive_provider_trait( .collect(); // Turn the supertrait constraints into `Context` constraints in the `where` clause - { - let context_constraints = iter_parse_and_replace_self_type( - provider_trait.supertraits.clone(), - context_type, - &local_assoc_types, - )?; - - let is_provider_params = parse_is_provider_params(&consumer_trait.generics)?; - - let provider_supertrait: TypeParamBound = parse2(quote!( - IsProviderFor< #component_name < #component_params >, #context_type, ( #is_provider_params ) > - ))?; - - provider_trait.supertraits = Punctuated::from_iter([provider_supertrait]); - - if !context_constraints.is_empty() { - match &mut provider_trait.generics.where_clause { - Some(where_clause) => { - let mut predicates = iter_parse_and_replace_self_type( - where_clause.predicates.clone(), - context_type, - &local_assoc_types, - )?; - - predicates.push(parse2(quote! { - #context_type : #context_constraints - })?); - - where_clause.predicates = predicates; - } - _ => { - provider_trait.generics.where_clause = Some(parse2(quote! { - where #context_type : #context_constraints - })?); - } - } - } + if !provider_trait.supertraits.is_empty() { + let supertraits = &provider_trait.supertraits; + + provider_trait + .generics + .make_where_clause() + .predicates + .push(parse_quote! { + #context_type_ident : #supertraits + }); } - // Replace self type and argument into context type argument - { - let context_var = to_snake_case_ident(context_type); - - for item in provider_trait.items.iter_mut() { - let mut replaced_item = - parse_and_replace_self_type(item, context_type, &local_assoc_types)?; - - if let TraitItem::Fn(func) = &mut replaced_item { - replace_self_receiver_in_signature( - &mut func.sig, - &context_var, - context_type.to_token_stream(), - ); - - if let Some(block) = &mut func.default { - let replaced = replace_self_var(block.to_token_stream(), &context_var); - *block = parse2(replaced)?; - } - } + let is_provider_params = parse_is_provider_params(&consumer_trait.generics)?; + + let provider_supertrait: TypeParamBound = parse2(quote!( + IsProviderFor< #component_name < #component_params >, #context_type_ident, ( #is_provider_params ) > + ))?; + + provider_trait.supertraits = Punctuated::from_iter([provider_supertrait]); + + let context_value_ident = to_snake_case_ident(context_type_ident); + + ReplaceSelfTypeVisitor { + replaced_type: &context_type, + skip_assoc_types: &local_assoc_types, + } + .visit_item_trait_mut(&mut provider_trait); + + ReplaceSelfReceiverVisitor { + replaced_ident: &context_value_ident, + replaced_type: &context_type, + } + .visit_item_trait_mut(&mut provider_trait); - *item = replaced_item; - } + ReplaceSelfValueVisitor { + replaced_ident: &context_value_ident, } + .visit_item_trait_mut(&mut provider_trait); Ok(provider_trait) } diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index 730a643e..777b2ef9 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -1,14 +1,10 @@ -use alloc::string::ToString; - +use cgp_macro_core::types::field::{FieldName, HasFieldBound}; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; -use syn::{Ident, ItemImpl, ItemTrait, TraitItemType, parse2}; +use syn::{Ident, ItemImpl, ItemTrait, TraitItemType, parse_quote, parse2}; use crate::derive_getter::getter_field::GetterField; -use crate::derive_getter::{ - ContextArg, ReceiverMode, derive_getter_constraint, derive_getter_method, -}; -use crate::symbol::symbol_from_string; +use crate::derive_getter::{ContextArg, ReceiverMode, derive_getter_method}; use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_blanket_impl( @@ -64,24 +60,31 @@ pub fn derive_blanket_impl( ), }; - let field_symbol = symbol_from_string(&field.field_name.to_string())?; + let field_name = FieldName::from(field.field_name.clone()); + let tag_type = parse_quote!(#field_name); let method = derive_getter_method( &context_arg, field, - Some(quote! { ::< #field_symbol > }), + Some(quote! { ::< #field_name > }), None, ); items.extend(method); - let constraint = derive_getter_constraint( - &field.field_type, - &field.receiver_mut, - &field.field_mode, - quote! { #field_symbol }, - &field_assoc_type.as_ref().map(|item| item.ident.clone()), - )?; + let field_type = if let Some(trait_item) = &field_assoc_type { + let trait_item_ident = &trait_item.ident; + parse_quote!(#trait_item_ident) + } else { + field.field_type.clone() + }; + + let constraint = HasFieldBound { + field_type, + field_mut: field.receiver_mut, + field_mode: field.field_mode.clone(), + tag_type, + }; where_clause.predicates.push(parse2(quote! { #receiver_type: #constraint diff --git a/crates/cgp-macro-lib/src/derive_getter/constraint.rs b/crates/cgp-macro-lib/src/derive_getter/constraint.rs deleted file mode 100644 index 6c28596c..00000000 --- a/crates/cgp-macro-lib/src/derive_getter/constraint.rs +++ /dev/null @@ -1,37 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::token::Mut; -use syn::{Ident, Type, TypeParamBound, parse_quote, parse2}; - -use crate::derive_getter::FieldMode; - -pub fn derive_getter_constraint( - field_type: &Type, - field_mut: &Option, - field_mode: &FieldMode, - field_symbol: TokenStream, - field_assoc_type: &Option, -) -> syn::Result { - let field_type = match field_assoc_type { - Some(field_assoc_type) => parse_quote! { #field_assoc_type }, - None => field_type.clone(), - }; - - let constraint = if field_mut.is_none() { - if let FieldMode::Slice = field_mode { - quote! { - HasField< #field_symbol, Value: AsRef< [ #field_type ] > + 'static > - } - } else { - quote! { - HasField< #field_symbol, Value = #field_type > - } - } - } else { - quote! { - HasFieldMut< #field_symbol, Value = #field_type > - } - }; - - parse2(constraint) -} diff --git a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs index d300d81c..e4758654 100644 --- a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs @@ -1,3 +1,4 @@ +use cgp_macro_core::types::getter::FieldMode; use syn::token::Mut; use syn::{Ident, Type}; @@ -11,16 +12,6 @@ pub struct GetterField { pub receiver_mode: ReceiverMode, } -#[derive(Clone, Eq, PartialEq)] -pub enum FieldMode { - Reference, - OptionRef, - MRef, - Str, - Copy, - Slice, -} - pub enum ReceiverMode { SelfReceiver, Type(Box), diff --git a/crates/cgp-macro-lib/src/derive_getter/method.rs b/crates/cgp-macro-lib/src/derive_getter/method.rs index 0ec82fce..23b804c4 100644 --- a/crates/cgp-macro-lib/src/derive_getter/method.rs +++ b/crates/cgp-macro-lib/src/derive_getter/method.rs @@ -1,9 +1,10 @@ +use cgp_macro_core::types::getter::FieldMode; use proc_macro2::TokenStream; use quote::quote; use syn::Ident; use syn::token::Mut; -use crate::derive_getter::{FieldMode, GetterField}; +use crate::derive_getter::GetterField; pub enum ContextArg { SelfArg, diff --git a/crates/cgp-macro-lib/src/derive_getter/mod.rs b/crates/cgp-macro-lib/src/derive_getter/mod.rs index 8d02d5ab..8b66cfa1 100644 --- a/crates/cgp-macro-lib/src/derive_getter/mod.rs +++ b/crates/cgp-macro-lib/src/derive_getter/mod.rs @@ -1,5 +1,4 @@ mod blanket; -mod constraint; mod getter_field; mod method; mod parse; @@ -8,7 +7,6 @@ mod use_fields; mod with_provider; pub use blanket::*; -pub use constraint::*; pub use getter_field::*; pub use method::*; pub use parse::*; diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index bfac7468..76aab53b 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -1,17 +1,18 @@ use alloc::vec::Vec; -use quote::{ToTokens, quote}; +use cgp_macro_core::functions::{parse_field_type, parse_single_segment_type_path}; +use cgp_macro_core::visitors::ReplaceSelfTypeVisitor; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::{Comma, Mut}; +use syn::visit_mut::VisitMut; use syn::{ Error, FnArg, GenericArgument, Ident, ItemTrait, PathArguments, PathSegment, ReturnType, - Signature, TraitItem, TraitItemFn, TraitItemType, Type, TypePath, parse_quote, parse2, + Signature, TraitItem, TraitItemFn, TraitItemType, Type, parse_quote, }; +use crate::derive_getter::ReceiverMode; use crate::derive_getter::getter_field::GetterField; -use crate::derive_getter::{FieldMode, ReceiverMode}; -use crate::replace_self::replace_self_type; pub fn parse_getter_fields( context_type: &Ident, @@ -228,11 +229,14 @@ fn parse_receiver(context_ident: &Ident, arg: &FnArg) -> syn::Result<(ReceiverMo } FnArg::Typed(arg) => match arg.ty.as_ref() { Type::Reference(ty) => { - let receiver = parse2(replace_self_type( - ty.elem.to_token_stream(), - context_ident.to_token_stream(), - &Vec::new(), - ))?; + let mut receiver = ty.elem.clone(); + + ReplaceSelfTypeVisitor { + replaced_type: &parse_quote!(#context_ident), + skip_assoc_types: &Vec::new(), + } + .visit_type_mut(&mut receiver); + Ok((ReceiverMode::Type(receiver), ty.mutability)) } _ => Err(Error::new( @@ -249,86 +253,24 @@ fn parse_return_type( field_assoc_type: &Option, ) -> syn::Result { match return_type { - ReturnType::Type(_, ty) => parse2(replace_self_type( - ty.to_token_stream(), - context_type.to_token_stream(), - &field_assoc_type.iter().cloned().collect::>(), - )), - _ => Err(Error::new( - return_type.span(), - "return type must be specified", - )), - } -} + ReturnType::Type(_, ty) => { + let mut replaced_type = ty.as_ref().clone(); -pub fn parse_field_type( - return_type: &Type, - receiver_mut: &Option, -) -> 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() - ), - )); + ReplaceSelfTypeVisitor { + replaced_type: &parse_quote!(#context_type), + skip_assoc_types: &Vec::from_iter(field_assoc_type.clone()), } + .visit_type_mut(&mut replaced_type); - 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)) - } + Ok(replaced_type) } _ => Err(Error::new( return_type.span(), - "return type must be a reference", + "return type must be specified", )), } } -fn parse_single_segment_type_path(type_path: &TypePath) -> syn::Result<&PathSegment> { - let [segment]: [&PathSegment; 1] = type_path - .path - .segments - .iter() - .collect::>() - .try_into() - .map_err(|_| { - Error::new( - type_path.span(), - "type path must contain exactly one path segment", - ) - })?; - - Ok(segment) -} - fn try_parse_phantom_arg_type_path(segment: &PathSegment) -> Option { if segment.ident == "PhantomData" && let PathArguments::AngleBracketed(args) = &segment.arguments @@ -339,35 +281,3 @@ fn try_parse_phantom_arg_type_path(segment: &PathSegment) -> Option { None } - -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 -} - -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 -} diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index cddc1977..2c5da4f9 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -1,13 +1,14 @@ +use cgp_macro_core::types::field::HasFieldBound; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; use syn::punctuated::Punctuated; use syn::token::Plus; -use syn::{Generics, ItemImpl, ItemTrait, TraitItemType, TypeParamBound, parse2}; +use syn::{ + Generics, ItemImpl, ItemTrait, TraitItemType, Type, TypeParamBound, parse_quote, parse2, +}; use crate::derive_getter::getter_field::GetterField; -use crate::derive_getter::{ - ContextArg, ReceiverMode, derive_getter_constraint, derive_getter_method, -}; +use crate::derive_getter::{ContextArg, ReceiverMode, derive_getter_method}; use crate::parse::ComponentSpec; use crate::type_component::get_bounds_and_replace_self_assoc_type; @@ -27,7 +28,7 @@ pub fn derive_use_field_impl( let mut field_constraints: Punctuated = Punctuated::default(); - let tag_type = quote! { __Tag__ }; + let tag_type: Type = parse_quote! { __Tag__ }; let mut items = TokenStream::new(); @@ -61,15 +62,21 @@ pub fn derive_use_field_impl( None, )); - let constraint = derive_getter_constraint( - &field.field_type, - &field.receiver_mut, - &field.field_mode, - quote! { #tag_type }, - &field_assoc_type.as_ref().map(|item| item.ident.clone()), - )?; + let field_type = if let Some(trait_item) = &field_assoc_type { + let trait_item_ident = &trait_item.ident; + parse_quote!(#trait_item_ident) + } else { + field.field_type.clone() + }; + + let constraint = HasFieldBound { + field_type, + field_mut: field.receiver_mut, + field_mode: field.field_mode.clone(), + tag_type: tag_type.clone(), + }; - field_constraints.push(constraint); + field_constraints.push(parse_quote!(#constraint)); let mut where_clause = provider_generics.make_where_clause().clone(); where_clause @@ -81,7 +88,7 @@ pub fn derive_use_field_impl( let impl_generics = { let mut generics: Generics = parse2(impl_generics.to_token_stream())?; - generics.params.push(parse2(tag_type.clone())?); + generics.params.push(parse_quote!(#tag_type)); generics }; diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 73f6849c..901e5a38 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -1,15 +1,11 @@ -use alloc::string::ToString; - +use cgp_macro_core::types::field::{HasFieldBound, Symbol}; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; -use syn::{ItemImpl, ItemTrait, TraitItemType, parse2}; +use syn::{ItemImpl, ItemTrait, TraitItemType, Type, parse_quote, parse2}; use crate::derive_getter::getter_field::GetterField; -use crate::derive_getter::{ - ContextArg, ReceiverMode, derive_getter_constraint, derive_getter_method, -}; +use crate::derive_getter::{ContextArg, ReceiverMode, derive_getter_method}; use crate::parse::ComponentSpec; -use crate::symbol::symbol_from_string; use crate::type_component::get_bounds_and_replace_self_assoc_type; pub fn derive_use_fields_impl( @@ -55,24 +51,31 @@ pub fn derive_use_fields_impl( ReceiverMode::Type(ty) => ty.to_token_stream(), }; - let field_symbol = symbol_from_string(&field.field_name.to_string())?; + let field_name = Symbol::new(field.field_name.clone()); + let tag_type: Type = parse_quote!(#field_name); let method = derive_getter_method( &ContextArg::Ident(receiver_type.clone()), field, - Some(quote! { ::< #field_symbol > }), + Some(quote! { ::< #field_name > }), None, ); items.extend(method); - let constraint = derive_getter_constraint( - &field.field_type, - &field.receiver_mut, - &field.field_mode, - quote! { #field_symbol }, - &field_assoc_type.as_ref().map(|item| item.ident.clone()), - )?; + let field_type = if let Some(trait_item) = &field_assoc_type { + let trait_item_ident = &trait_item.ident; + parse_quote!(#trait_item_ident) + } else { + field.field_type.clone() + }; + + let constraint = HasFieldBound { + field_type, + field_mut: field.receiver_mut, + field_mode: field.field_mode.clone(), + tag_type: tag_type.clone(), + }; where_clause .predicates diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index 6f470d6a..9c5ec55c 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -1,9 +1,10 @@ +use cgp_macro_core::types::getter::FieldMode; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, quote}; use syn::{Generics, Ident, ItemImpl, ItemTrait, TraitItemType, parse_quote, parse2}; use crate::derive_getter::getter_field::GetterField; -use crate::derive_getter::{ContextArg, FieldMode, ReceiverMode, derive_getter_method}; +use crate::derive_getter::{ContextArg, ReceiverMode, derive_getter_method}; use crate::parse::ComponentSpec; use crate::type_component::get_bounds_and_replace_self_assoc_type; diff --git a/crates/cgp-macro-lib/src/derive_provider/component_name.rs b/crates/cgp-macro-lib/src/derive_provider/component_name.rs deleted file mode 100644 index 9179695a..00000000 --- a/crates/cgp-macro-lib/src/derive_provider/component_name.rs +++ /dev/null @@ -1,23 +0,0 @@ -use quote::ToTokens; -use syn::spanned::Spanned; -use syn::{Error, Ident, ItemImpl, Type, parse2}; - -use crate::parse::SimpleType; - -pub fn derive_component_name_from_provider_impl(provider_impl: &ItemImpl) -> syn::Result { - let provider_trait = provider_impl.trait_.as_ref().ok_or_else(|| { - Error::new( - provider_impl.span(), - "expect provider trait name to be present", - ) - })?; - - let provider_trait: SimpleType = parse2(provider_trait.1.to_token_stream())?; - - let component_ident = Ident::new( - &format!("{}Component", provider_trait.name), - provider_trait.span(), - ); - - parse2(component_ident.to_token_stream()) -} diff --git a/crates/cgp-macro-lib/src/derive_provider/derive.rs b/crates/cgp-macro-lib/src/derive_provider/derive.rs deleted file mode 100644 index b6507c38..00000000 --- a/crates/cgp-macro-lib/src/derive_provider/derive.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::collections::BTreeMap; - -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{Comma, For}; -use syn::{ - AngleBracketedGenericArguments, Error, GenericArgument, ItemImpl, ItemStruct, Path, - PathArguments, Type, parse_quote, -}; - -use crate::derive_provider::replace_provider_in_generics; -use crate::parse::SimpleType; - -pub fn derive_provider_struct(provider_impl: &ItemImpl) -> syn::Result { - let impl_self_type = &provider_impl.self_ty; - - let provider_type: SimpleType = syn::parse2(quote!( #impl_self_type ))?; - - let provider_name = &provider_type.name; - - let provider_field = match &provider_type.generics { - Some(generics) => { - let args = &generics.args; - quote! { - #generics - ( pub ::core::marker::PhantomData<( #args )> ) - } - } - None => TokenStream::new(), - }; - - let provider_struct = syn::parse2(quote! { - pub struct #provider_name #provider_field; - })?; - - Ok(provider_struct) -} - -pub fn derive_is_provider_for( - component_name: &Type, - provider_impl: &ItemImpl, -) -> syn::Result { - let provider_path = provider_impl - .trait_ - .as_ref() - .ok_or_else(|| { - Error::new( - provider_impl.span(), - "provider impl should contain trait path", - ) - })? - .1 - .segments - .last() - .ok_or_else(|| { - Error::new( - provider_impl.span(), - "provider impl should contain trait path containing generic parameters", - ) - })?; - - let provider_map = BTreeMap::from([(provider_path.ident.clone(), component_name.clone())]); - - let is_provider_generics: AngleBracketedGenericArguments = match &provider_path.arguments { - PathArguments::AngleBracketed(generics) => { - let mut generic_args = generics.clone().args.into_iter(); - - let mut is_provider_params: Punctuated = Punctuated::default(); - - let mut context_arg: Option = None; - - for arg in generic_args.by_ref() { - if let GenericArgument::Lifetime(life) = arg { - // Lifetime params are forced to be pushed to the front of a provider trait. - // Skip those and put them in the form of `Life<'a>` inside the IsProviderFor params - is_provider_params.push(parse_quote! { Life<#life> }) - } else { - // Find the first non-lifetime context type argument and break - context_arg = Some(arg); - break; - } - } - - let context_arg = context_arg.ok_or_else(|| { - Error::new( - provider_impl.span(), - "provider impl should contain trait path containing at least one generic parameter", - ) - })?; - - is_provider_params.extend(generic_args); - - parse_quote!( < #component_name, #context_arg, ( #is_provider_params ) > ) - } - _ => { - return Err(Error::new( - provider_impl.span(), - "provider impl should contain trait path containing generic parameters", - )); - } - }; - - let is_provider_path: Path = parse_quote!( IsProviderFor #is_provider_generics ); - - let mut is_provider_impl = provider_impl.clone(); - - is_provider_impl.attrs.clear(); - is_provider_impl.items.clear(); - is_provider_impl.defaultness = None; - is_provider_impl.unsafety = None; - - is_provider_impl.trait_ = Some((None, is_provider_path, For(Span::call_site()))); - - replace_provider_in_generics(&provider_map, &mut is_provider_impl.generics); - - Ok(is_provider_impl) -} diff --git a/crates/cgp-macro-lib/src/derive_provider/mod.rs b/crates/cgp-macro-lib/src/derive_provider/mod.rs deleted file mode 100644 index 07b38c97..00000000 --- a/crates/cgp-macro-lib/src/derive_provider/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod component_name; -mod derive; -mod replace_constraint; - -pub use component_name::*; -pub use derive::*; -pub use replace_constraint::*; diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs index 9c97bf6a..dcc319a3 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use std::collections::btree_map::Entry; +use cgp_macro_core::types::is_provider_for::derive_is_provider_for; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; use syn::{Ident, ItemTrait, Type, parse_quote, parse2}; @@ -12,7 +13,6 @@ use crate::derive_getter::{ GetterField, derive_use_field_impl, derive_use_fields_impl, derive_with_provider_impl, parse_getter_fields, }; -use crate::derive_provider::derive_is_provider_for; use crate::parse::{ComponentSpec, Entries}; pub fn cgp_getter(attr: TokenStream, body: TokenStream) -> syn::Result { diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index 199b9029..16ee9fe1 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,11 +1,16 @@ +use cgp_macro_core::types::cgp_impl::{ImplArgs, ItemCgpImpl}; use proc_macro2::TokenStream; +use quote::ToTokens; use syn::{ItemImpl, parse2}; -use crate::cgp_impl::{ImplProviderSpec, derive_cgp_impl}; - pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { - let spec: ImplProviderSpec = parse2(attr)?; + let args: ImplArgs = parse2(attr)?; let item_impl: ItemImpl = parse2(body)?; - derive_cgp_impl(spec, item_impl) + let item_cgp_impl = ItemCgpImpl { args, item_impl }; + + let lowered = item_cgp_impl.lower()?; + let lowered = lowered.lower()?; + + Ok(lowered.to_token_stream()) } diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_inherit.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_inherit.rs index 42745075..4d375d6f 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_inherit.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_inherit.rs @@ -1,14 +1,13 @@ use cgp_macro_core::types::generics::TypeGenerics; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use proc_macro2::TokenStream; use quote::quote; use syn::{Ident, ItemImpl, ItemStruct, parse_quote, parse2}; -use crate::parse::SimpleType; - pub fn cgp_inherit(attr: TokenStream, body: TokenStream) -> syn::Result { let context_struct: ItemStruct = parse2(body)?; - let preset: SimpleType = parse2(attr)?; + let preset: IdentWithTypeArgs = parse2(attr)?; let type_generics = TypeGenerics::try_from(&context_struct.generics)?; @@ -27,10 +26,10 @@ pub fn cgp_inherit(attr: TokenStream, body: TokenStream) -> syn::Result, - preset: &SimpleType, + preset: &IdentWithTypeArgs, ) -> syn::Result<(ItemImpl, ItemImpl)> { - let preset_name = &preset.name; - let preset_generics = &preset.generics; + let preset_name = &preset.ident; + let preset_generics = &preset.type_args; let provider_params = match provider_generics { Some(generics) => { diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_new_provider.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_new_provider.rs index 653ff7c5..81c15f63 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_new_provider.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_new_provider.rs @@ -1,28 +1,20 @@ +use cgp_macro_core::types::cgp_provider::{ItemCgpProvider, ProviderArgs}; +use cgp_macro_core::types::keyword::Keyword; use proc_macro2::TokenStream; use quote::quote; -use syn::{ItemImpl, Type}; - -use crate::derive_provider::{ - derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, -}; +use syn::{ItemImpl, parse2}; pub fn cgp_new_provider(attr: TokenStream, body: TokenStream) -> syn::Result { - let provider_impl: ItemImpl = syn::parse2(body)?; - - let component_name: Type = if !attr.is_empty() { - syn::parse2(attr)? - } else { - derive_component_name_from_provider_impl(&provider_impl)? - }; + let item_impl: ItemImpl = parse2(body)?; + let mut args: ProviderArgs = parse2(attr)?; + args.new = Some(Keyword::default()); - let provider_struct = derive_provider_struct(&provider_impl)?; + let item = ItemCgpProvider { args, item_impl }; - let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_name, &provider_impl)?; + let lowered = item.lower()?; let result = quote! { - #provider_struct - #provider_impl - #is_provider_for_impl + #lowered }; Ok(result) diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_preset.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_preset.rs index 11d1bed7..46f32b8d 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_preset.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_preset.rs @@ -1,6 +1,8 @@ use std::collections::HashSet; +use cgp_macro_core::functions::to_snake_case_str; use cgp_macro_core::types::generics::ImplGenerics; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use cgp_macro_core::types::provider_struct::ProviderStruct; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt, quote}; @@ -9,14 +11,13 @@ use syn::token::{At, Comma}; use syn::{GenericParam, Ident, ItemTrait, TypeParamBound, parse_quote, parse2}; use crate::delegate_components::impl_delegate_components; -use crate::parse::{DefinePreset, DelegateEntry, SimpleType}; +use crate::parse::{DefinePreset, DelegateEntry}; use crate::preset::{define_substitution_macro, impl_components_is_preset}; -use crate::replace_self::to_snake_case_str; pub fn define_preset(body: TokenStream) -> syn::Result { let ast: DefinePreset = syn::parse2(body)?; - let delegate_entries: Punctuated, Comma> = ast + let delegate_entries: Punctuated, Comma> = ast .delegate_entries .iter() .map(|entry| entry.entry.clone()) @@ -36,8 +37,8 @@ pub fn define_preset(body: TokenStream) -> syn::Result { }; if let Some(parent) = m_parent { - let parent_ident = &parent.name; - let parent_generics = &parent.generics; + let parent_ident = &parent.ident; + let parent_generics = &parent.type_args; let parent_components_ident = Ident::new( &format!("__{parent_ident}Components__"), @@ -58,7 +59,7 @@ pub fn define_preset(body: TokenStream) -> syn::Result { for entry in ast.delegate_entries.iter() { if entry.is_override.is_some() { for component in entry.entry.keys.iter() { - overrides.push(&component.ty.name); + overrides.push(&component.ty.ident); } } } @@ -194,7 +195,7 @@ pub fn define_preset(body: TokenStream) -> syn::Result { let mut parent_exports = TokenStream::new(); for parent in parent_presets.iter() { - let parent_ident = &parent.parent_type.name; + let parent_ident = &parent.parent_type.ident; parent_exports.append_all(quote! { #[doc(hidden)] #[doc(no_inline)] @@ -224,7 +225,7 @@ pub fn define_preset(body: TokenStream) -> syn::Result { for entry in delegate_entries.iter() { for component in entry.keys.iter() { - let component_name = &component.ty.name; + let component_name = &component.ty.ident; components.insert(component_name.clone()); for param in component.generics.generics.params.iter() { diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_provider.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_provider.rs index 33f8bf11..d541ca39 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_provider.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_provider.rs @@ -1,23 +1,18 @@ +use cgp_macro_core::types::cgp_provider::{ItemCgpProvider, ProviderArgs}; use proc_macro2::TokenStream; use quote::quote; -use syn::{ItemImpl, Type, parse2}; - -use crate::derive_provider::{derive_component_name_from_provider_impl, derive_is_provider_for}; +use syn::{ItemImpl, parse2}; pub fn cgp_provider(attr: TokenStream, body: TokenStream) -> syn::Result { - let provider_impl: ItemImpl = parse2(body)?; + let item_impl: ItemImpl = parse2(body)?; + let args: ProviderArgs = parse2(attr)?; - let component_name: Type = if !attr.is_empty() { - syn::parse2(attr)? - } else { - derive_component_name_from_provider_impl(&provider_impl)? - }; + let item = ItemCgpProvider { args, item_impl }; - let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_name, &provider_impl)?; + let lowered = item.lower()?; let result = quote! { - #provider_impl - #is_provider_for_impl + #lowered }; Ok(result) diff --git a/crates/cgp-macro-lib/src/for_each_replace.rs b/crates/cgp-macro-lib/src/for_each_replace.rs index ccb739b9..6133e13f 100644 --- a/crates/cgp-macro-lib/src/for_each_replace.rs +++ b/crates/cgp-macro-lib/src/for_each_replace.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use proc_macro2::{Group, TokenStream, TokenTree}; use quote::ToTokens; use syn::__private::parse_brackets; @@ -9,7 +10,7 @@ use syn::punctuated::Punctuated; use syn::token::{Comma, Or}; use syn::{Ident, braced}; -use crate::parse::{DelegateKey, SimpleType}; +use crate::parse::DelegateKey; pub struct ReplaceSpecs { pub target_ident: Ident, @@ -19,9 +20,10 @@ pub struct ReplaceSpecs { impl Parse for ReplaceSpecs { fn parse(input: ParseStream) -> syn::Result { - let raw_replacements: Vec> = { + let raw_replacements: Vec> = { let content = parse_brackets(input)?.content; - let types = , Comma>>::parse_terminated(&content)?; + let types = + , Comma>>::parse_terminated(&content)?; types.into_iter().collect() }; @@ -58,7 +60,7 @@ impl Parse for ReplaceSpecs { let replacements = raw_replacements .into_iter() .filter(|replacement| { - let target_ident = &replacement.ty.name; + let target_ident = &replacement.ty.ident; exclude.iter().all(|exclude| exclude != target_ident) }) diff --git a/crates/cgp-macro-lib/src/lib.rs b/crates/cgp-macro-lib/src/lib.rs index 2b322b95..421bede0 100644 --- a/crates/cgp-macro-lib/src/lib.rs +++ b/crates/cgp-macro-lib/src/lib.rs @@ -10,7 +10,6 @@ extern crate alloc; pub(crate) mod attributes; pub(crate) mod blanket_trait; pub(crate) mod cgp_fn; -pub(crate) mod cgp_impl; pub(crate) mod check_components; pub(crate) mod delegate_components; pub(crate) mod derive_builder; @@ -18,13 +17,11 @@ pub(crate) mod derive_component; pub(crate) mod derive_extractor; pub(crate) mod derive_getter; pub(crate) mod derive_has_fields; -pub(crate) mod derive_provider; pub(crate) mod field; pub(crate) mod for_each_replace; pub(crate) mod parse; pub(crate) mod preset; pub(crate) mod product; -pub(crate) mod replace_self; pub(crate) mod symbol; pub(crate) mod type_component; diff --git a/crates/cgp-macro-lib/src/parse/check_components.rs b/crates/cgp-macro-lib/src/parse/check_components.rs index 00bca26b..924d091f 100644 --- a/crates/cgp-macro-lib/src/parse/check_components.rs +++ b/crates/cgp-macro-lib/src/parse/check_components.rs @@ -1,4 +1,5 @@ use cgp_macro_core::types::generics::ImplGenerics; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use proc_macro2::Span; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; @@ -7,8 +8,6 @@ use syn::spanned::Spanned; use syn::token::{Bracket, Colon, Comma, Lt, Pound, Where}; use syn::{Attribute, Ident, Type, WhereClause, braced, bracketed, parse2}; -use crate::parse::SimpleType; - pub struct CheckComponentsSpecs { pub specs: Vec, } @@ -97,10 +96,10 @@ impl Parse for CheckComponents { let trait_name = if let Some(check_trait_name) = m_check_trait_name { check_trait_name } else { - let context_type: SimpleType = parse2(context_type.to_token_stream())?; + let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?; Ident::new( - &format!("__Check{}", context_type.name), + &format!("__Check{}", context_type.ident), context_type.span(), ) }; diff --git a/crates/cgp-macro-lib/src/parse/define_preset.rs b/crates/cgp-macro-lib/src/parse/define_preset.rs index 53df3049..3de9e30d 100644 --- a/crates/cgp-macro-lib/src/parse/define_preset.rs +++ b/crates/cgp-macro-lib/src/parse/define_preset.rs @@ -1,3 +1,4 @@ +use cgp_macro_core::types::ident::IdentWithTypeArgs; use proc_macro2::TokenStream; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; @@ -5,7 +6,7 @@ use syn::punctuated::Punctuated; use syn::token::{At, Colon, Comma, Override, Plus, Pound}; use syn::{Error, Ident, braced, bracketed, parenthesized}; -use crate::parse::{DelegateEntry, SimpleType, TypeSpec}; +use crate::parse::{DelegateEntry, TypeSpec}; pub struct DefinePreset { pub provider_wrapper: Option, @@ -16,7 +17,7 @@ pub struct DefinePreset { pub struct DelegatePresetEntry { pub is_override: Option, - pub entry: DelegateEntry, + pub entry: DelegateEntry, } impl Parse for DefinePreset { @@ -81,7 +82,7 @@ impl Parse for DelegatePresetEntry { #[derive(Clone)] pub struct PresetParent { pub has_expanded: Option, - pub parent_type: SimpleType, + pub parent_type: IdentWithTypeArgs, } impl Parse for PresetParent { diff --git a/crates/cgp-macro-lib/src/parse/delegate_and_check_components.rs b/crates/cgp-macro-lib/src/parse/delegate_and_check_components.rs index ba8e96eb..0c11087d 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_and_check_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_and_check_components.rs @@ -1,6 +1,7 @@ use core::iter; use cgp_macro_core::types::generics::ImplGenerics; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; @@ -8,7 +9,7 @@ use syn::spanned::Spanned; use syn::token::{Bracket, Comma, Lt, Pound}; use syn::{Attribute, Ident, Type, braced, bracketed, parse2}; -use crate::parse::{DelegateMode, DelegateValue, SimpleType}; +use crate::parse::{DelegateMode, DelegateValue}; pub struct DelegateAndCheckSpec { pub impl_generics: ImplGenerics, @@ -45,9 +46,9 @@ impl Parse for DelegateAndCheckSpec { let trait_name = match m_trait_name { Some(ident) => ident, None => { - let context_type: SimpleType = parse2(context_type.to_token_stream())?; + let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?; Ident::new( - &format!("__CanUse{}", context_type.name), + &format!("__CanUse{}", context_type.ident), context_type.span(), ) } diff --git a/crates/cgp-macro-lib/src/parse/delegate_components.rs b/crates/cgp-macro-lib/src/parse/delegate_components.rs index e3c25f4c..736aaf1d 100644 --- a/crates/cgp-macro-lib/src/parse/delegate_components.rs +++ b/crates/cgp-macro-lib/src/parse/delegate_components.rs @@ -2,6 +2,7 @@ use core::iter; use cgp_macro_core::functions::merge_generics; use cgp_macro_core::types::generics::{ImplGenerics, TypeGenerics}; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt, quote}; use syn::parse::discouraged::Speculative; @@ -10,7 +11,7 @@ use syn::punctuated::Punctuated; use syn::token::{At, Bracket, Colon, Comma, Gt, Lt, RArrow}; use syn::{Error, Generics, Ident, Token, Type, braced, bracketed, parse_quote}; -use crate::parse::{ComponentPaths, SimpleType}; +use crate::parse::ComponentPaths; #[derive(Clone)] pub struct DelegateEntry { @@ -128,14 +129,14 @@ impl Parse for DelegateEntry { } } -impl Parse for DelegateEntry { +impl Parse for DelegateEntry { fn parse(input: ParseStream) -> syn::Result { let components = if input.peek(Bracket) { let components_body; bracketed!(components_body in input); components_body.parse_terminated(DelegateKey::parse, Token![,])? } else { - let component: DelegateKey = input.parse()?; + let component: DelegateKey = input.parse()?; Punctuated::from_iter(iter::once(component)) }; diff --git a/crates/cgp-macro-lib/src/parse/mod.rs b/crates/cgp-macro-lib/src/parse/mod.rs index 6dd0a2a2..3839cf99 100644 --- a/crates/cgp-macro-lib/src/parse/mod.rs +++ b/crates/cgp-macro-lib/src/parse/mod.rs @@ -6,7 +6,6 @@ mod delegate_components; mod entry; mod is_provider_params; mod path; -mod simple_type; mod type_spec; pub use check_components::*; @@ -17,5 +16,4 @@ pub use delegate_components::*; pub use entry::*; pub use is_provider_params::*; pub use path::*; -pub use simple_type::*; pub use type_spec::*; diff --git a/crates/cgp-macro-lib/src/parse/simple_type.rs b/crates/cgp-macro-lib/src/parse/simple_type.rs deleted file mode 100644 index c53460bc..00000000 --- a/crates/cgp-macro-lib/src/parse/simple_type.rs +++ /dev/null @@ -1,33 +0,0 @@ -use quote::ToTokens; -use syn::parse::{Parse, ParseStream}; -use syn::token::Lt; -use syn::{AngleBracketedGenericArguments, Ident}; - -#[derive(Clone)] -pub struct SimpleType { - pub name: Ident, - pub generics: Option, -} - -impl Parse for SimpleType -where - Generics: Parse, -{ - fn parse(input: ParseStream) -> syn::Result { - let name: Ident = input.parse()?; - let generics = if input.peek(Lt) { - Some(input.parse()?) - } else { - None - }; - - Ok(Self { name, generics }) - } -} - -impl ToTokens for SimpleType { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.name.to_tokens(tokens); - self.generics.to_tokens(tokens); - } -} diff --git a/crates/cgp-macro-lib/src/preset/impl_is_preset.rs b/crates/cgp-macro-lib/src/preset/impl_is_preset.rs index ec912b26..72336da1 100644 --- a/crates/cgp-macro-lib/src/preset/impl_is_preset.rs +++ b/crates/cgp-macro-lib/src/preset/impl_is_preset.rs @@ -1,17 +1,18 @@ use alloc::vec::Vec; use cgp_macro_core::types::generics::ImplGenerics; +use cgp_macro_core::types::ident::IdentWithTypeArgs; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Ident, ItemImpl, Type, parse_quote}; -use crate::parse::{DelegateEntry, DelegateKey, SimpleType}; +use crate::parse::{DelegateEntry, DelegateKey}; pub fn impl_components_is_preset( trait_name: &Ident, preset_type: &Type, preset_generics: &ImplGenerics, - delegate_entries: &Punctuated, Comma>, + delegate_entries: &Punctuated, Comma>, ) -> Vec { delegate_entries .iter() @@ -27,7 +28,7 @@ pub fn impl_component_is_preset( trait_name: &Ident, _preset_type: &Type, _preset_generics: &ImplGenerics, - component: &DelegateKey, + component: &DelegateKey, ) -> ItemImpl { let component_type = &component.ty; diff --git a/crates/cgp-macro-lib/src/replace_self/replace_self_receiver.rs b/crates/cgp-macro-lib/src/replace_self/replace_self_receiver.rs deleted file mode 100644 index 425843c5..00000000 --- a/crates/cgp-macro-lib/src/replace_self/replace_self_receiver.rs +++ /dev/null @@ -1,41 +0,0 @@ -use proc_macro2::{Ident, TokenStream}; -use syn::{FnArg, Receiver, Signature, parse_quote}; - -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 - { - *arg = replace_self_receiver(receiver, replaced_var, 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) - } - } -} diff --git a/crates/cgp-macro-lib/src/replace_self/replace_self_type.rs b/crates/cgp-macro-lib/src/replace_self/replace_self_type.rs deleted file mode 100644 index bf8a6952..00000000 --- a/crates/cgp-macro-lib/src/replace_self/replace_self_type.rs +++ /dev/null @@ -1,95 +0,0 @@ -use alloc::vec::Vec; - -use itertools::Itertools; -use proc_macro2::{Group, Ident, TokenStream, TokenTree}; -use quote::{ToTokens, format_ident}; -use syn::parse::Parse; - -pub fn iter_parse_and_replace_self_type( - vals: I, - replaced_ident: &Ident, - local_assoc_types: &Vec, -) -> syn::Result -where - I: IntoIterator + FromIterator, - T: ToTokens + Parse, -{ - vals.into_iter() - .map(|val| parse_and_replace_self_type(&val, replaced_ident, local_assoc_types)) - .collect() -} - -pub fn parse_and_replace_self_type( - val: &T, - replaced_ident: &Ident, - local_assoc_types: &Vec, -) -> syn::Result -where - T: ToTokens + Parse, -{ - 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: TokenStream, - local_assoc_types: &Vec, -) -> TokenStream { - let self_type = format_ident!("Self"); - - let mut result_stream: Vec = Vec::new(); - - let mut token_iter = stream.into_iter().multipeek(); - - while let Some(tree) = token_iter.next() { - match tree { - TokenTree::Ident(ident) => { - if ident == self_type { - let replaced_ident = replaced_ident.clone(); - - // Do not replace self if it is an associated type expression that refers to local associated type - let replaced = match token_iter.peek() { - Some(TokenTree::Punct(p)) if p.as_char() == ':' => { - match token_iter.peek() { - Some(TokenTree::Punct(p)) if p.as_char() == ':' => { - match token_iter.peek() { - Some(TokenTree::Ident(assoc_type)) - if local_assoc_types.contains(assoc_type) => - { - ident.to_token_stream() - } - _ => replaced_ident, - } - } - _ => replaced_ident, - } - } - _ => replaced_ident, - }; - - result_stream.extend(replaced); - } else { - result_stream.push(TokenTree::Ident(ident)); - } - } - TokenTree::Group(group) => { - let replaced_stream = - 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)); - } - TokenTree::Punct(punct) => { - result_stream.push(TokenTree::Punct(punct)); - } - TokenTree::Literal(lit) => result_stream.push(TokenTree::Literal(lit)), - } - } - - result_stream.into_iter().collect() -} diff --git a/crates/cgp-macro-lib/src/replace_self/replace_self_var.rs b/crates/cgp-macro-lib/src/replace_self/replace_self_var.rs deleted file mode 100644 index b94e6b2e..00000000 --- a/crates/cgp-macro-lib/src/replace_self/replace_self_var.rs +++ /dev/null @@ -1,35 +0,0 @@ -use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::format_ident; -use syn::Ident; - -pub fn replace_self_var(stream: TokenStream, replaced_ident: &Ident) -> TokenStream { - let self_ident = format_ident!("self"); - - let mut result_stream: Vec = 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() -} diff --git a/crates/cgp-macro-lib/src/type_component/derive.rs b/crates/cgp-macro-lib/src/type_component/derive.rs index 53b18322..4671f6a7 100644 --- a/crates/cgp-macro-lib/src/type_component/derive.rs +++ b/crates/cgp-macro-lib/src/type_component/derive.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; +use cgp_macro_core::types::is_provider_for::derive_is_provider_for; use quote::{ToTokens, quote}; use syn::spanned::Spanned; use syn::{Error, Generics, ItemImpl, ItemTrait, TraitItem, TraitItemType, Type, parse2}; -use crate::derive_provider::derive_is_provider_for; use crate::parse::ComponentSpec; use crate::type_component::replace::get_bounds_and_replace_self_assoc_type; diff --git a/crates/cgp-runtime/Cargo.toml b/crates/cgp-runtime/Cargo.toml index 5c00a985..da3894bd 100644 --- a/crates/cgp-runtime/Cargo.toml +++ b/crates/cgp-runtime/Cargo.toml @@ -7,9 +7,6 @@ repository = { workspace = true } authors = { workspace = true } rust-version = { workspace = true } keywords = { workspace = true } -description = """ - Context-generic programming core component traits -""" [dependencies] -cgp-core = { workspace = true } +cgp = { version = "0.7.0", path = "../cgp-core", package = "cgp-core" } diff --git a/crates/cgp-runtime/src/traits/has_runtime.rs b/crates/cgp-runtime/src/traits/has_runtime.rs index 7a12f6f8..35b8c537 100644 --- a/crates/cgp-runtime/src/traits/has_runtime.rs +++ b/crates/cgp-runtime/src/traits/has_runtime.rs @@ -1,4 +1,4 @@ -use cgp_core::prelude::*; +use cgp::prelude::*; use crate::HasRuntimeType; diff --git a/crates/cgp-runtime/src/traits/has_runtime_type.rs b/crates/cgp-runtime/src/traits/has_runtime_type.rs index 10cd1fc0..446392a8 100644 --- a/crates/cgp-runtime/src/traits/has_runtime_type.rs +++ b/crates/cgp-runtime/src/traits/has_runtime_type.rs @@ -1,4 +1,4 @@ -use cgp_core::prelude::*; +use cgp::prelude::*; #[cgp_type] pub trait HasRuntimeType {