We are excited to announce the release of CGP v0.4.1! This release brings several new features, quality-of-life improvements for macro usages, and a new crate cgp-handler
.
Here are some of the highlights:
- New
cgp-handler
crate: Provides abstract interfaces for defining components with common method signatures. cgp_preset!
macro improvement: Now supports wrapping ofPreset::Provider
.#[cgp_component]
macro improvement: Now supports automated derivation ofUseDelegate
.- Improved Documentation: Added inline Rust documentation for common CGP constructs.
Below we will go through some of the most significant changes in this release.
New cgp-handler
Crate
This release introduces a new cgp-handler
crate, which offers abstract interfaces for defining components with common method signatures. This helps in creating reusable and composable handlers for various tasks.
As a semi-stable and non-essential crate, the cgp-handler
crate is re-exported by cgp-extra
, and is available from cgp::extra::handler
.
The introduction of cgp-handler
is mainly to support the development of Hypershell, which makes heavy use of the Handler
component to design and implement its DSL providers.
The crate introduces three main components: Handler
, Computer
, and Producer
.
Handler
The Handler
component provides the most commonly used interface for performing asynchronous operations that may fail:
#[cgp_component(Handler)]
#[async_trait]
pub trait CanHandle<Code: Send, Input: Send>: HasAsyncErrorType {
type Output: Send;
async fn handle(
&self,
_tag: PhantomData<Code>,
input: Input,
) -> Result<Self::Output, Self::Error>;
}
Computer
The Computer
component mirrors a pure function that takes some input, performs some computation, and produces an output.
#[cgp_component(Computer)]
pub trait CanCompute<Code, Input> {
type Output;
fn compute(&self, _tag: PhantomData<Code>, input: Input) -> Self::Output;
}
Producer
The Producer
component mirrors a global singleton function to produce an output value. It is useful to emulate global values that cannot be constructed through the const
context in Rust, such as String
.
#[cgp_component(Producer)]
pub trait CanProduce<Code> {
type Output;
fn produce(&self, _tag: PhantomData<Code>) -> Self::Output;
}
Code
Parameter
All the traits in cgp-handler
contain a phantom Code
parameter that can be used for building type-level DSLs such as Hypershell. They can also be used as type-level identifiers for dispatching, such as in API handlers.
cgp_preset!
Macro Improvements
This release also brings minor improvements to our cgp_preset!
macro, supporting the definition of CGP presets for more diverse use cases.
Support for Wrapping Preset::Provider
in cgp_preset!
The cgp_preset!
macro now allows users to specify a #[wrap_provider]
attribute to wrap the Preset::Provider
type. This is particularly useful when using CGP presets to define extensible mappings for generic dispatch through the UseDelegate
pattern.
Wrapping the provider makes it easier to extend non-provider mappings across multiple levels of preset inheritance. The wrapped Preset::Provider
type will implement the expected provider trait, making it a valid delegation target.
Example
Given the following preset definition:
cgp_preset! {
#[wrap_provider(UseDelegate)]
MyHandlerPreset {
String: HandleString,
u64: HandleU64,
...
}
}
The macro generates the following implementation:
pub mod MyHandlerPreset {
...
pub type Provider = UseDelegate<Components>;
delegate_components! {
new Components {
String: HandleString,
u64: HandleU64,
...
}
}
...
}
Automated UseDelegate
Derivation in #[cgp_component]
The #[cgp_component]
family of macros now includes a derive_delegate
field, which allows for the automated implementation of UseDelegate
for CGP components. This reduces boilerplate code that was previously required to be implemented manually.
Example
The updated ErrorRaiser
component can now be defined as:
#[cgp_component {
provider: ErrorRaiser,
derive_delegate: UseDelegate<SourceError>,
}]
pub trait CanRaiseError<SourceError>: HasErrorType {
fn raise_error(error: SourceError) -> Self::Error;
}
This will automatically derive the UseDelegate
implementation:
#[cgp_provider(ErrorRaiserComponent)]
impl<Context, SourceError, Components, Delegate> ErrorRaiser<Context, SourceError>
for UseDelegate<Components>
where
Context: HasErrorType,
Components: DelegateComponent<SourceError, Delegate = Delegate>,
Delegate: ErrorRaiser<Context, SourceError>,
{
fn raise_error(e: SourceError) -> Context::Error {
Delegate::raise_error(e)
}
}
Other Improvements
This release also includes several other minor improvements and fixes:
- Improved Documentation: We have added inline Rust documentation for many common CGP constructs, making it easier for developers to understand and use them. This is part of our ongoing effort to improve the developer experience.
- Static
Char
Formatting: TheChar
type can now be formatted statically without requiringself
, which allows type-level strings to be formatted without constructing any value. - Use
__Self__
instead ofT
when derivingPreset::IsPreset
to avoid identifier conflicts when users useT
in their generic parameters. - Included trait bound identifiers in
Preset::components
re-export.
We hope you enjoy the new features and improvements in this release. As always, we welcome feedback and contributions from the community. Check out the project on GitHub and the full changelog for more details.