1 unstable release
0.1.0 | Apr 11, 2024 |
---|
#1702 in Rust patterns
34KB
53 lines
amass
Automatically generate From
impls for nested enums, even across crates.
All that is required is an attribute applied to each nesting enum:
Example
pub mod letter {
pub struct A;
pub struct B;
pub struct C;
#[amass::amass_telety(crate::letter)]
pub enum Letter {
A(A),
B(B),
C(C),
}
}
pub mod number {
pub struct One;
pub struct Two;
pub struct Three;
#[amass::amass_telety(crate::number)]
pub enum Number {
One(One),
Two(Two),
Three(Three),
}
}
use letter::Letter;
use number::Number;
#[amass::amass_telety(crate)]
pub enum Alphanumeric {
Letter(Letter),
Number(Number),
}
pub struct Underscore;
#[amass::amass_telety(crate)]
pub enum IdentifierChar {
Alphanumeric(Alphanumeric),
Underscore(Underscore),
}
fn main() {
let _: &[IdentifierChar] = &[
letter::A.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Letter(Letter::A(A)))
letter::B.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Letter(Letter::B(B)))
letter::C.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Letter(Letter::C(C)))
Underscore.into(), // IdentifierChar::Underscore(Underscore)
number::One.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Number(Number::One(One)))
number::Two.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Number(Number::Two(Two)))
number::Three.into(), // IdentifierChar::Alphanumeric(Alphanumeric::Number(Number::Three(Three)))
];
}
amass is powered by telety,
which still has limitations in the language features it supports.
You can use the #[amass]
and #[telety(...)]
attributes separately,
or simply use the combined #[amass_telety(...)]
attribute.
Specifying implementations
amass has customizable behavior for applicable fields. The following options exist:
- ignore - No From impl will be created for the field type or the field types contained within that type.
- shallow - A From impl will be created for the field type, but not for any field types contained within that type.
- deep - A From impl will be created for the field type, and if that type is telety-enabled, for the the field types contained within that type.
- force - A From impl will be created for the field type and for the the field types contained within that type. If the type is not telety-enabled, a compile error will be generated.
A default action can be specified on the main attribute: #[amass(default = force)]
.
If no default is provided on the attribute, deep
is the default action.
This default can be overriden on specific variants with the #[amass_action(...)]
helper attribute.
#[amass_telety(crate)]
pub enum ConflictingVariants {
Default(i32),
#[amass_action(ignore)]
Alternate(i32),
}
#[amass_telety(crate, default = ignore)]
pub enum ConflictingVariants {
#[amass_action(shallow)]
Default(i32),
Alternate(i32),
}
Note that is not currently possible to override behavior on an upstream enum.
In this example, two impls for From<DiamondTop> for DiamondBottom
are generated, causing a compile error.
# use amass::amass_telety;
pub struct DiamondTop;
#[amass_telety(crate)]
pub struct DiamondLeft {
Top(DiamondTop)
}
#[amass_telety(crate)]
pub struct DiamondRight {
Top(DiamondTop)
}
#[amass_telety(crate)]
pub struct DiamondBottom {
Left(DiamondLeft),
Right(DiamondRight),
}
# fn main() { }
To solve this, either DiamondLeft
or DiamondRight
must ignore
the Top
variant,
or DiamondBottom
must use shallow
for the Left
or Right
variant. It is not possible to only skip
the DiamondTop
-> DiamondLeft
/DiamondRight
-> DiamondBottom
impl.
Dependencies
~0.4–0.9MB
~21K SLoC