From 7c680b70243e9788c7dcecfdd644f02f83e8e983 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 7 Dec 2024 22:58:04 +0100 Subject: [PATCH] Redesign `derive_component` to `cgp_component` with improved syntax (#38) * Rename derive_component to cgp_component * Redesign syntax for cgp_component macro * Update tests * Add changelog --- CHANGELOG.md | 9 +++ .../src/derive_component/component_spec.rs | 73 +++++++++++++++---- .../src/derive_component/entry.rs | 36 +++++++++ .../src/derive_component/mod.rs | 1 + .../src/tests/derive_component.rs | 10 ++- crates/cgp-component-macro/src/lib.rs | 2 +- crates/cgp-component/src/lib.rs | 2 +- crates/cgp-core/src/prelude.rs | 2 +- crates/cgp-error/src/can_raise_error.rs | 6 +- crates/cgp-error/src/has_error_type.rs | 7 +- crates/cgp-inner/src/lib.rs | 7 +- crates/cgp-run/src/lib.rs | 4 +- crates/cgp-type/src/traits/has_type.rs | 7 +- 13 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 crates/cgp-component-macro-lib/src/derive_component/entry.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c8f71d5..65ca093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Pre-Release +- Redesign `derive_component` to `cgp_component` with improved syntax - [#38](https://github.com/contextgeneric/cgp/pull/38) + - Rename the attribute `#[derive_component]` to `#[cgp_component]` + - The macro syntax has been changed as follows: + - Old: `#[derive_component(NameGetterComponent, NameGetter)]` + - New: `#[cgp_component { name: NameGetterComponent, context: MyContext, provider: NameGetter }]` + - For migration, the following regex can be used in a global search and replace: + - Search: `#\[derive_component\(([\w<>, ]+), (\w+)<(\w+)>\)\]` + - Replace: `#[cgp_component {\n name: $1,\n provider: $2,\n context: $3,\n}]` + - Support async-generic feature flags in cgp-async - [#37](https://github.com/contextgeneric/cgp/pull/37) - Introduce the following feature flags to `cgp-async`: - `async` 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 8c0a0b5..d0c9a10 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 @@ -1,7 +1,11 @@ +use proc_macro2::Span; +use quote::ToTokens; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Comma, Gt, Lt}; -use syn::Ident; +use syn::{Error, Ident}; + +use crate::derive_component::entry::Entries; pub struct ComponentSpec { pub provider_name: Ident, @@ -10,7 +14,60 @@ pub struct ComponentSpec { pub component_params: Punctuated, } +pub struct ComponentNameSpec { + pub component_name: Ident, + pub component_params: Punctuated, +} + impl Parse for ComponentSpec { + fn parse(input: ParseStream) -> syn::Result { + let Entries { entries } = input.parse()?; + + let context_type: Ident = { + let raw_context_type = entries.get(&Ident::new("context", Span::call_site())); + + if let Some(context_type) = raw_context_type { + syn::parse2(context_type.to_token_stream())? + } else { + Ident::new("Context", Span::call_site()) + } + }; + + let provider_name: Ident = { + let raw_provider_name = entries + .get(&Ident::new("provider", Span::call_site())) + .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())); + + if let Some(raw_component_name) = raw_component_name { + let ComponentNameSpec { + component_name, + component_params, + } = syn::parse2(raw_component_name.to_token_stream())?; + (component_name, component_params) + } else { + ( + Ident::new(&format!("{}Component", provider_name), provider_name.span()), + Punctuated::default(), + ) + } + }; + + Ok(ComponentSpec { + component_name, + provider_name, + context_type, + component_params, + }) + } +} + +impl Parse for ComponentNameSpec { fn parse(input: ParseStream) -> syn::Result { let component_name: Ident = input.parse()?; @@ -27,20 +84,8 @@ impl Parse for ComponentSpec { Punctuated::default() }; - let _: Comma = input.parse()?; - - let provider_name: Ident = input.parse()?; - - let _: Lt = input.parse()?; - - let context_type: Ident = input.parse()?; - - let _: Gt = input.parse()?; - - Ok(ComponentSpec { + Ok(Self { component_name, - provider_name, - context_type, component_params, }) } diff --git a/crates/cgp-component-macro-lib/src/derive_component/entry.rs b/crates/cgp-component-macro-lib/src/derive_component/entry.rs new file mode 100644 index 0000000..ad38eed --- /dev/null +++ b/crates/cgp-component-macro-lib/src/derive_component/entry.rs @@ -0,0 +1,36 @@ +use std::collections::BTreeMap; + +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Colon, Comma}; +use syn::{Ident, Type}; + +pub struct Entry { + pub key: Ident, + pub value: Type, +} + +impl Parse for Entry { + fn parse(input: ParseStream) -> syn::Result { + let key = input.parse()?; + let _colon: Colon = input.parse()?; + let value = input.parse()?; + + Ok(Entry { key, value }) + } +} + +pub struct Entries { + 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))); + + Ok(Entries { entries }) + } +} diff --git a/crates/cgp-component-macro-lib/src/derive_component/mod.rs b/crates/cgp-component-macro-lib/src/derive_component/mod.rs index 9f509b5..cc61ec6 100644 --- a/crates/cgp-component-macro-lib/src/derive_component/mod.rs +++ b/crates/cgp-component-macro-lib/src/derive_component/mod.rs @@ -4,6 +4,7 @@ pub mod consumer_impl; pub mod delegate_fn; pub mod delegate_type; pub mod derive; +pub mod entry; pub mod provider_impl; pub mod provider_trait; pub mod replace_self_receiver; 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 5eb987b..8ce2171 100644 --- a/crates/cgp-component-macro-lib/src/tests/derive_component.rs +++ b/crates/cgp-component-macro-lib/src/tests/derive_component.rs @@ -6,7 +6,10 @@ use crate::tests::helper::equal::equal_token_stream; #[test] fn test_basic_derive_component() { derive_component( - quote! { FooComponent, FooProvider }, + quote! { + name: FooComponent, + provider: FooProvider, + }, quote! { pub trait HasFoo { type Foo; @@ -20,7 +23,10 @@ fn test_basic_derive_component() { #[test] fn test_derive_component_with_const_generic() { let derived = derive_component( - quote! { FooComponent, FooProvider }, + quote! { + name: FooComponent, + provider: FooProvider, + }, quote! { pub trait HasFoo { type Foo; diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index a1bef67..afe0d1a 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -7,7 +7,7 @@ extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_attribute] -pub fn derive_component(attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn cgp_component(attr: TokenStream, item: TokenStream) -> TokenStream { cgp_component_macro_lib::derive_component(attr.into(), item.into()).into() } diff --git a/crates/cgp-component/src/lib.rs b/crates/cgp-component/src/lib.rs index 07f6ba9..a209b1f 100644 --- a/crates/cgp-component/src/lib.rs +++ b/crates/cgp-component/src/lib.rs @@ -8,6 +8,6 @@ pub mod traits; pub mod types; -pub use cgp_component_macro::{define_components, delegate_components, derive_component}; +pub use cgp_component_macro::{cgp_component, define_components, delegate_components}; pub use traits::{DelegateComponent, HasComponents}; pub use types::{UseContext, UseDelegate, WithContext, WithProvider}; diff --git a/crates/cgp-core/src/prelude.rs b/crates/cgp-core/src/prelude.rs index 9a30c0f..f4014f0 100644 --- a/crates/cgp-core/src/prelude.rs +++ b/crates/cgp-core/src/prelude.rs @@ -1,6 +1,6 @@ pub use cgp_async::{async_trait, Async, MaybeSend, MaybeStatic, MaybeSync}; pub use cgp_component::{ - define_components, delegate_components, derive_component, DelegateComponent, HasComponents, + cgp_component, define_components, delegate_components, DelegateComponent, HasComponents, }; pub use cgp_error::{CanRaiseError, HasErrorType}; pub use cgp_field::{ diff --git a/crates/cgp-error/src/can_raise_error.rs b/crates/cgp-error/src/can_raise_error.rs index 920a1f7..bdf0441 100644 --- a/crates/cgp-error/src/can_raise_error.rs +++ b/crates/cgp-error/src/can_raise_error.rs @@ -1,4 +1,4 @@ -use cgp_component::{derive_component, DelegateComponent, HasComponents, UseDelegate}; +use cgp_component::{cgp_component, DelegateComponent, HasComponents, UseDelegate}; use crate::has_error_type::HasErrorType; @@ -10,7 +10,9 @@ use crate::has_error_type::HasErrorType; [`err: ParseIntError`](core::num::ParseIntError) and get back a [`Context::Error`](HasErrorType::Error) value. */ -#[derive_component(ErrorRaiserComponent, ErrorRaiser)] +#[cgp_component { + provider: ErrorRaiser +}] pub trait CanRaiseError: HasErrorType { fn raise_error(e: E) -> Self::Error; } diff --git a/crates/cgp-error/src/has_error_type.rs b/crates/cgp-error/src/has_error_type.rs index 1b6f90e..f93441f 100644 --- a/crates/cgp-error/src/has_error_type.rs +++ b/crates/cgp-error/src/has_error_type.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; use cgp_async::Async; -use cgp_component::{derive_component, DelegateComponent, HasComponents, WithProvider}; +use cgp_component::{cgp_component, DelegateComponent, HasComponents, WithProvider}; use cgp_type::traits::has_type::ProvideType; /** @@ -15,7 +15,10 @@ use cgp_type::traits::has_type::ProvideType; parent traits, so that multiple traits can all refer to the same abstract `Self::Error` type. */ -#[derive_component(ErrorTypeComponent, ProvideErrorType)] +#[cgp_component { + name: ErrorTypeComponent, + provider: ProvideErrorType, +}] pub trait HasErrorType { /** The `Error` associated type is also required to implement [`Debug`]. diff --git a/crates/cgp-inner/src/lib.rs b/crates/cgp-inner/src/lib.rs index c0efac5..d48c772 100644 --- a/crates/cgp-inner/src/lib.rs +++ b/crates/cgp-inner/src/lib.rs @@ -2,9 +2,12 @@ extern crate alloc; -use cgp_component::{derive_component, DelegateComponent, HasComponents}; +use cgp_component::{cgp_component, DelegateComponent, HasComponents}; -#[derive_component(InnerComponent, ProvideInner)] +#[cgp_component { + name: InnerComponent, + provider: ProvideInner, +}] pub trait HasInner { type Inner; diff --git a/crates/cgp-run/src/lib.rs b/crates/cgp-run/src/lib.rs index c89e8d9..2802947 100644 --- a/crates/cgp-run/src/lib.rs +++ b/crates/cgp-run/src/lib.rs @@ -9,7 +9,9 @@ use cgp_async::*; use cgp_component::*; use cgp_error::HasErrorType; -#[derive_component(RunnerComponent, Runner)] +#[cgp_component { + provider: Runner, +}] #[async_trait] pub trait CanRun: Async + HasErrorType { async fn run(&self) -> Result<(), Self::Error>; diff --git a/crates/cgp-type/src/traits/has_type.rs b/crates/cgp-type/src/traits/has_type.rs index 980618e..43242be 100644 --- a/crates/cgp-type/src/traits/has_type.rs +++ b/crates/cgp-type/src/traits/has_type.rs @@ -1,6 +1,9 @@ -use cgp_component::{derive_component, DelegateComponent, HasComponents, UseContext, UseDelegate}; +use cgp_component::{cgp_component, DelegateComponent, HasComponents, UseContext, UseDelegate}; -#[derive_component(TypeComponent, ProvideType)] +#[cgp_component { + name: TypeComponent, + provider: ProvideType, +}] pub trait HasType { type Type; }