-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
cgp_type!
macro for defining simple abstract CGP types (#55)
* Draft cgp_type! macro * Also derive WithProvider and UseDelegate * Also derive alias type * Use cgp_type! to derive HasErrorType * Use cgp_type! to derive HasRuntimeType * Add changelog
- Loading branch information
1 parent
9d8cf04
commit 8fe487c
Showing
8 changed files
with
185 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
crates/cgp-component-macro-lib/src/type_component/derive.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use alloc::format; | ||
use alloc::vec::Vec; | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use syn::parse::{Parse, ParseStream}; | ||
use syn::punctuated::Punctuated; | ||
use syn::token::{Colon, Plus, Pound}; | ||
use syn::{parse_quote, Attribute, Ident, ItemImpl, ItemTrait, ItemType, TypeParamBound}; | ||
|
||
pub fn derive_type_component(stream: TokenStream) -> syn::Result<TokenStream> { | ||
let spec: TypeComponentSpecs = syn::parse2(stream)?; | ||
|
||
Ok(do_derive_type_component( | ||
spec.attributes, | ||
spec.ident, | ||
spec.bounds, | ||
)) | ||
} | ||
|
||
pub struct TypeComponentSpecs { | ||
pub attributes: Vec<Attribute>, | ||
pub ident: Ident, | ||
pub bounds: Punctuated<TypeParamBound, Plus>, | ||
} | ||
|
||
impl Parse for TypeComponentSpecs { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let attributes = { | ||
let lookahead = input.lookahead1(); | ||
if lookahead.peek(Pound) { | ||
input.call(Attribute::parse_outer)? | ||
} else { | ||
Vec::new() | ||
} | ||
}; | ||
|
||
let ident = input.parse()?; | ||
|
||
if input.is_empty() { | ||
return Ok(Self { | ||
attributes, | ||
ident, | ||
bounds: Punctuated::new(), | ||
}); | ||
} | ||
|
||
let _: Colon = input.parse()?; | ||
|
||
let bounds = input.parse_terminated(TypeParamBound::parse, Plus)?; | ||
|
||
Ok(Self { | ||
attributes, | ||
ident, | ||
bounds, | ||
}) | ||
} | ||
} | ||
|
||
pub fn do_derive_type_component( | ||
attributes: Vec<Attribute>, | ||
ident: Ident, | ||
bounds: Punctuated<TypeParamBound, Plus>, | ||
) -> TokenStream { | ||
let consumer_trait_name = Ident::new(&format!("Has{ident}Type"), ident.span()); | ||
|
||
let provider_trait_name = Ident::new(&format!("Provide{ident}Type"), ident.span()); | ||
|
||
let alias_name = Ident::new(&format!("{ident}Of"), ident.span()); | ||
|
||
let component_name = Ident::new(&format!("{ident}TypeComponent"), ident.span()); | ||
|
||
let alias_type: ItemType = parse_quote! { | ||
pub type #alias_name <Context> = <Context as #consumer_trait_name>:: #ident; | ||
}; | ||
|
||
let mut consumer_trait: ItemTrait = parse_quote! { | ||
pub trait #consumer_trait_name { | ||
type #ident : #bounds ; | ||
} | ||
}; | ||
|
||
consumer_trait.attrs = attributes; | ||
|
||
let provider_trait: ItemTrait = parse_quote! { | ||
pub trait #provider_trait_name <Context> { | ||
type #ident : #bounds; | ||
} | ||
}; | ||
|
||
let consumer_impl: ItemImpl = parse_quote! { | ||
impl<Context, Components> #consumer_trait_name for Context | ||
where | ||
Context: HasComponents< Components = Components >, | ||
Components: #provider_trait_name <Context>, | ||
Components:: #ident : #bounds, | ||
{ | ||
type #ident = Components:: #ident; | ||
} | ||
}; | ||
|
||
let provider_impl: ItemImpl = parse_quote! { | ||
impl<Context, Component, Delegate> | ||
#provider_trait_name <Context> for Component | ||
where | ||
Component: DelegateComponent< #component_name, Delegate = Delegate >, | ||
Delegate: #provider_trait_name <Context>, | ||
Delegate:: #ident : #bounds, | ||
{ | ||
type #ident = Delegate:: #ident; | ||
} | ||
}; | ||
|
||
let with_provider_impl: ItemImpl = parse_quote! { | ||
impl<Context, Provider, #ident> #provider_trait_name <Context> | ||
for WithProvider<Provider> | ||
where | ||
Provider: ProvideType<Context, #component_name, Type = #ident >, | ||
#ident: #bounds, | ||
{ | ||
type #ident = #ident; | ||
} | ||
}; | ||
|
||
let use_type_impl: ItemImpl = parse_quote! { | ||
impl<Context, #ident> #provider_trait_name <Context> | ||
for UseType<#ident> | ||
where | ||
#ident: #bounds, | ||
{ | ||
type #ident = #ident; | ||
} | ||
}; | ||
|
||
quote! { | ||
pub struct #component_name; | ||
|
||
#consumer_trait | ||
|
||
#alias_type | ||
|
||
#provider_trait | ||
|
||
#consumer_impl | ||
|
||
#provider_impl | ||
|
||
#with_provider_impl | ||
|
||
#use_type_impl | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod derive; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
pub use cgp_async::{async_trait, Async, MaybeSend, MaybeStatic, MaybeSync}; | ||
pub use cgp_component::{DelegateComponent, HasComponents}; | ||
pub use cgp_component::{DelegateComponent, HasComponents, WithContext, WithProvider}; | ||
pub use cgp_component_macro::{ | ||
cgp_component, cgp_preset, delegate_components, for_each_replace, replace_with, | ||
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_macro::{product, symbol, HasField, Product, Sum}; | ||
pub use cgp_type::{HasType, ProvideType, UseType}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,27 @@ | ||
use core::fmt::Debug; | ||
|
||
use cgp_async::Async; | ||
use cgp_component::{DelegateComponent, HasComponents, WithProvider}; | ||
use cgp_component_macro::cgp_component; | ||
use cgp_type::ProvideType; | ||
use cgp_component_macro::cgp_type; | ||
use cgp_type::{ProvideType, UseType}; | ||
|
||
/** | ||
This is used for contexts to declare that they have a _unique_ `Self::Error` type. | ||
cgp_type! { | ||
/** | ||
This is used for contexts to declare that they have a _unique_ `Self::Error` type. | ||
Although it is possible for each context to declare their own associated | ||
`Error` type, doing so may result in having multiple ambiguous `Self::Error` types, | ||
if there are multiple associated types with the same name in different traits. | ||
Although it is possible for each context to declare their own associated | ||
`Error` type, doing so may result in having multiple ambiguous `Self::Error` types, | ||
if there are multiple associated types with the same name in different traits. | ||
As a result, it is better for context traits to include `HasError` as their | ||
parent traits, so that multiple traits can all refer to the same abstract | ||
`Self::Error` type. | ||
*/ | ||
#[cgp_component { | ||
name: ErrorTypeComponent, | ||
provider: ProvideErrorType, | ||
}] | ||
pub trait HasErrorType { | ||
/** | ||
The `Error` associated type is also required to implement [`Debug`]. | ||
As a result, it is better for context traits to include `HasError` as their | ||
parent traits, so that multiple traits can all refer to the same abstract | ||
`Self::Error` type. | ||
The `Error` associated type is also required to implement [`Debug`]. | ||
This is to allow `Self::Error` to be used in calls like `.unwrap()`, | ||
as well as for simpler error logging. | ||
*/ | ||
type Error: Debug; | ||
} | ||
pub type ErrorOf<Context> = <Context as HasErrorType>::Error; | ||
|
||
impl<Context, Provider, Error> ProvideErrorType<Context> for WithProvider<Provider> | ||
where | ||
Provider: ProvideType<Context, ErrorTypeComponent, Type = Error>, | ||
Error: Async + Debug, | ||
{ | ||
type Error = Error; | ||
More details about how to use `HasErrorType` is available at | ||
<https://patterns.contextgeneric.dev/error-handling.html> | ||
*/ | ||
Error: Debug | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,5 @@ | ||
use cgp_core::component::WithProvider; | ||
use cgp_core::prelude::*; | ||
use cgp_core::types::ProvideType; | ||
|
||
#[cgp_component { | ||
name: RuntimeTypeComponent, | ||
provider: ProvideRuntimeType, | ||
}] | ||
pub trait HasRuntimeType { | ||
type Runtime; | ||
} | ||
|
||
pub type RuntimeOf<Context> = <Context as HasRuntimeType>::Runtime; | ||
|
||
impl<Context, Provider, Runtime> ProvideRuntimeType<Context> for WithProvider<Provider> | ||
where | ||
Provider: ProvideType<Context, RuntimeTypeComponent, Type = Runtime>, | ||
{ | ||
type Runtime = Runtime; | ||
cgp_type! { | ||
Runtime | ||
} |