From 3cfa37e48a234d31e60dcbf8d2cdb7609cf61726 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 09:51:37 +0000 Subject: [PATCH 01/19] Refactor derive_component --- .../src/derive_component/derive.rs | 32 +++++++++++-------- .../src/getter_component/derive.rs | 17 ++++++++++ .../src/getter_component/mod.rs | 1 + crates/cgp-component-macro-lib/src/lib.rs | 1 + .../src/tests/derive_component.rs | 6 ++-- crates/cgp-component-macro/src/lib.rs | 4 ++- 6 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 crates/cgp-component-macro-lib/src/getter_component/derive.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/mod.rs diff --git a/crates/cgp-component-macro-lib/src/derive_component/derive.rs b/crates/cgp-component-macro-lib/src/derive_component/derive.rs index f95307d..0089224 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/derive.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; -use quote::ToTokens; +use quote::quote; use syn::ItemTrait; use crate::derive_component::component_name::derive_component_name_struct; @@ -8,19 +8,24 @@ use crate::derive_component::consumer_impl::derive_consumer_impl; use crate::derive_component::provider_impl::derive_provider_impl; use crate::derive_component::provider_trait::derive_provider_trait; -pub fn derive_component(attr: TokenStream, item: TokenStream) -> TokenStream { - let spec: ComponentSpec = syn::parse2(attr).unwrap(); +pub fn derive_component(attr: TokenStream, item: TokenStream) -> syn::Result { + let spec: ComponentSpec = syn::parse2(attr)?; + let consumer_trait: ItemTrait = syn::parse2(item)?; - let consumer_trait: ItemTrait = syn::parse2(item).unwrap(); + derive_component_with_ast(spec, consumer_trait) +} +pub fn derive_component_with_ast( + spec: ComponentSpec, + consumer_trait: ItemTrait, +) -> syn::Result { let provider_name = &spec.provider_name; let context_type = &spec.context_type; let component_struct = derive_component_name_struct(&spec.component_name, &spec.component_params); - let provider_trait = - derive_provider_trait(&consumer_trait, provider_name, context_type).unwrap(); + let provider_trait = derive_provider_trait(&consumer_trait, provider_name, context_type)?; let consumer_impl = derive_consumer_impl(&consumer_trait, provider_name, context_type); @@ -30,12 +35,13 @@ pub fn derive_component(attr: TokenStream, item: TokenStream) -> TokenStream { &spec.component_params, ); - let mut output = consumer_trait.to_token_stream(); - - output.extend(component_struct.to_token_stream()); - output.extend(provider_trait.to_token_stream()); - output.extend(consumer_impl.to_token_stream()); - output.extend(provider_impl.to_token_stream()); + let derived = quote! { + #consumer_trait + #component_struct + #provider_trait + #consumer_impl + #provider_impl + }; - output + Ok(derived) } diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs new file mode 100644 index 0000000..f1ff614 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -0,0 +1,17 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::ItemTrait; + +use crate::derive_component::component_spec::ComponentSpec; +use crate::derive_component::derive::derive_component_with_ast; + +pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Result { + let spec: ComponentSpec = syn::parse2(attr).unwrap(); + let consumer_trait: ItemTrait = syn::parse2(item).unwrap(); + + let derived_component = derive_component_with_ast(spec, consumer_trait)?; + + Ok(quote! { + #derived_component + }) +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/mod.rs b/crates/cgp-component-macro-lib/src/getter_component/mod.rs new file mode 100644 index 0000000..8fe0591 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/mod.rs @@ -0,0 +1 @@ +pub mod derive; diff --git a/crates/cgp-component-macro-lib/src/lib.rs b/crates/cgp-component-macro-lib/src/lib.rs index b2dc1a9..82f96ad 100644 --- a/crates/cgp-component-macro-lib/src/lib.rs +++ b/crates/cgp-component-macro-lib/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; pub mod delegate_components; pub mod derive_component; pub mod for_each_replace; +pub mod getter_component; pub mod preset; pub mod type_component; diff --git a/crates/cgp-component-macro-lib/src/tests/derive_component.rs b/crates/cgp-component-macro-lib/src/tests/derive_component.rs index 8ce2171..9ed0872 100644 --- a/crates/cgp-component-macro-lib/src/tests/derive_component.rs +++ b/crates/cgp-component-macro-lib/src/tests/derive_component.rs @@ -17,7 +17,8 @@ fn test_basic_derive_component() { fn foo(&self) -> Self::Foo; } }, - ); + ) + .unwrap(); } #[test] @@ -34,7 +35,8 @@ fn test_derive_component_with_const_generic() { fn foo(&self) -> Self::Foo; } }, - ); + ) + .unwrap(); let expected = quote! { pub trait HasFoo { diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index 4491644..62cb3d8 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -10,7 +10,9 @@ use proc_macro::TokenStream; #[proc_macro_attribute] pub fn cgp_component(attr: TokenStream, item: TokenStream) -> TokenStream { - cgp_component_macro_lib::derive_component(attr.into(), item.into()).into() + cgp_component_macro_lib::derive_component(attr.into(), item.into()) + .unwrap() + .into() } #[proc_macro] From 81a5eb06693763dbf2dc8a232096e332d0a67184 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 10:04:13 +0000 Subject: [PATCH 02/19] Use into_compile_error instead of unwrap --- Cargo.lock | 1 + .../src/derive_component/component_spec.rs | 12 +++++++++--- .../src/derive_component/entry.rs | 10 +++++++--- .../src/getter_component/derive.rs | 4 ++-- crates/cgp-component-macro/Cargo.toml | 1 + crates/cgp-component-macro/src/lib.rs | 12 ++++++------ 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0214ad3..2ac7741 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,7 @@ name = "cgp-component-macro" version = "0.2.0" dependencies = [ "cgp-component-macro-lib", + "syn", ] [[package]] diff --git a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs index d7c721a..be490aa 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs @@ -21,12 +21,18 @@ pub struct ComponentNameSpec { pub component_params: Punctuated, } +static VALID_KEYS: [&'static str; 3] = ["context", "provider", "name"]; + impl Parse for ComponentSpec { fn parse(input: ParseStream) -> syn::Result { let Entries { entries } = input.parse()?; + for key in entries.keys() { + if !VALID_KEYS.iter().any(|valid| valid == key) {} + } + let context_type: Ident = { - let raw_context_type = entries.get(&Ident::new("context", Span::call_site())); + let raw_context_type = entries.get("context"); if let Some(context_type) = raw_context_type { syn::parse2(context_type.to_token_stream())? @@ -37,14 +43,14 @@ impl Parse for ComponentSpec { let provider_name: Ident = { let raw_provider_name = entries - .get(&Ident::new("provider", Span::call_site())) + .get("provider") .ok_or_else(|| Error::new(input.span(), "expect provider name to be given"))?; syn::parse2(raw_provider_name.to_token_stream())? }; let (component_name, component_params) = { - let raw_component_name = entries.get(&Ident::new("name", Span::call_site())); + let raw_component_name = entries.get("name"); if let Some(raw_component_name) = raw_component_name { let ComponentNameSpec { diff --git a/crates/cgp-component-macro-lib/src/derive_component/entry.rs b/crates/cgp-component-macro-lib/src/derive_component/entry.rs index 9d76c09..a4032a5 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/entry.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/entry.rs @@ -1,5 +1,6 @@ use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Colon, Comma}; @@ -21,15 +22,18 @@ impl Parse for Entry { } pub struct Entries { - pub entries: BTreeMap, + pub entries: BTreeMap, } impl Parse for Entries { fn parse(input: ParseStream) -> syn::Result { let entry_list: Punctuated = Punctuated::parse_terminated(input)?; - let entries = - BTreeMap::from_iter(entry_list.into_iter().map(|entry| (entry.key, entry.value))); + let entries = BTreeMap::from_iter( + entry_list + .into_iter() + .map(|entry| (entry.key.to_string(), entry.value)), + ); Ok(Entries { entries }) } diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index f1ff614..c2106f9 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -6,8 +6,8 @@ use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Result { - let spec: ComponentSpec = syn::parse2(attr).unwrap(); - let consumer_trait: ItemTrait = syn::parse2(item).unwrap(); + let spec: ComponentSpec = syn::parse2(attr)?; + let consumer_trait: ItemTrait = syn::parse2(item)?; let derived_component = derive_component_with_ast(spec, consumer_trait)?; diff --git a/crates/cgp-component-macro/Cargo.toml b/crates/cgp-component-macro/Cargo.toml index 02e3542..7052328 100644 --- a/crates/cgp-component-macro/Cargo.toml +++ b/crates/cgp-component-macro/Cargo.toml @@ -15,4 +15,5 @@ description = """ proc-macro = true [dependencies] +syn = { version = "2.0.95", features = [ "full", "extra-traits" ] } cgp-component-macro-lib = { version = "0.2.0" } \ No newline at end of file diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index 62cb3d8..32e1376 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -11,41 +11,41 @@ use proc_macro::TokenStream; #[proc_macro_attribute] pub fn cgp_component(attr: TokenStream, item: TokenStream) -> TokenStream { cgp_component_macro_lib::derive_component(attr.into(), item.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn delegate_components(body: TokenStream) -> TokenStream { cgp_component_macro_lib::delegate_components(body.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn cgp_preset(body: TokenStream) -> TokenStream { cgp_component_macro_lib::define_preset(body.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn cgp_type(body: TokenStream) -> TokenStream { cgp_component_macro_lib::derive_type_component(body.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn for_each_replace(body: TokenStream) -> TokenStream { cgp_component_macro_lib::handle_for_each_replace(body.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn replace_with(body: TokenStream) -> TokenStream { cgp_component_macro_lib::handle_replace(body.into()) - .unwrap() + .unwrap_or_else(syn::Error::into_compile_error) .into() } From 3ebff2c49e2406ad70eee7eba38a0c2524b92d64 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 10:07:42 +0000 Subject: [PATCH 03/19] Validate entries in component spec --- .../src/derive_component/component_spec.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs index be490aa..ecc4a7e 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs @@ -28,7 +28,14 @@ impl Parse for ComponentSpec { let Entries { entries } = input.parse()?; for key in entries.keys() { - if !VALID_KEYS.iter().any(|valid| valid == key) {} + if !VALID_KEYS.iter().any(|valid| valid == key) { + return Err(syn::Error::new( + Span::call_site(), + format!( + r#"invalid key in component spec: {key}. the following keys are valid: "context", "provider", "name"."# + ), + )); + } } let context_type: Ident = { From b9c096d3226ef7b606ee7599d00bf417ca8e455a Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 10:41:11 +0000 Subject: [PATCH 04/19] Implement parser for getter fields --- .../src/derive_component/derive.rs | 6 +- .../src/getter_component/derive.rs | 113 +++++++++++++++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/derive_component/derive.rs b/crates/cgp-component-macro-lib/src/derive_component/derive.rs index 0089224..81c53be 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/derive.rs @@ -12,12 +12,12 @@ pub fn derive_component(attr: TokenStream, item: TokenStream) -> syn::Result syn::Result { let provider_name = &spec.provider_name; let context_type = &spec.context_type; diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index c2106f9..7b19ac2 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -1,6 +1,9 @@ +use alloc::vec::Vec; use proc_macro2::TokenStream; use quote::quote; -use syn::ItemTrait; +use syn::spanned::Spanned; +use syn::token::Mut; +use syn::{parse_quote, Error, FnArg, Ident, ItemTrait, ReturnType, TraitItem, Type}; use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; @@ -9,9 +12,115 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res let spec: ComponentSpec = syn::parse2(attr)?; let consumer_trait: ItemTrait = syn::parse2(item)?; - let derived_component = derive_component_with_ast(spec, consumer_trait)?; + let derived_component = derive_component_with_ast(&spec, &consumer_trait)?; + + let fields = parse_getter_fields(&consumer_trait)?; Ok(quote! { #derived_component }) } + +pub fn parse_getter_fields( + consumer_trait: &ItemTrait, +) -> syn::Result)>> { + if !consumer_trait.generics.params.is_empty() { + return Err(Error::new( + consumer_trait.generics.params.span(), + "getter trait cannot contain generic parameters", + )); + } + + let mut fields = Vec::new(); + + for item in consumer_trait.items.iter() { + match item { + TraitItem::Fn(method) => { + let signature = &method.sig; + + if signature.constness.is_some() { + return Err(Error::new( + signature.constness.span(), + "getter method must not be const fn", + )); + } + + if signature.asyncness.is_some() { + return Err(Error::new( + signature.asyncness.span(), + "getter method must not be async fn", + )); + } + + if signature.unsafety.is_some() { + return Err(Error::new( + signature.unsafety.span(), + "getter method must not be unsafe fn", + )); + } + + if !signature.generics.params.is_empty() { + return Err(Error::new( + signature.generics.params.span(), + "getter method must not contain generic param", + )); + } + + if signature.generics.where_clause.is_some() { + return Err(Error::new( + signature.generics.where_clause.span(), + "getter method must not contain where clause", + )); + } + + let method_name = signature.ident.clone(); + + let return_type: Type = match &signature.output { + ReturnType::Default => parse_quote!(()), + ReturnType::Type(_, ty) => ty.as_ref().clone(), + }; + + let [arg]: [&FnArg; 1] = signature + .inputs + .iter() + .collect::>() + .try_into() + .map_err(|_| { + Error::new( + signature.inputs.span(), + "getter method must contain exactly one `&self` argument", + ) + })?; + + let m_mut = match arg { + FnArg::Receiver(receiver) => { + if receiver.reference.is_none() { + return Err(Error::new( + receiver.span(), + "first argument to getter method must be a reference to self, i.e. `&self`" + )); + } + + receiver.mutability + } + _ => { + return Err(Error::new( + arg.span(), + "first argument to getter method must be `&self`", + )) + } + }; + + fields.push((method_name, return_type, m_mut)) + } + _ => { + return Err(Error::new( + item.span(), + "getter trait can only contain getter methods", + )) + } + } + } + + Ok(fields) +} From c54251dca0bb55d51ff08a10914f037591bfffb0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 10:43:11 +0000 Subject: [PATCH 05/19] Define GetterField struct --- .../src/getter_component/derive.rs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 7b19ac2..c8320df 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -14,16 +14,20 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res let derived_component = derive_component_with_ast(&spec, &consumer_trait)?; - let fields = parse_getter_fields(&consumer_trait)?; + let _fields = parse_getter_fields(&consumer_trait)?; Ok(quote! { #derived_component }) } -pub fn parse_getter_fields( - consumer_trait: &ItemTrait, -) -> syn::Result)>> { +pub struct GetterField { + pub field_name: Ident, + pub field_type: Type, + pub field_mut: Option, +} + +pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result> { if !consumer_trait.generics.params.is_empty() { return Err(Error::new( consumer_trait.generics.params.span(), @@ -73,9 +77,9 @@ pub fn parse_getter_fields( )); } - let method_name = signature.ident.clone(); + let field_name = signature.ident.clone(); - let return_type: Type = match &signature.output { + let field_type: Type = match &signature.output { ReturnType::Default => parse_quote!(()), ReturnType::Type(_, ty) => ty.as_ref().clone(), }; @@ -92,7 +96,7 @@ pub fn parse_getter_fields( ) })?; - let m_mut = match arg { + let field_mut = match arg { FnArg::Receiver(receiver) => { if receiver.reference.is_none() { return Err(Error::new( @@ -111,7 +115,11 @@ pub fn parse_getter_fields( } }; - fields.push((method_name, return_type, m_mut)) + fields.push(GetterField { + field_name, + field_type, + field_mut, + }) } _ => { return Err(Error::new( From 3e774e83a5bd3b4143090abde3533ecad0f3cae3 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 10:58:56 +0000 Subject: [PATCH 06/19] Draft derive_use_fields_impl --- .../src/getter_component/derive.rs | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index c8320df..428db6b 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -1,9 +1,10 @@ +use alloc::string::ToString; use alloc::vec::Vec; use proc_macro2::TokenStream; use quote::quote; use syn::spanned::Spanned; use syn::token::Mut; -use syn::{parse_quote, Error, FnArg, Ident, ItemTrait, ReturnType, TraitItem, Type}; +use syn::{parse_quote, Error, FnArg, Ident, ItemImpl, ItemTrait, ReturnType, TraitItem, Type}; use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; @@ -27,6 +28,54 @@ pub struct GetterField { pub field_mut: Option, } +pub fn derive_use_fields_impl( + consumer_trait: &ItemTrait, + provider_name: &Ident, + fields: &[GetterField], +) -> ItemImpl { + let super_traits = &consumer_trait.supertraits; + + let mut has_field_constraints: TokenStream = TokenStream::new(); + let mut methods: TokenStream = TokenStream::new(); + + for field in fields { + let field_name = &field.field_name; + let field_type = &field.field_type; + let field_symbol = symbol_from_string(&field.field_name.to_string()); + + if field.field_mut.is_none() { + has_field_constraints.extend(quote! { + HasField< #field_symbol, Value = #field_type > + }); + + methods.extend(quote! { + fn #field_name( context: &Context ) -> & #field_type { + context.get_field( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } else { + has_field_constraints.extend(quote! { + HasFieldMut< #field_symbol, Value = #field_type > + }); + + methods.extend(quote! { + fn #field_name( context: &mut Context ) -> &mut #field_type { + context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } + } + + parse_quote! { + impl #provider_name for UseFields + where + Context: #super_traits + #has_field_constraints + { + #methods + } + } +} + pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result> { if !consumer_trait.generics.params.is_empty() { return Err(Error::new( @@ -132,3 +181,11 @@ pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result Type { + value + .chars() + .rfold(parse_quote! { Nil }, |tail, c: char| -> Type { + parse_quote!( Cons< Char< #c >, #tail > ) + }) +} From d34367d6094b7057497eef4f03ffca907e81d75d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 11:08:42 +0000 Subject: [PATCH 07/19] Check that return type must be reference --- .../src/getter_component/derive.rs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 428db6b..0709ad6 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -128,11 +128,6 @@ pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result parse_quote!(()), - ReturnType::Type(_, ty) => ty.as_ref().clone(), - }; - let [arg]: [&FnArg; 1] = signature .inputs .iter() @@ -164,6 +159,31 @@ pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result parse_quote!(()), + ReturnType::Type(_, ty) => { + let ty = ty.as_ref().clone(); + match &ty { + Type::Reference(type_ref) => { + if type_ref.mutability.is_some() != field_mut.is_some() { + return Err(Error::new( + type_ref.span(), + "return type have the same mutability as the self reference", + )); + } + + type_ref.elem.as_ref().clone() + } + _ => { + return Err(Error::new( + ty.span(), + "return type must be a reference", + )) + } + } + } + }; + fields.push(GetterField { field_name, field_type, From 8a0b5fe56d19ad91d202f1d63b21c4c543c8c30f Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 11:14:16 +0000 Subject: [PATCH 08/19] Export cgp_getter macro --- .../cgp-component-macro-lib/src/getter_component/derive.rs | 6 +++++- crates/cgp-component-macro-lib/src/lib.rs | 1 + crates/cgp-component-macro/src/lib.rs | 7 +++++++ crates/cgp-component/src/lib.rs | 2 +- crates/cgp-component/src/types/mod.rs | 2 ++ crates/cgp-component/src/types/use_field.rs | 0 crates/cgp-component/src/types/use_fields.rs | 1 + crates/cgp-core/src/prelude.rs | 4 ++-- 8 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 crates/cgp-component/src/types/use_field.rs create mode 100644 crates/cgp-component/src/types/use_fields.rs diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 0709ad6..71d98a1 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -15,10 +15,14 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res let derived_component = derive_component_with_ast(&spec, &consumer_trait)?; - let _fields = parse_getter_fields(&consumer_trait)?; + let fields = parse_getter_fields(&consumer_trait)?; + + let use_fields_impl = derive_use_fields_impl(&consumer_trait, &spec.provider_name, &fields); Ok(quote! { #derived_component + + #use_fields_impl }) } diff --git a/crates/cgp-component-macro-lib/src/lib.rs b/crates/cgp-component-macro-lib/src/lib.rs index 82f96ad..32f282a 100644 --- a/crates/cgp-component-macro-lib/src/lib.rs +++ b/crates/cgp-component-macro-lib/src/lib.rs @@ -22,5 +22,6 @@ mod tests; pub use crate::delegate_components::delegate_components; pub use crate::derive_component::derive_component; pub use crate::for_each_replace::{handle_for_each_replace, handle_replace}; +pub use crate::getter_component::derive::derive_getter_component; pub use crate::preset::define_preset; pub use crate::type_component::derive::derive_type_component; diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index 32e1376..f7f836c 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -15,6 +15,13 @@ pub fn cgp_component(attr: TokenStream, item: TokenStream) -> TokenStream { .into() } +#[proc_macro_attribute] +pub fn cgp_getter(attr: TokenStream, item: TokenStream) -> TokenStream { + cgp_component_macro_lib::derive_getter_component(attr.into(), item.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + #[proc_macro] pub fn delegate_components(body: TokenStream) -> TokenStream { cgp_component_macro_lib::delegate_components(body.into()) diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 76e55a0..3e46720 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -8,4 +8,4 @@ mod traits; mod types; pub use traits::{DelegateComponent, HasComponents}; -pub use types::{UseContext, UseDelegate, WithContext, WithProvider}; +pub use types::{UseContext, UseDelegate, UseFields, WithContext, WithProvider}; diff --git a/crates/cgp-component/src/types/mod.rs b/crates/cgp-component/src/types/mod.rs index b925781..e2d83e9 100644 --- a/crates/cgp-component/src/types/mod.rs +++ b/crates/cgp-component/src/types/mod.rs @@ -1,7 +1,9 @@ mod use_context; mod use_delegate; +mod use_fields; mod with_provider; pub use use_context::{UseContext, WithContext}; pub use use_delegate::UseDelegate; +pub use use_fields::UseFields; pub use with_provider::WithProvider; diff --git a/crates/cgp-component/src/types/use_field.rs b/crates/cgp-component/src/types/use_field.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/cgp-component/src/types/use_fields.rs b/crates/cgp-component/src/types/use_fields.rs new file mode 100644 index 0000000..9e18e09 --- /dev/null +++ b/crates/cgp-component/src/types/use_fields.rs @@ -0,0 +1 @@ +pub struct UseFields; diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index ffaef2d..877ae82 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -1,9 +1,9 @@ pub use cgp_async::{async_trait, Async, MaybeSend, MaybeStatic, MaybeSync}; -pub use cgp_component::{DelegateComponent, HasComponents, WithContext, WithProvider}; +pub use cgp_component::{DelegateComponent, HasComponents, UseFields, WithContext, WithProvider}; pub use cgp_component_macro::{ cgp_component, cgp_preset, cgp_type, delegate_components, for_each_replace, replace_with, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; -pub use cgp_field::{Char, Cons, Either, HasField, HasFieldMut, Nil, Void}; +pub use cgp_field::{Char, Cons, Either, HasField, HasFieldMut, Nil, UseField, Void}; pub use cgp_field_macro::{product, symbol, HasField, Product, Sum}; pub use cgp_type::{HasType, ProvideType, UseType}; From fe58edc995ac4d57beef1c57390a63499b1ac96c Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 11:29:15 +0000 Subject: [PATCH 09/19] Replace Self:: with Context:: inside provider impl --- .../src/getter_component/derive.rs | 38 +++++++++++++------ crates/cgp-core/src/prelude.rs | 3 +- crates/cgp-runtime/src/traits/has_runtime.rs | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 71d98a1..25d2cea 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -1,13 +1,14 @@ use alloc::string::ToString; use alloc::vec::Vec; use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; use syn::spanned::Spanned; use syn::token::Mut; use syn::{parse_quote, Error, FnArg, Ident, ItemImpl, ItemTrait, ReturnType, TraitItem, Type}; use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; +use crate::derive_component::replace_self_type::replace_self_type; pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Result { let spec: ComponentSpec = syn::parse2(attr)?; @@ -15,9 +16,9 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res let derived_component = derive_component_with_ast(&spec, &consumer_trait)?; - let fields = parse_getter_fields(&consumer_trait)?; + let fields = parse_getter_fields(&spec, &consumer_trait)?; - let use_fields_impl = derive_use_fields_impl(&consumer_trait, &spec.provider_name, &fields); + let use_fields_impl = derive_use_fields_impl(&spec, &consumer_trait, &fields); Ok(quote! { #derived_component @@ -29,14 +30,17 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res pub struct GetterField { pub field_name: Ident, pub field_type: Type, + pub provider_type: Type, pub field_mut: Option, } pub fn derive_use_fields_impl( + spec: &ComponentSpec, consumer_trait: &ItemTrait, - provider_name: &Ident, fields: &[GetterField], ) -> ItemImpl { + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; let super_traits = &consumer_trait.supertraits; let mut has_field_constraints: TokenStream = TokenStream::new(); @@ -44,26 +48,26 @@ pub fn derive_use_fields_impl( for field in fields { let field_name = &field.field_name; - let field_type = &field.field_type; + let provider_type = &field.provider_type; let field_symbol = symbol_from_string(&field.field_name.to_string()); if field.field_mut.is_none() { has_field_constraints.extend(quote! { - HasField< #field_symbol, Value = #field_type > + HasField< #field_symbol, Value = #provider_type > }); methods.extend(quote! { - fn #field_name( context: &Context ) -> & #field_type { + fn #field_name( context: & #context_type ) -> & #provider_type { context.get_field( ::core::marker::PhantomData::< #field_symbol > ) } }); } else { has_field_constraints.extend(quote! { - HasFieldMut< #field_symbol, Value = #field_type > + HasFieldMut< #field_symbol, Value = #provider_type > }); methods.extend(quote! { - fn #field_name( context: &mut Context ) -> &mut #field_type { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) } }); @@ -71,16 +75,19 @@ pub fn derive_use_fields_impl( } parse_quote! { - impl #provider_name for UseFields + impl< #context_type > #provider_name < #context_type > for UseFields where - Context: #super_traits + #has_field_constraints + #context_type: #super_traits + #has_field_constraints { #methods } } } -pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result> { +pub fn parse_getter_fields( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, +) -> syn::Result> { if !consumer_trait.generics.params.is_empty() { return Err(Error::new( consumer_trait.generics.params.span(), @@ -188,9 +195,16 @@ pub fn parse_getter_fields(consumer_trait: &ItemTrait) -> syn::Result Date: Wed, 8 Jan 2025 11:35:45 +0000 Subject: [PATCH 10/19] Properly concatenate HasField constraints --- .../src/getter_component/derive.rs | 10 +++++----- crates/cgp-runtime/src/traits/has_runtime.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 25d2cea..f4e190f 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -41,9 +41,9 @@ pub fn derive_use_fields_impl( ) -> ItemImpl { let context_type = &spec.context_type; let provider_name = &spec.provider_name; - let super_traits = &consumer_trait.supertraits; - let mut has_field_constraints: TokenStream = TokenStream::new(); + let mut constraints = consumer_trait.supertraits.clone(); + let mut methods: TokenStream = TokenStream::new(); for field in fields { @@ -52,7 +52,7 @@ pub fn derive_use_fields_impl( let field_symbol = symbol_from_string(&field.field_name.to_string()); if field.field_mut.is_none() { - has_field_constraints.extend(quote! { + constraints.push(parse_quote! { HasField< #field_symbol, Value = #provider_type > }); @@ -62,7 +62,7 @@ pub fn derive_use_fields_impl( } }); } else { - has_field_constraints.extend(quote! { + constraints.push(parse_quote! { HasFieldMut< #field_symbol, Value = #provider_type > }); @@ -77,7 +77,7 @@ pub fn derive_use_fields_impl( parse_quote! { impl< #context_type > #provider_name < #context_type > for UseFields where - #context_type: #super_traits + #has_field_constraints + #context_type: #constraints { #methods } diff --git a/crates/cgp-runtime/src/traits/has_runtime.rs b/crates/cgp-runtime/src/traits/has_runtime.rs index 718d134..1c2ad20 100644 --- a/crates/cgp-runtime/src/traits/has_runtime.rs +++ b/crates/cgp-runtime/src/traits/has_runtime.rs @@ -11,6 +11,7 @@ use crate::HasRuntimeType; }] pub trait HasRuntime: HasRuntimeType { fn runtime(&self) -> &Self::Runtime; + // fn runtime_mut(&mut self) -> &mut Self::Runtime; } impl RuntimeGetter for WithProvider From 1205625ff4c270b770cc56f202970c3235092ff8 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 11:44:29 +0000 Subject: [PATCH 11/19] Implement UseDelegate if there is only one method --- .../src/getter_component/derive.rs | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index f4e190f..0668139 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -20,11 +20,21 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res let use_fields_impl = derive_use_fields_impl(&spec, &consumer_trait, &fields); - Ok(quote! { + let m_field: Option<[GetterField; 1]> = fields.try_into().ok(); + + let mut derived = quote! { #derived_component #use_fields_impl - }) + }; + + if let Some([field]) = m_field { + let use_field_impl = derive_use_field_impl(&spec, &consumer_trait, &field); + + derived.extend(use_field_impl.to_token_stream()) + } + + Ok(derived) } pub struct GetterField { @@ -42,6 +52,7 @@ pub fn derive_use_fields_impl( let context_type = &spec.context_type; let provider_name = &spec.provider_name; + // FIXME: replace `Self` with `Context` inside super trait bound let mut constraints = consumer_trait.supertraits.clone(); let mut methods: TokenStream = TokenStream::new(); @@ -84,6 +95,51 @@ pub fn derive_use_fields_impl( } } +pub fn derive_use_field_impl( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, + field: &GetterField, +) -> ItemImpl { + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; + + // FIXME: replace `Self` with `Context` inside super trait bound + let mut constraints = consumer_trait.supertraits.clone(); + + let field_name = &field.field_name; + let provider_type = &field.provider_type; + + let method = if field.field_mut.is_none() { + constraints.push(parse_quote! { + HasField< Tag, Value = #provider_type > + }); + + quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + context.get_field( ::core::marker::PhantomData ) + } + } + } else { + constraints.push(parse_quote! { + HasFieldMut< Tag, Value = #provider_type > + }); + + quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + context.get_field_mut( ::core::marker::PhantomData ) + } + } + }; + + parse_quote! { + impl< #context_type, Tag > #provider_name < #context_type > for UseField + where + #context_type: #constraints + { + #method + } + } +} pub fn parse_getter_fields( spec: &ComponentSpec, consumer_trait: &ItemTrait, From 47a6e18a7a6ee823bb24258c587a2a4d5b69f4ec Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 12:41:49 +0000 Subject: [PATCH 12/19] Derive macro is now working --- .../src/getter_component/derive.rs | 63 ++++++++++++++++++- crates/cgp-core/src/prelude.rs | 4 +- crates/cgp-runtime/src/traits/has_runtime.rs | 14 +---- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 0668139..70870ca 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -30,8 +30,10 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res if let Some([field]) = m_field { let use_field_impl = derive_use_field_impl(&spec, &consumer_trait, &field); + let use_provider_impl = derive_with_provider_impl(&spec, &consumer_trait, &field); - derived.extend(use_field_impl.to_token_stream()) + derived.extend(use_field_impl); + derived.extend(use_provider_impl); } Ok(derived) @@ -99,7 +101,7 @@ pub fn derive_use_field_impl( spec: &ComponentSpec, consumer_trait: &ItemTrait, field: &GetterField, -) -> ItemImpl { +) -> TokenStream { let context_type = &spec.context_type; let provider_name = &spec.provider_name; @@ -131,15 +133,70 @@ pub fn derive_use_field_impl( } }; - parse_quote! { + let use_field_impl: ItemImpl = parse_quote! { impl< #context_type, Tag > #provider_name < #context_type > for UseField where #context_type: #constraints { #method } + }; + + quote! { + #use_field_impl + } +} + +pub fn derive_with_provider_impl( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, + field: &GetterField, +) -> TokenStream { + let component_name = &spec.component_name; + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; + + // FIXME: replace `Self` with `Context` inside super trait bound + let context_constraints = consumer_trait.supertraits.clone(); + + let field_name = &field.field_name; + let provider_type = &field.provider_type; + + let provider_constraint = if field.field_mut.is_none() { + quote! { + FieldGetter< #context_type, #component_name, Value = #provider_type > + } + } else { + quote! { + MutFieldGetter< #context_type, #component_name, Value = #provider_type > + } + }; + + let method = if field.field_mut.is_none() { + quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + Provider::get_field(context, ::core::marker::PhantomData ) + } + } + } else { + quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + Provider::get_field_mut(context, ::core::marker::PhantomData ) + } + } + }; + + quote! { + impl< #context_type, Provider > #provider_name < #context_type > for WithProvider + where + #context_type: #context_constraints, + Provider: #provider_constraint, + { + #method + } } } + pub fn parse_getter_fields( spec: &ComponentSpec, consumer_trait: &ItemTrait, diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 0d994cf..2a730dc 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -5,6 +5,8 @@ pub use cgp_component_macro::{ replace_with, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; -pub use cgp_field::{Char, Cons, Either, HasField, HasFieldMut, Nil, UseField, Void}; +pub use cgp_field::{ + Char, Cons, Either, FieldGetter, HasField, HasFieldMut, MutFieldGetter, Nil, UseField, Void, +}; pub use cgp_field_macro::{product, symbol, HasField, Product, Sum}; pub use cgp_type::{HasType, ProvideType, UseType}; diff --git a/crates/cgp-runtime/src/traits/has_runtime.rs b/crates/cgp-runtime/src/traits/has_runtime.rs index 1c2ad20..387c1ba 100644 --- a/crates/cgp-runtime/src/traits/has_runtime.rs +++ b/crates/cgp-runtime/src/traits/has_runtime.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use cgp_core::component::WithProvider; use cgp_core::field::FieldGetter; use cgp_core::prelude::*; @@ -7,19 +5,9 @@ use cgp_core::prelude::*; use crate::HasRuntimeType; #[cgp_getter { + context: App, provider: RuntimeGetter, }] pub trait HasRuntime: HasRuntimeType { fn runtime(&self) -> &Self::Runtime; - // fn runtime_mut(&mut self) -> &mut Self::Runtime; -} - -impl RuntimeGetter for WithProvider -where - Context: HasRuntimeType, - Provider: FieldGetter, -{ - fn runtime(context: &Context) -> &Runtime { - Provider::get_field(context, PhantomData) - } } From 219831aa79a6170ccd707942dd49df89d6664baa Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 12:47:42 +0000 Subject: [PATCH 13/19] Break down implementation into smaller modules --- .../src/getter_component/derive.rs | 316 +----------------- .../src/getter_component/getter_field.rs | 9 + .../src/getter_component/mod.rs | 6 + .../src/getter_component/parse.rs | 144 ++++++++ .../src/getter_component/symbol.rs | 9 + .../src/getter_component/use_field.rs | 56 ++++ .../src/getter_component/use_fields.rs | 59 ++++ .../src/getter_component/with_provider.rs | 56 ++++ 8 files changed, 346 insertions(+), 309 deletions(-) create mode 100644 crates/cgp-component-macro-lib/src/getter_component/getter_field.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/parse.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/symbol.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/use_field.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/use_fields.rs create mode 100644 crates/cgp-component-macro-lib/src/getter_component/with_provider.rs diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index 70870ca..e29a7d3 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -1,14 +1,14 @@ -use alloc::string::ToString; -use alloc::vec::Vec; use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; -use syn::spanned::Spanned; -use syn::token::Mut; -use syn::{parse_quote, Error, FnArg, Ident, ItemImpl, ItemTrait, ReturnType, TraitItem, Type}; +use quote::quote; +use syn::ItemTrait; use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; -use crate::derive_component::replace_self_type::replace_self_type; +use crate::getter_component::getter_field::GetterField; +use crate::getter_component::parse::parse_getter_fields; +use crate::getter_component::use_field::derive_use_field_impl; +use crate::getter_component::use_fields::derive_use_fields_impl; +use crate::getter_component::with_provider::derive_with_provider_impl; pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Result { let spec: ComponentSpec = syn::parse2(attr)?; @@ -38,305 +38,3 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res Ok(derived) } - -pub struct GetterField { - pub field_name: Ident, - pub field_type: Type, - pub provider_type: Type, - pub field_mut: Option, -} - -pub fn derive_use_fields_impl( - spec: &ComponentSpec, - consumer_trait: &ItemTrait, - fields: &[GetterField], -) -> ItemImpl { - let context_type = &spec.context_type; - let provider_name = &spec.provider_name; - - // FIXME: replace `Self` with `Context` inside super trait bound - let mut constraints = consumer_trait.supertraits.clone(); - - let mut methods: TokenStream = TokenStream::new(); - - for field in fields { - let field_name = &field.field_name; - let provider_type = &field.provider_type; - let field_symbol = symbol_from_string(&field.field_name.to_string()); - - if field.field_mut.is_none() { - constraints.push(parse_quote! { - HasField< #field_symbol, Value = #provider_type > - }); - - methods.extend(quote! { - fn #field_name( context: & #context_type ) -> & #provider_type { - context.get_field( ::core::marker::PhantomData::< #field_symbol > ) - } - }); - } else { - constraints.push(parse_quote! { - HasFieldMut< #field_symbol, Value = #provider_type > - }); - - methods.extend(quote! { - fn #field_name( context: &mut #context_type ) -> &mut #provider_type { - context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) - } - }); - } - } - - parse_quote! { - impl< #context_type > #provider_name < #context_type > for UseFields - where - #context_type: #constraints - { - #methods - } - } -} - -pub fn derive_use_field_impl( - spec: &ComponentSpec, - consumer_trait: &ItemTrait, - field: &GetterField, -) -> TokenStream { - let context_type = &spec.context_type; - let provider_name = &spec.provider_name; - - // FIXME: replace `Self` with `Context` inside super trait bound - let mut constraints = consumer_trait.supertraits.clone(); - - let field_name = &field.field_name; - let provider_type = &field.provider_type; - - let method = if field.field_mut.is_none() { - constraints.push(parse_quote! { - HasField< Tag, Value = #provider_type > - }); - - quote! { - fn #field_name( context: & #context_type ) -> & #provider_type { - context.get_field( ::core::marker::PhantomData ) - } - } - } else { - constraints.push(parse_quote! { - HasFieldMut< Tag, Value = #provider_type > - }); - - quote! { - fn #field_name( context: &mut #context_type ) -> &mut #provider_type { - context.get_field_mut( ::core::marker::PhantomData ) - } - } - }; - - let use_field_impl: ItemImpl = parse_quote! { - impl< #context_type, Tag > #provider_name < #context_type > for UseField - where - #context_type: #constraints - { - #method - } - }; - - quote! { - #use_field_impl - } -} - -pub fn derive_with_provider_impl( - spec: &ComponentSpec, - consumer_trait: &ItemTrait, - field: &GetterField, -) -> TokenStream { - let component_name = &spec.component_name; - let context_type = &spec.context_type; - let provider_name = &spec.provider_name; - - // FIXME: replace `Self` with `Context` inside super trait bound - let context_constraints = consumer_trait.supertraits.clone(); - - let field_name = &field.field_name; - let provider_type = &field.provider_type; - - let provider_constraint = if field.field_mut.is_none() { - quote! { - FieldGetter< #context_type, #component_name, Value = #provider_type > - } - } else { - quote! { - MutFieldGetter< #context_type, #component_name, Value = #provider_type > - } - }; - - let method = if field.field_mut.is_none() { - quote! { - fn #field_name( context: & #context_type ) -> & #provider_type { - Provider::get_field(context, ::core::marker::PhantomData ) - } - } - } else { - quote! { - fn #field_name( context: &mut #context_type ) -> &mut #provider_type { - Provider::get_field_mut(context, ::core::marker::PhantomData ) - } - } - }; - - quote! { - impl< #context_type, Provider > #provider_name < #context_type > for WithProvider - where - #context_type: #context_constraints, - Provider: #provider_constraint, - { - #method - } - } -} - -pub fn parse_getter_fields( - spec: &ComponentSpec, - consumer_trait: &ItemTrait, -) -> syn::Result> { - if !consumer_trait.generics.params.is_empty() { - return Err(Error::new( - consumer_trait.generics.params.span(), - "getter trait cannot contain generic parameters", - )); - } - - let mut fields = Vec::new(); - - for item in consumer_trait.items.iter() { - match item { - TraitItem::Fn(method) => { - let signature = &method.sig; - - if signature.constness.is_some() { - return Err(Error::new( - signature.constness.span(), - "getter method must not be const fn", - )); - } - - if signature.asyncness.is_some() { - return Err(Error::new( - signature.asyncness.span(), - "getter method must not be async fn", - )); - } - - if signature.unsafety.is_some() { - return Err(Error::new( - signature.unsafety.span(), - "getter method must not be unsafe fn", - )); - } - - if !signature.generics.params.is_empty() { - return Err(Error::new( - signature.generics.params.span(), - "getter method must not contain generic param", - )); - } - - if signature.generics.where_clause.is_some() { - return Err(Error::new( - signature.generics.where_clause.span(), - "getter method must not contain where clause", - )); - } - - let field_name = signature.ident.clone(); - - let [arg]: [&FnArg; 1] = signature - .inputs - .iter() - .collect::>() - .try_into() - .map_err(|_| { - Error::new( - signature.inputs.span(), - "getter method must contain exactly one `&self` argument", - ) - })?; - - let field_mut = match arg { - FnArg::Receiver(receiver) => { - if receiver.reference.is_none() { - return Err(Error::new( - receiver.span(), - "first argument to getter method must be a reference to self, i.e. `&self`" - )); - } - - receiver.mutability - } - _ => { - return Err(Error::new( - arg.span(), - "first argument to getter method must be `&self`", - )) - } - }; - - let field_type: Type = match &signature.output { - ReturnType::Default => parse_quote!(()), - ReturnType::Type(_, ty) => { - let ty = ty.as_ref().clone(); - match &ty { - Type::Reference(type_ref) => { - if type_ref.mutability.is_some() != field_mut.is_some() { - return Err(Error::new( - type_ref.span(), - "return type have the same mutability as the self reference", - )); - } - - type_ref.elem.as_ref().clone() - } - _ => { - return Err(Error::new( - ty.span(), - "return type must be a reference", - )) - } - } - } - }; - - let provider_type: Type = syn::parse2(replace_self_type( - field_type.to_token_stream(), - &spec.context_type, - &Vec::new(), - ))?; - - fields.push(GetterField { - field_name, - field_type, - provider_type, - field_mut, - }) - } - _ => { - return Err(Error::new( - item.span(), - "getter trait can only contain getter methods", - )) - } - } - } - - Ok(fields) -} - -pub fn symbol_from_string(value: &str) -> Type { - value - .chars() - .rfold(parse_quote! { Nil }, |tail, c: char| -> Type { - parse_quote!( Cons< Char< #c >, #tail > ) - }) -} diff --git a/crates/cgp-component-macro-lib/src/getter_component/getter_field.rs b/crates/cgp-component-macro-lib/src/getter_component/getter_field.rs new file mode 100644 index 0000000..7e48535 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/getter_field.rs @@ -0,0 +1,9 @@ +use syn::token::Mut; +use syn::{Ident, Type}; + +pub struct GetterField { + pub field_name: Ident, + pub field_type: Type, + pub provider_type: Type, + pub field_mut: Option, +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/mod.rs b/crates/cgp-component-macro-lib/src/getter_component/mod.rs index 8fe0591..2c81d79 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/mod.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/mod.rs @@ -1 +1,7 @@ pub mod derive; +pub mod getter_field; +pub mod parse; +pub mod symbol; +pub mod use_field; +pub mod use_fields; +pub mod with_provider; diff --git a/crates/cgp-component-macro-lib/src/getter_component/parse.rs b/crates/cgp-component-macro-lib/src/getter_component/parse.rs new file mode 100644 index 0000000..3c6f2f5 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/parse.rs @@ -0,0 +1,144 @@ +use alloc::vec::Vec; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{parse_quote, Error, FnArg, ItemTrait, ReturnType, TraitItem, Type}; + +use crate::derive_component::component_spec::ComponentSpec; +use crate::derive_component::replace_self_type::replace_self_type; +use crate::getter_component::getter_field::GetterField; + +pub fn parse_getter_fields( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, +) -> syn::Result> { + if !consumer_trait.generics.params.is_empty() { + return Err(Error::new( + consumer_trait.generics.params.span(), + "getter trait cannot contain generic parameters", + )); + } + + let mut fields = Vec::new(); + + for item in consumer_trait.items.iter() { + match item { + TraitItem::Fn(method) => { + let signature = &method.sig; + + if signature.constness.is_some() { + return Err(Error::new( + signature.constness.span(), + "getter method must not be const fn", + )); + } + + if signature.asyncness.is_some() { + return Err(Error::new( + signature.asyncness.span(), + "getter method must not be async fn", + )); + } + + if signature.unsafety.is_some() { + return Err(Error::new( + signature.unsafety.span(), + "getter method must not be unsafe fn", + )); + } + + if !signature.generics.params.is_empty() { + return Err(Error::new( + signature.generics.params.span(), + "getter method must not contain generic param", + )); + } + + if signature.generics.where_clause.is_some() { + return Err(Error::new( + signature.generics.where_clause.span(), + "getter method must not contain where clause", + )); + } + + let field_name = signature.ident.clone(); + + let [arg]: [&FnArg; 1] = signature + .inputs + .iter() + .collect::>() + .try_into() + .map_err(|_| { + Error::new( + signature.inputs.span(), + "getter method must contain exactly one `&self` argument", + ) + })?; + + let field_mut = match arg { + FnArg::Receiver(receiver) => { + if receiver.reference.is_none() { + return Err(Error::new( + receiver.span(), + "first argument to getter method must be a reference to self, i.e. `&self`" + )); + } + + receiver.mutability + } + _ => { + return Err(Error::new( + arg.span(), + "first argument to getter method must be `&self`", + )) + } + }; + + let field_type: Type = match &signature.output { + ReturnType::Default => parse_quote!(()), + ReturnType::Type(_, ty) => { + let ty = ty.as_ref().clone(); + match &ty { + Type::Reference(type_ref) => { + if type_ref.mutability.is_some() != field_mut.is_some() { + return Err(Error::new( + type_ref.span(), + "return type have the same mutability as the self reference", + )); + } + + type_ref.elem.as_ref().clone() + } + _ => { + return Err(Error::new( + ty.span(), + "return type must be a reference", + )) + } + } + } + }; + + let provider_type: Type = syn::parse2(replace_self_type( + field_type.to_token_stream(), + &spec.context_type, + &Vec::new(), + ))?; + + fields.push(GetterField { + field_name, + field_type, + provider_type, + field_mut, + }) + } + _ => { + return Err(Error::new( + item.span(), + "getter trait can only contain getter methods", + )) + } + } + } + + Ok(fields) +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/symbol.rs b/crates/cgp-component-macro-lib/src/getter_component/symbol.rs new file mode 100644 index 0000000..87f3678 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/symbol.rs @@ -0,0 +1,9 @@ +use syn::{parse_quote, Type}; + +pub fn symbol_from_string(value: &str) -> Type { + value + .chars() + .rfold(parse_quote! { Nil }, |tail, c: char| -> Type { + parse_quote!( Cons< Char< #c >, #tail > ) + }) +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/use_field.rs b/crates/cgp-component-macro-lib/src/getter_component/use_field.rs new file mode 100644 index 0000000..48c8af5 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/use_field.rs @@ -0,0 +1,56 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, ItemImpl, ItemTrait}; + +use crate::derive_component::component_spec::ComponentSpec; +use crate::getter_component::getter_field::GetterField; + +pub fn derive_use_field_impl( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, + field: &GetterField, +) -> TokenStream { + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; + + // FIXME: replace `Self` with `Context` inside super trait bound + let mut constraints = consumer_trait.supertraits.clone(); + + let field_name = &field.field_name; + let provider_type = &field.provider_type; + + let method = if field.field_mut.is_none() { + constraints.push(parse_quote! { + HasField< Tag, Value = #provider_type > + }); + + quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + context.get_field( ::core::marker::PhantomData ) + } + } + } else { + constraints.push(parse_quote! { + HasFieldMut< Tag, Value = #provider_type > + }); + + quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + context.get_field_mut( ::core::marker::PhantomData ) + } + } + }; + + let use_field_impl: ItemImpl = parse_quote! { + impl< #context_type, Tag > #provider_name < #context_type > for UseField + where + #context_type: #constraints + { + #method + } + }; + + quote! { + #use_field_impl + } +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs b/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs new file mode 100644 index 0000000..e1f7ba1 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs @@ -0,0 +1,59 @@ +use alloc::string::ToString; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, ItemImpl, ItemTrait}; + +use crate::derive_component::component_spec::ComponentSpec; +use crate::getter_component::getter_field::GetterField; +use crate::getter_component::symbol::symbol_from_string; + +pub fn derive_use_fields_impl( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, + fields: &[GetterField], +) -> ItemImpl { + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; + + // FIXME: replace `Self` with `Context` inside super trait bound + let mut constraints = consumer_trait.supertraits.clone(); + + let mut methods: TokenStream = TokenStream::new(); + + for field in fields { + let field_name = &field.field_name; + let provider_type = &field.provider_type; + let field_symbol = symbol_from_string(&field.field_name.to_string()); + + if field.field_mut.is_none() { + constraints.push(parse_quote! { + HasField< #field_symbol, Value = #provider_type > + }); + + methods.extend(quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + context.get_field( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } else { + constraints.push(parse_quote! { + HasFieldMut< #field_symbol, Value = #provider_type > + }); + + methods.extend(quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } + } + + parse_quote! { + impl< #context_type > #provider_name < #context_type > for UseFields + where + #context_type: #constraints + { + #methods + } + } +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/with_provider.rs b/crates/cgp-component-macro-lib/src/getter_component/with_provider.rs new file mode 100644 index 0000000..66e0633 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/with_provider.rs @@ -0,0 +1,56 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::ItemTrait; + +use crate::derive_component::component_spec::ComponentSpec; +use crate::getter_component::getter_field::GetterField; + +pub fn derive_with_provider_impl( + spec: &ComponentSpec, + consumer_trait: &ItemTrait, + field: &GetterField, +) -> TokenStream { + let component_name = &spec.component_name; + let context_type = &spec.context_type; + let provider_name = &spec.provider_name; + + // FIXME: replace `Self` with `Context` inside super trait bound + let context_constraints = consumer_trait.supertraits.clone(); + + let field_name = &field.field_name; + let provider_type = &field.provider_type; + + let provider_constraint = if field.field_mut.is_none() { + quote! { + FieldGetter< #context_type, #component_name, Value = #provider_type > + } + } else { + quote! { + MutFieldGetter< #context_type, #component_name, Value = #provider_type > + } + }; + + let method = if field.field_mut.is_none() { + quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + Provider::get_field(context, ::core::marker::PhantomData ) + } + } + } else { + quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + Provider::get_field_mut(context, ::core::marker::PhantomData ) + } + } + }; + + quote! { + impl< #context_type, Provider > #provider_name < #context_type > for WithProvider + where + #context_type: #context_constraints, + Provider: #provider_constraint, + { + #method + } + } +} From c3ec1e44eb27d3aa829904a4112e2974b971e30b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 12:59:38 +0000 Subject: [PATCH 14/19] Implement derive auto getter --- .../src/getter_component/blanket.rs | 56 +++++++++++++++++++ .../src/getter_component/derive.rs | 36 ++++++++++-- .../src/getter_component/mod.rs | 1 + .../src/getter_component/parse.rs | 7 +-- crates/cgp-component-macro-lib/src/lib.rs | 2 +- crates/cgp-component-macro/src/lib.rs | 7 +++ 6 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 crates/cgp-component-macro-lib/src/getter_component/blanket.rs diff --git a/crates/cgp-component-macro-lib/src/getter_component/blanket.rs b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs new file mode 100644 index 0000000..f1317a1 --- /dev/null +++ b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs @@ -0,0 +1,56 @@ +use alloc::string::ToString; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, Ident, ItemImpl, ItemTrait}; + +use crate::getter_component::getter_field::GetterField; +use crate::getter_component::symbol::symbol_from_string; + +pub fn derive_blanket_impl( + context_type: &Ident, + consumer_trait: &ItemTrait, + fields: &[GetterField], +) -> ItemImpl { + let consumer_name = &consumer_trait.ident; + + let mut constraints = consumer_trait.supertraits.clone(); + + let mut methods: TokenStream = TokenStream::new(); + + for field in fields { + let field_name = &field.field_name; + let provider_type = &field.provider_type; + let field_symbol = symbol_from_string(&field.field_name.to_string()); + + if field.field_mut.is_none() { + constraints.push(parse_quote! { + HasField< #field_symbol, Value = #provider_type > + }); + + methods.extend(quote! { + fn #field_name( context: & #context_type ) -> & #provider_type { + context.get_field( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } else { + constraints.push(parse_quote! { + HasFieldMut< #field_symbol, Value = #provider_type > + }); + + methods.extend(quote! { + fn #field_name( context: &mut #context_type ) -> &mut #provider_type { + context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) + } + }); + } + } + + parse_quote! { + impl< #context_type > #consumer_name for #context_type + where + #context_type: #constraints + { + #methods + } + } +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/derive.rs b/crates/cgp-component-macro-lib/src/getter_component/derive.rs index e29a7d3..bcb3583 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/derive.rs @@ -1,22 +1,23 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::ItemTrait; +use syn::{Error, Ident, ItemTrait}; use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::derive::derive_component_with_ast; +use crate::getter_component::blanket::derive_blanket_impl; use crate::getter_component::getter_field::GetterField; use crate::getter_component::parse::parse_getter_fields; use crate::getter_component::use_field::derive_use_field_impl; use crate::getter_component::use_fields::derive_use_fields_impl; use crate::getter_component::with_provider::derive_with_provider_impl; -pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Result { +pub fn derive_getter_component(attr: TokenStream, body: TokenStream) -> syn::Result { let spec: ComponentSpec = syn::parse2(attr)?; - let consumer_trait: ItemTrait = syn::parse2(item)?; + let consumer_trait: ItemTrait = syn::parse2(body)?; let derived_component = derive_component_with_ast(&spec, &consumer_trait)?; - let fields = parse_getter_fields(&spec, &consumer_trait)?; + let fields = parse_getter_fields(&spec.context_type, &consumer_trait)?; let use_fields_impl = derive_use_fields_impl(&spec, &consumer_trait, &fields); @@ -38,3 +39,28 @@ pub fn derive_getter_component(attr: TokenStream, item: TokenStream) -> syn::Res Ok(derived) } + +pub fn derive_auto_getter_component( + attr: TokenStream, + body: TokenStream, +) -> syn::Result { + if !attr.is_empty() { + return Err(Error::new( + Span::call_site(), + "#[derive_auto_getter] does not accept any attribute argument", + )); + } + + let consumer_trait: ItemTrait = syn::parse2(body)?; + + let context_type = Ident::new("Context", Span::call_site()); + + let fields = parse_getter_fields(&context_type, &consumer_trait)?; + + let blanket_impl = derive_blanket_impl(&context_type, &consumer_trait, &fields); + + Ok(quote! { + #consumer_trait + #blanket_impl + }) +} diff --git a/crates/cgp-component-macro-lib/src/getter_component/mod.rs b/crates/cgp-component-macro-lib/src/getter_component/mod.rs index 2c81d79..ec54db7 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/mod.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/mod.rs @@ -1,3 +1,4 @@ +pub mod blanket; pub mod derive; pub mod getter_field; pub mod parse; diff --git a/crates/cgp-component-macro-lib/src/getter_component/parse.rs b/crates/cgp-component-macro-lib/src/getter_component/parse.rs index 3c6f2f5..63391ee 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/parse.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/parse.rs @@ -1,14 +1,13 @@ use alloc::vec::Vec; use quote::ToTokens; use syn::spanned::Spanned; -use syn::{parse_quote, Error, FnArg, ItemTrait, ReturnType, TraitItem, Type}; +use syn::{parse_quote, Error, FnArg, Ident, ItemTrait, ReturnType, TraitItem, Type}; -use crate::derive_component::component_spec::ComponentSpec; use crate::derive_component::replace_self_type::replace_self_type; use crate::getter_component::getter_field::GetterField; pub fn parse_getter_fields( - spec: &ComponentSpec, + context_type: &Ident, consumer_trait: &ItemTrait, ) -> syn::Result> { if !consumer_trait.generics.params.is_empty() { @@ -120,7 +119,7 @@ pub fn parse_getter_fields( let provider_type: Type = syn::parse2(replace_self_type( field_type.to_token_stream(), - &spec.context_type, + context_type, &Vec::new(), ))?; diff --git a/crates/cgp-component-macro-lib/src/lib.rs b/crates/cgp-component-macro-lib/src/lib.rs index 32f282a..6534923 100644 --- a/crates/cgp-component-macro-lib/src/lib.rs +++ b/crates/cgp-component-macro-lib/src/lib.rs @@ -22,6 +22,6 @@ mod tests; pub use crate::delegate_components::delegate_components; pub use crate::derive_component::derive_component; pub use crate::for_each_replace::{handle_for_each_replace, handle_replace}; -pub use crate::getter_component::derive::derive_getter_component; +pub use crate::getter_component::derive::{derive_auto_getter_component, derive_getter_component}; pub use crate::preset::define_preset; pub use crate::type_component::derive::derive_type_component; diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index f7f836c..7ea0904 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -22,6 +22,13 @@ pub fn cgp_getter(attr: TokenStream, item: TokenStream) -> TokenStream { .into() } +#[proc_macro_attribute] +pub fn cgp_auto_getter(attr: TokenStream, item: TokenStream) -> TokenStream { + cgp_component_macro_lib::derive_auto_getter_component(attr.into(), item.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + #[proc_macro] pub fn delegate_components(body: TokenStream) -> TokenStream { cgp_component_macro_lib::delegate_components(body.into()) From bda46a93e4e9ee550a3f191191cfabefe0f463fd Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 13:02:46 +0000 Subject: [PATCH 15/19] cgp_auto_getter macro is now working --- .../src/getter_component/blanket.rs | 8 ++++---- crates/cgp-core/src/prelude.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/getter_component/blanket.rs b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs index f1317a1..5fd2adf 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/blanket.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs @@ -28,8 +28,8 @@ pub fn derive_blanket_impl( }); methods.extend(quote! { - fn #field_name( context: & #context_type ) -> & #provider_type { - context.get_field( ::core::marker::PhantomData::< #field_symbol > ) + fn #field_name( &self ) -> & #provider_type { + self.get_field( ::core::marker::PhantomData::< #field_symbol > ) } }); } else { @@ -38,8 +38,8 @@ pub fn derive_blanket_impl( }); methods.extend(quote! { - fn #field_name( context: &mut #context_type ) -> &mut #provider_type { - context.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) + fn #field_name( &mut self ) -> &mut #provider_type { + self.get_field_mut( ::core::marker::PhantomData::< #field_symbol > ) } }); } diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 2a730dc..42a3c1c 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -1,8 +1,8 @@ pub use cgp_async::{async_trait, Async, MaybeSend, MaybeStatic, MaybeSync}; pub use cgp_component::{DelegateComponent, HasComponents, UseFields, WithContext, WithProvider}; pub use cgp_component_macro::{ - cgp_component, cgp_getter, cgp_preset, cgp_type, delegate_components, for_each_replace, - replace_with, + cgp_auto_getter, cgp_component, cgp_getter, cgp_preset, cgp_type, delegate_components, + for_each_replace, replace_with, }; pub use cgp_error::{CanRaiseError, CanWrapError, HasErrorType}; pub use cgp_field::{ From 9e0b49537cb0eea8fec644eca8890689041422bb Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 16:48:03 +0000 Subject: [PATCH 16/19] Fix clippy --- .../src/derive_component/component_spec.rs | 2 +- crates/cgp-component-macro-lib/src/derive_component/derive.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs index ecc4a7e..0a52b56 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/component_spec.rs @@ -21,7 +21,7 @@ pub struct ComponentNameSpec { pub component_params: Punctuated, } -static VALID_KEYS: [&'static str; 3] = ["context", "provider", "name"]; +static VALID_KEYS: [&str; 3] = ["context", "provider", "name"]; impl Parse for ComponentSpec { fn parse(input: ParseStream) -> syn::Result { diff --git a/crates/cgp-component-macro-lib/src/derive_component/derive.rs b/crates/cgp-component-macro-lib/src/derive_component/derive.rs index 81c53be..d3c805e 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/derive.rs @@ -25,9 +25,9 @@ pub fn derive_component_with_ast( let component_struct = derive_component_name_struct(&spec.component_name, &spec.component_params); - let provider_trait = derive_provider_trait(&consumer_trait, provider_name, context_type)?; + let provider_trait = derive_provider_trait(consumer_trait, provider_name, context_type)?; - let consumer_impl = derive_consumer_impl(&consumer_trait, provider_name, context_type); + let consumer_impl = derive_consumer_impl(consumer_trait, provider_name, context_type); let provider_impl = derive_provider_impl( &provider_trait, From 146a2c72aa9bbc47cb0239f3a3c5d329815769d0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 16:48:45 +0000 Subject: [PATCH 17/19] Fix formatting --- .github/workflows/tests.yml | 2 +- .rustfmt.toml | 7 ++----- .../cgp-component-macro-lib/src/derive_component/entry.rs | 2 +- .../src/getter_component/blanket.rs | 1 + .../cgp-component-macro-lib/src/getter_component/parse.rs | 1 + .../src/getter_component/use_fields.rs | 1 + .../cgp-component-macro-lib/src/type_component/derive.rs | 1 + crates/cgp-error-anyhow/src/impls/debug_error.rs | 2 +- crates/cgp-error-eyre/src/impls/debug_error.rs | 2 +- crates/cgp-error-std/src/types/wrap.rs | 3 +-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ec4b149..61fa580 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly override: true - uses: actions-rs/cargo@v1 with: diff --git a/.rustfmt.toml b/.rustfmt.toml index 1900537..1161809 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,7 +1,4 @@ max_width = 100 reorder_imports = true - -# nightly only - -# group_imports = "StdExternalCrate" -# imports_granularity = "Module" \ No newline at end of file +group_imports = "StdExternalCrate" +imports_granularity = "Module" \ No newline at end of file diff --git a/crates/cgp-component-macro-lib/src/derive_component/entry.rs b/crates/cgp-component-macro-lib/src/derive_component/entry.rs index a4032a5..dd0b83d 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/entry.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/entry.rs @@ -1,6 +1,6 @@ use alloc::collections::BTreeMap; - use alloc::string::{String, ToString}; + use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Colon, Comma}; diff --git a/crates/cgp-component-macro-lib/src/getter_component/blanket.rs b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs index 5fd2adf..ae36a80 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/blanket.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/blanket.rs @@ -1,4 +1,5 @@ use alloc::string::ToString; + use proc_macro2::TokenStream; use quote::quote; use syn::{parse_quote, Ident, ItemImpl, ItemTrait}; diff --git a/crates/cgp-component-macro-lib/src/getter_component/parse.rs b/crates/cgp-component-macro-lib/src/getter_component/parse.rs index 63391ee..3b8fb72 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/parse.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/parse.rs @@ -1,4 +1,5 @@ use alloc::vec::Vec; + use quote::ToTokens; use syn::spanned::Spanned; use syn::{parse_quote, Error, FnArg, Ident, ItemTrait, ReturnType, TraitItem, Type}; diff --git a/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs b/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs index e1f7ba1..1dbc408 100644 --- a/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs +++ b/crates/cgp-component-macro-lib/src/getter_component/use_fields.rs @@ -1,4 +1,5 @@ use alloc::string::ToString; + use proc_macro2::TokenStream; use quote::quote; use syn::{parse_quote, ItemImpl, ItemTrait}; diff --git a/crates/cgp-component-macro-lib/src/type_component/derive.rs b/crates/cgp-component-macro-lib/src/type_component/derive.rs index 629d950..58fd246 100644 --- a/crates/cgp-component-macro-lib/src/type_component/derive.rs +++ b/crates/cgp-component-macro-lib/src/type_component/derive.rs @@ -1,5 +1,6 @@ use alloc::format; use alloc::vec::Vec; + use proc_macro2::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; diff --git a/crates/cgp-error-anyhow/src/impls/debug_error.rs b/crates/cgp-error-anyhow/src/impls/debug_error.rs index e04dd63..75d6928 100644 --- a/crates/cgp-error-anyhow/src/impls/debug_error.rs +++ b/crates/cgp-error-anyhow/src/impls/debug_error.rs @@ -1,6 +1,6 @@ +use alloc::format; use core::fmt::Debug; -use alloc::format; use anyhow::{anyhow, Error}; use cgp_core::error::{ErrorRaiser, ErrorWrapper}; use cgp_core::prelude::*; diff --git a/crates/cgp-error-eyre/src/impls/debug_error.rs b/crates/cgp-error-eyre/src/impls/debug_error.rs index d44b3b0..abbe4ce 100644 --- a/crates/cgp-error-eyre/src/impls/debug_error.rs +++ b/crates/cgp-error-eyre/src/impls/debug_error.rs @@ -1,6 +1,6 @@ +use alloc::format; use core::fmt::Debug; -use alloc::format; use cgp_core::error::{ErrorRaiser, ErrorWrapper}; use cgp_core::prelude::*; use eyre::{eyre, Error}; diff --git a/crates/cgp-error-std/src/types/wrap.rs b/crates/cgp-error-std/src/types/wrap.rs index fa2e1d9..4e8675c 100644 --- a/crates/cgp-error-std/src/types/wrap.rs +++ b/crates/cgp-error-std/src/types/wrap.rs @@ -1,8 +1,7 @@ +use alloc::string::String; use core::error::Error as StdError; use core::fmt::{Debug, Display}; -use alloc::string::String; - use crate::Error; pub struct WrapError { From c362ce9f159052c00f089aa613202cfc44b8381b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 17:13:49 +0000 Subject: [PATCH 18/19] Add changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ef1efb..20a29e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## v0.3.0 (pre-release) +- Introduce Accessor Component Macros - [#56](https://github.com/contextgeneric/cgp/pull/55) + - Introduce `#[cgp_getter]` attribute macro that extends `#[cgp_component]` and implement + `UseFields` and `UseField` for accessor traits. + - Introduce `#[cgp_auto_getter]` attribute macro for deriving accessor traits with + blanket implementations that use `HasField` directly. + - Introduce `cgp_type!` macro for defining simple abstract CGP types - [#55](https://github.com/contextgeneric/cgp/pull/55) - Use `cgp_type!` to derive `HasErrorType` and `HasRuntimeType`. From e57385f50371b96b65d684c6f7c3c4c40b70d254 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 8 Jan 2025 17:14:54 +0000 Subject: [PATCH 19/19] Fix CI --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 61fa580..eb746be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,6 +26,7 @@ jobs: with: toolchain: nightly override: true + components: rustfmt - uses: actions-rs/cargo@v1 with: command: fmt