Skip to content

avandecreme/enum_convert

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

enum_convert

Crate to derive From implementations between enums.

Features

  • EnumFrom: Derive From<Source> for AnnotatedEnum
  • EnumInto: Derive From<AnnotatedEnum> for Target
  • Support for multiple source/target enums
  • Flexible variant name mapping (one-to-many, many-to-one)
  • Field-level mapping for named struct variants
  • Automatic type conversion for fields via .into()

Usage

EnumFrom - Convert from source enums to annotated target enum

use enum_convert::EnumFrom;

enum Source {
    Unit,
    Tuple(i32, String),
    Struct { x: i32, y: i32 },
}

#[derive(EnumFrom)]
#[enum_from(Source)]
enum Target {
    #[enum_from]  // Maps from Source::Unit
    Unit,
    #[enum_from]  // Maps from Source::Tuple with type conversion
    Tuple(i64, String),
    #[enum_from]  // Maps from Source::Struct with type conversion
    Struct { x: f64, y: f64 },
    Extra,  // This variant has no mapping
}

// Usage
let source = Source::Tuple(42, "hello".to_string());
let target: Target = source.into();

EnumInto - Convert from annotated source enum to target enums

ℹ️ While the macro is named EnumInto, it still implements From<AnnotatedEnum> for TargetEnum and thus has an indirect implementation of Into as recommended by the docs. This is similar to derive_more's Into.

use enum_convert::EnumInto;

#[derive(EnumInto)]
#[enum_into(Target)]
enum Source {
    Unit,  // Maps to Target::Unit
    #[enum_into(Target::Different)]  // Maps to Target::Different
    Variant(i32),
}

enum Target {
    Unit,
    Different(i64),
    Extra,
}

// Usage
let source = Source::Variant(42);
let target: Target = source.into();

Advanced Features

Multiple source/target enums

use enum_convert::EnumFrom;

enum FirstSource {
    Unit,
    Data(String),
}

enum SecondSource {
    Empty,
}

#[derive(EnumFrom)]
#[enum_from(FirstSource, SecondSource)]
enum Target {
    #[enum_from(FirstSource, SecondSource::Empty)]
    Unit,
    #[enum_from(FirstSource)]
    Data(String),
}

Field mapping

use enum_convert::EnumFrom;

enum Source {
    Tuple(String, u8),
    Record {
        name: String,
        value: i32,
    },
    Point(i32, i32),
}

#[derive(EnumFrom)]
#[enum_from(Source)]
enum Target {
    #[enum_from]
    Tuple(
        // We effectively re-order fields
        #[enum_from(Source::Tuple.1)] u8,
        #[enum_from(Source::Tuple.0)] String,
    ),
    #[enum_from]
    Record {
        #[enum_from(Source::Record.name)]  // Maps Source::Record.name to Target::Record.title
        title: String,
        value: i32,
    },
    #[enum_from]
    Point {
        #[enum_from(Source::Point.0)]
        x: i64,
        #[enum_from(Source::Point.1)]
        y: i64,
    },
}

Related and similar crates

derive_more

This crate has some similarities with derive_more's From and Into derive macros.

The difference is that with derive_more the conversion is field → variant (From) and variant → field (Into); with this crate it is variant ↔ variant.

#[derive(derive_more::From, enum_convert::EnumFrom)]
#[enum_from(OtherEnum)]
enum MyEnum {
    #[enum_from]
    Variant1(i32),
}

enum OtherEnum {
    Variant1(i32),
}
// `derive_more::From` get expanded to
impl From<i32> for MyEnum {
    fn from(value: i32) -> MyEnum {
        MyEnum::Variant1(value)
    }
}

// `enum_convert::EnumFrom` get expanded to
impl From<OtherEnum> for MyEnum {
    fn from(value: OtherEnum) -> MyEnum {
        match value {
            OtherEnum::Variant1(i) => MyEnum::Variant1(i),
        }
    }
}

enum_to_enum

This crate is very similar to enum_to_enum.

At the time of writing (enum_to_enum in version 0.1.0) the differences are:

  • enum_convert does not support many-to-one conversion with try_into logic .
  • enum_convert does not support effectful conversion.
  • enum_to_enum does not support EnumInto.
  • enum_to_enum does not support having variants in the target for which there is no mapping from source.
  • enum_to_enum does not support fields mapping.

For the common features, here is a comparison of how they are expressed in both crates:

enum SourceA {
    Unit,
    Tuple(i32, String),
    Struct { x: i32, y: i32 },
}

enum SourceB {
    Unit,
    NoField,
    Tuple(i32, String),
    Struct { x: i32, y: i32 },
}

#[derive(enum_to_enum::FromEnum)]
#[from_enum(SourceA, SourceB)]
enum TargetEnumToEnum {
    #[from_case(SourceB = NoField, Unit)]
    Unit,

    Tuple(i64, String),

    Struct { x: f64, y: f64 },
}

#[derive(enum_convert::EnumFrom)]
#[enum_from(SourceA, SourceB)]
enum TargetEnumConvert {
    #[enum_from(SourceA, SourceB::Unit, SourceB::NoField)]
    Unit,

    #[enum_from(SourceA, SourceB)]
    Tuple(i64, String),

    #[enum_from(SourceA, SourceB)]
    Struct { x: f64, y: f64 },
}

subenum

The subenum crate allows to implement easily subset of enums with conversion between parent and child.

However, there are cases where it is not desirable or possible to use subenum. For example:

  • You don't want to declare the child enum in the same module or crate as the parent enum.
  • There already is a child enum coming from another crate and you want to convert from that child enum to your own parent enum.

About

Rust crate to derive From implementations between enums

Resources

License

Stars

Watchers

Forks