5 releases (3 breaking)
new 0.4.0 | Nov 18, 2024 |
---|---|
0.3.0 | May 24, 2024 |
0.2.2 | May 23, 2024 |
0.2.1 | May 5, 2024 |
0.1.0 | Jan 22, 2024 |
#1033 in Procedural macros
Used in enumcapsulate
75KB
1.5K
SLoC
enumcapsulate-macros
Derive macros for enumcapsulate crate.
Macros
The enumcapsulate-macros
proc-macro crate exports the following derive macros:
Derive macro | Functionality |
---|---|
FromVariant |
Derives impls for Self: enumcapsulate::FromVariant<T> for each non-unit variant type T . |
IntoVariant |
Derives impls for Self: enumcapsulate::FromVariant<T> for each non-unit variant type T . |
From |
Derives impls for Self: core::convert::From<T> for each non-unit variant type T . |
TryInto |
Derives impls for T: core::convert::TryFrom<Self> for each non-unit variant type T . |
AsVariant |
Derives impls for Self: enumcapsulate::AsVariant<T> for each non-unit variant type T . |
AsVariantMut |
Derives impls for Self: enumcapsulate::AsVariantMut<T> for each non-unit variant type T . |
AsVariantRef |
Derives impls for Self: enumcapsulate::AsVariantRef<T> for each non-unit variant type T . |
VariantDiscriminant |
Derives impl for Self: enumcapsulate::VariantDiscriminant . |
… as well as an umbrella derive macro Encapsulate
.
[!NOTE] The implementations generated by the
From
andFromVariant
, as well as theTryInto
andIntoVariant
derive macro pairs are semantically equivalent.
#[derive(Encapsulate)]
The umbrella derive macro Encapsulate
allows for conveniently deriving AsVariant
, AsVariantMut
, AsVariantRef
, From
, FromVariant
, IntoVariant
, TryInto
, VariantDiscriminant
, and VariantDowncast
all at once.
The following two snippets are semantically equivalent:
#[derive(Encapsulate)]
enum Enum { /* ... */ }
#[derive(
FromVariant,
IntoVariant,
From,
TryInto,
AsVariant,
AsVariantMut,
AsVariantRef,
VariantDiscriminant,
)]
enum Enum { /* ... */ }
[!TIP] If the list of trait derives produced by
#[derive(Encapsulate)]
is too broad of a stroke your your particular use case then your can selectively opt them out by use of an#[enumcapsulate(exclude(…))]
attribute on the enum itself.You can even opt trait derives back in at the variant-level, by use of
#[enumcapsulate(include)]
for previously opted-out derives, or individually by use of#[enumcapsulate(include(…))]
:#[derive(Encapsulate)] #[enumcapsulate(exclude(From, TryInto))] enum Enum { // Excluded from `From` and `TryInto` derives // due to existing enum-level attribute: VariantA(VariantA), // Selectively re-included for all derives: #[enumcapsulate(include)] VariantB(VariantB), // Selectively re-included for `From` derive: #[enumcapsulate(include(From))] VariantC(VariantC), }
#[derive(AsVariant)]
The AsVariant
derive macro requires the variant's field type to implement Clone
.
Macro helper attributes
Most of the derive macros support helper attributes:
Enum attributes
#[enumcapsulate(exclude(…))]
Exclude this variant from trait derivation.
-
#[enumcapsulate(exclude(…))]
Exclude variant from specific
enumcapsulate
derive macros.
If you wish to opt out of a select few of Encapsulate
's trait derives,
then you can do so by use of an #[enumcapsulate(exclude(…))]
attribute:
#[derive(Encapsulate)]
#[enumcapsulate(exclude(From, TryInto))]
enum Enum { /* ... */ }
[!TIP] If you wish to opt all but a select few variants out of a trait's derive, then you can do so by use of an
#[enumcapsulate(exclude(…))]
attribute on the enum, together with a#[enumcapsulate(include(…))]
attribute on the variant:#[derive(Encapsulate)] #[enumcapsulate(exclude(From, TryInto))] enum Enum { // Excluded from `From` and `TryInto` derives // due to existing enum-level attribute: VariantA(VariantA), // Selectively re-included for all derives: #[enumcapsulate(include)] VariantB(VariantB), // Selectively re-included for // just the `From` derive: #[enumcapsulate(include(From))] VariantC(VariantC), // ... }
Variant attributes
[!NOTE] Variant-level attributes have a higher precedence than their equivalent enum-level attributes and thus act as a selective override if both are present.
#[enumcapsulate(exclude(…))]
Exclude this variant from trait derivation.
-
#[enumcapsulate(exclude)]
Exclude variant from all
enumcapsulate
derive macros. -
#[enumcapsulate(exclude(…))]
Exclude variant from specific
enumcapsulate
derive macros.
#[derive(Encapsulate)]
enum Enum {
// Included by default.
VariantA(VariantA),
// Excluded from all derives:
#[enumcapsulate(exclude)]
VariantB(VariantB),
// Excluded from just the `From` and `TryInto` derives:
#[enumcapsulate(exclude(From, TryInto))]
VariantC(VariantC),
}
[!TIP] Combine the use of
#[enumcapsulate(exclude)]
with#[enumcapsulate(include(…)]
in order to exclude a variant from all but a select few derive macros.// Exclude variant from all derives, // then selectively re-include it for // just the `From` and `TryInto` derives: #[enumcapsulate(exclude)] #[enumcapsulate(include(From, TryInto))]
This attribute is recognized by the following variant-based derive macros:
AsVariant
AsVariantMut
AsVariantRef
FromVariant
IntoVariant
From
TryInto
… as well as the umbrella derive macro:
Encapsulate
#[enumcapsulate(include(…)]
Include this variant for specific trait derivation (overriding existing uses of #[enumcapsulate(exclude)]
).
-
#[enumcapsulate(include)]
Include variant from all
enumcapsulate
derive macros. -
#[enumcapsulate(include(…))]
Include variant from specific
enumcapsulate
derive macros.
#[derive(Encapsulate)]
#[enumcapsulate(exclude(From, TryInto))]
enum Enum {
// Included by default.
VariantA(VariantA),
// Selectively included for just
// the `From` and `TryInto` derives:
#[enumcapsulate(exclude)]
#[enumcapsulate(include(From, TryInto))]
VariantB(VariantB),
}
[!NOTE] The
#[enumcapsulate(include(…)]
variant has a higher precedence than#[enumcapsulate(exclude)]
, and thus acts as a selective override if both are present on a variant:// Exclude variant from all derives, // then selectively re-include it for // just the `From` and `TryInto` derives: #[enumcapsulate(exclude)] #[enumcapsulate(include(From, TryInto))]
This attribute is recognized by the following variant-based derive macros:
AsVariant
AsVariantMut
AsVariantRef
FromVariant
IntoVariant
From
TryInto
… as well as the umbrella derive macro:
Encapsulate
#[enumcapsulate(field(… = …)]
Select a specific variant field as target for trait derivation.
-
#[enumcapsulate(field(index = <index>))]
Select field at index
<index>
to be used as target. -
#[enumcapsulate(field(name = "<name>"))]
Select field with name
<name>
to be used as target.
#[derive(Encapsulate)]
enum Enum {
// Select field `1`, i.e. `VariantA`:
#[enumcapsulate(field(index = 1))]
VariantA(i32, VariantA),
// Select field `b`, i.e. `VariantB`:
#[enumcapsulate(field(name = "b"))]
VariantB { b: VariantB, c: u32 },
}
[!NOTE] When deriving traits for variants with multiple fields a field has to be explicitly specified using
#[enumcapsulate(field(… = …))]
.Alternatively the variant can be excluded via
#[enumcapsulate(exclude)]
.
This attribute is recognized by the following variant-based derive macros:
AsVariant
AsVariantMut
AsVariantRef
FromVariant
IntoVariant
From
TryInto
… as well as the umbrella derive macro:
Encapsulate
Generics
There is limited support for generic enums:
Variants using generic const/type parameters are always excluded when deriving generic traits with enumcapsulate
's derive macros.
The reason for this behavior is that implementing generic traits for variants that use any of the generic parameters of the enum tends to result in conflicting implementations in Rust, as shown by the following example program:
use enumcapsulate::FromVariant;
pub struct VariantA;
pub struct VariantB;
#[derive(FromVariant)]
// error[E0119]: conflicting implementations of trait `FromVariant<VariantB>` for type `Enum<VariantB>`
pub enum Enum<T> {
Unit,
Generic(T),
NonGeneric(VariantB),
}
fn main() {
let _: Enum<VariantA> = Enum::from_variant(VariantA);
let _: Enum<VariantA> = Enum::from_variant(VariantB);
}
The expanded version of the above makes it easier to see why: The compiler can't prove that T
and VariantB
are disjoint types.
pub struct VariantA;
pub struct VariantB;
pub enum Enum<T> {
Unit,
Generic(T),
NonGeneric(VariantB),
}
impl<T> FromVariant<T> for Enum<T> { // <- first implementation here
fn from_variant(variant: T) -> Self {
Self::Generic(variant)
}
}
// error[E0119]: conflicting implementations of trait `FromVariant<VariantB>` for type `Enum<VariantB>`
impl<T> FromVariant<VariantB> for Enum<T> { // <- conflicting implementation for `Enum<VariantB>`
fn from_variant(variant: VariantB) -> Self {
Self::NonGeneric(variant)
}
}
fn main() {
let _: Enum<VariantA> = Enum::from_variant(VariantA);
let _: Enum<VariantA> = Enum::from_variant(VariantB);
}
So in order to avoid such pitfalls altogether enumcapsulate
's derive macros will skip impl<T> FromVariant<T> for Enum<T>
, since it uses a generic type (or const) parameter of Enum<T>
.
So all you have to do is provide your own non-generic implementations for specific type instances of your generic type yourself, filling any gaps left behind by the derive macro:
use enumcapsulate::FromVariant;
pub struct VariantA;
pub struct VariantB;
#[derive(FromVariant)]
pub enum Enum<T> {
Unit,
Generic(T),
NonGeneric(VariantB),
}
// Notice how the trait is implemented on
// a specific type of the `Enum<T>` kind,
// rather than on the generic kind itself:
impl From<VariantA> for Enum<VariantA> {
fn from(value: VariantA) -> Self {
Self::Generic(value)
}
}
fn main() {
let _: Enum<VariantA> = Enum::from_variant(VariantA);
let _: Enum<VariantA> = Enum::from_variant(VariantB);
}
Documentation
Please refer to the documentation on docs.rs.
Contributing
Please read CONTRIBUTING.md for details on our code of conduct,
and the process for submitting pull requests to us.
Versioning
We use SemVer for versioning. For the versions available, see the tags on this repository.
License
This project is licensed under the MPL-2.0 – see the LICENSE.md file for details.
Dependencies
~240–690KB
~16K SLoC