diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..8683bbb --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,69 @@ +name: Rust Unit Tests +on: + pull_request: {} + push: + branches: main + +env: + CARGO_INCREMENTAL: 0 + CARGO_PROFILE_DEV_DEBUG: 1 + CARGO_PROFILE_RELEASE_DEBUG: 1 + RUST_BACKTRACE: short + CARGO_NET_RETRY: 10 + RUSTUP_MAX_RETRIES: 10 + +# Cancel previous runs of this workflow when a new commit is added to the PR, branch or tag +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/clippy-check@v1 + with: + name: clippy-all-features + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features --all-targets -- -D warnings + + test: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-nextest + run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin + - uses: actions-rs/cargo@v1 + with: + command: test + args: --all-features --no-fail-fast --no-run + - uses: actions-rs/cargo@v1 + with: + command: nextest + args: run --all-features --no-fail-fast --workspace --no-capture diff --git a/crates/cgp-component-macro/src/helper/consumer_impl.rs b/crates/cgp-component-macro/src/helper/consumer_impl.rs index 18db22f..9b3b397 100644 --- a/crates/cgp-component-macro/src/helper/consumer_impl.rs +++ b/crates/cgp-component-macro/src/helper/consumer_impl.rs @@ -1,5 +1,5 @@ use syn::punctuated::Punctuated; -use syn::token::{Brace, For, Impl, Plus}; +use syn::token::{Brace, Comma, For, Impl, Plus}; use syn::{ parse_quote, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Path, TraitItem, TypeParamBound, @@ -15,14 +15,32 @@ pub fn derive_consumer_impl( ) -> ItemImpl { let consumer_name = &consumer_trait.ident; - let provider_generics = { - let mut provider_generics = consumer_trait.generics.clone(); - provider_generics - .params - .insert(0, parse_quote!(#context_type)); - provider_generics.where_clause = None; + let consumer_generic_args = { + let mut generic_args: Punctuated = Punctuated::new(); - provider_generics + for param in consumer_trait.generics.params.iter() { + match param { + GenericParam::Type(ty) => { + generic_args.push(ty.ident.clone()); + } + GenericParam::Const(arg) => { + generic_args.push(arg.ident.clone()); + } + GenericParam::Lifetime(_life) => { + unimplemented!() + } + } + } + + generic_args + }; + + let provider_generic_args = { + let mut generic_args = consumer_generic_args.clone(); + + generic_args.insert(0, parse_quote!(#context_type)); + + generic_args }; let impl_generics = { @@ -52,7 +70,7 @@ pub fn derive_consumer_impl( }; let provider_constraint: Punctuated = parse_quote! { - #provider_name #provider_generics + #provider_name < #provider_generic_args > }; if let Some(where_clause) = &mut impl_generics.where_clause { @@ -103,9 +121,9 @@ pub fn derive_consumer_impl( }; let impl_type = derive_delegate_type_impl( - &trait_type, + trait_type, parse_quote!( - < #context_type :: Components as #provider_name #provider_generics > :: #type_name #type_generics + < #context_type :: Components as #provider_name < #provider_generic_args > > :: #type_name #type_generics ), ); @@ -115,12 +133,7 @@ pub fn derive_consumer_impl( } } - let trait_path: Path = { - let mut trait_generics = consumer_trait.generics.clone(); - trait_generics.where_clause = None; - - parse_quote!( #consumer_name #trait_generics ) - }; + let trait_path: Path = parse_quote!( #consumer_name < #consumer_generic_args > ); ItemImpl { attrs: consumer_trait.attrs.clone(), diff --git a/crates/cgp-component-macro/src/helper/delegate_type.rs b/crates/cgp-component-macro/src/helper/delegate_type.rs index 08498c8..3282390 100644 --- a/crates/cgp-component-macro/src/helper/delegate_type.rs +++ b/crates/cgp-component-macro/src/helper/delegate_type.rs @@ -6,11 +6,11 @@ pub fn derive_delegate_type_impl(trait_type: &TraitItemType, delegated_type: Typ attrs: trait_type.attrs.clone(), vis: Visibility::Inherited, defaultness: None, - type_token: trait_type.type_token.clone(), + type_token: trait_type.type_token, ident: trait_type.ident.clone(), generics: trait_type.generics.clone(), eq_token: Eq::default(), ty: delegated_type, - semi_token: trait_type.semi_token.clone(), + semi_token: trait_type.semi_token, } } diff --git a/crates/cgp-component-macro/src/helper/provider_impl.rs b/crates/cgp-component-macro/src/helper/provider_impl.rs index 7c3e9bd..23b3e1a 100644 --- a/crates/cgp-component-macro/src/helper/provider_impl.rs +++ b/crates/cgp-component-macro/src/helper/provider_impl.rs @@ -18,10 +18,24 @@ pub fn derive_provider_impl( let component_type = Ident::new("Component", Span::call_site()); - let provider_generics = { - let mut provider_generics = provider_trait.generics.clone(); - provider_generics.where_clause = None; - provider_generics + let provider_generic_args = { + let mut generic_args: Punctuated = Punctuated::new(); + + for param in provider_trait.generics.params.iter() { + match param { + GenericParam::Type(ty) => { + generic_args.push(ty.ident.clone()); + } + GenericParam::Const(arg) => { + generic_args.push(arg.ident.clone()); + } + GenericParam::Lifetime(_life) => { + unimplemented!() + } + } + } + + generic_args }; let impl_generics = { @@ -37,7 +51,7 @@ pub fn derive_provider_impl( }; let provider_constraint: Punctuated = parse_quote! { - #provider_name #provider_generics + #provider_name < #provider_generic_args > }; if let Some(where_clause) = &mut impl_generics.where_clause { @@ -88,9 +102,9 @@ pub fn derive_provider_impl( }; let impl_type = derive_delegate_type_impl( - &trait_type, + trait_type, parse_quote!( - < #component_type :: Delegate as #provider_name #provider_generics > :: #type_name #type_generics + < #component_type :: Delegate as #provider_name < #provider_generic_args > > :: #type_name #type_generics ), ); @@ -100,12 +114,7 @@ pub fn derive_provider_impl( } } - let trait_path: Path = { - let mut trait_generics = provider_trait.generics.clone(); - trait_generics.where_clause = None; - - parse_quote!( #provider_name #trait_generics ) - }; + let trait_path: Path = parse_quote!( #provider_name < #provider_generic_args > ); ItemImpl { attrs: provider_trait.attrs.clone(), diff --git a/crates/cgp-component-macro/src/lib.rs b/crates/cgp-component-macro/src/lib.rs index b6e0ccd..ceeefb2 100644 --- a/crates/cgp-component-macro/src/lib.rs +++ b/crates/cgp-component-macro/src/lib.rs @@ -2,6 +2,9 @@ extern crate proc_macro; mod helper; +#[cfg(test)] +mod tests; + use proc_macro::TokenStream; #[proc_macro_attribute] diff --git a/crates/cgp-component-macro/src/tests/basic.rs b/crates/cgp-component-macro/src/tests/basic.rs new file mode 100644 index 0000000..7d6e9e4 --- /dev/null +++ b/crates/cgp-component-macro/src/tests/basic.rs @@ -0,0 +1,16 @@ +use crate::helper::derive::derive_component; +use quote::quote; + +#[test] +fn test_basic_derive_component() { + derive_component( + quote! { FooComponent, FooProvider }, + quote! { + pub trait HasFoo { + type Foo; + + fn foo(&self) -> Self::Foo; + } + }, + ); +} diff --git a/crates/cgp-component-macro/src/tests/const_generic.rs b/crates/cgp-component-macro/src/tests/const_generic.rs new file mode 100644 index 0000000..74409e6 --- /dev/null +++ b/crates/cgp-component-macro/src/tests/const_generic.rs @@ -0,0 +1,16 @@ +use crate::helper::derive::derive_component; +use quote::quote; + +#[test] +fn test_derive_component_with_const_generic() { + derive_component( + quote! { FooComponent, FooProvider }, + quote! { + pub trait HasFoo { + type Foo; + + fn foo(&self) -> Self::Foo; + } + }, + ); +} diff --git a/crates/cgp-component-macro/src/tests/mod.rs b/crates/cgp-component-macro/src/tests/mod.rs new file mode 100644 index 0000000..5e9588d --- /dev/null +++ b/crates/cgp-component-macro/src/tests/mod.rs @@ -0,0 +1,2 @@ +pub mod basic; +pub mod const_generic;