Derive Macro enum_to_enum::FromEnum[][src]

#[derive(FromEnum)]
{
    // Attributes available to this derive:
    #[from_enum]
    #[from_case]
}
Expand description

You can add #[derive(FromEnum)] to any enum to generate a possibly effectful From implementation to convert from other source enums to the annotated destination enum.

from_enum

  • You must annotate the destination enum with #[from_enum(SrcEnum1, SrcEnum2, ...)].
  • You may include effect_container = YourEffectContainer, like this: #[from_enum(SrcEnum1, effect_container = YourEffectContainer)]. If effect_container is specified, the conversion will be From<SrcEnum1> for YourEffectContainer<Value = DestEnum>. YourEffectContainer must implement enum_to_enum::WithEffects. If effect_container is not specified, the conversion will be From<SrcEnum1> for DestEnum.

from_case

  • You may also annotate any variant of the destination enum with #[from_case(SomeCase)] to convert from SomeCase on all source enums to the annotated variant.
  • You may annotate #[from_case(DefaultCase, source_enum_1 = SourceEnum1Case)] to convert from SourceEnum1Case of source_enum_1 to the annotated case and from DefaultCase of all other source enums to the annotated case.
  • Without any from_case annotation, the we default to converting from same-named variants.

Examples

1-to-1 conversion

1-to-1 conversion from source to destination enum variants, demonstrating recursive, field-level conversions and from_case usage.

use enum_to_enum::FromEnum;

#[derive(Debug)]
enum Src {
    Case1(),
    Case2(SrcStrField),
    Case3 { a: SrcStrField, b: u8 },
}

#[derive(FromEnum, Debug, PartialEq, Eq)]
#[from_enum(Src)]
enum Dest {
    Case1(),

    #[from_case(Case2)]
    DestCase2(DestStrField),

    #[from_case(Src = Case3)]
    DestCase3 { a: DestStrField, b: u8 },
}

#[derive(Debug, PartialEq, Eq)]
struct SrcStrField(String);

#[derive(Debug, PartialEq, Eq)]
struct DestStrField(String);

impl From<SrcStrField> for DestStrField {
    fn from(src: SrcStrField) -> DestStrField {
        DestStrField(src.0 + " world")
    }
}

assert_eq!(
    Dest::from(Src::Case1()),
    Dest::Case1(),
);

assert_eq!(
    Dest::from(Src::Case2(SrcStrField(String::from("hello")))),
    Dest::DestCase2(DestStrField(String::from("hello world"))),
);

assert_eq!(
    Dest::from(Src::Case3 {
        a: SrcStrField(String::from("hello")),
        b: 100u8,
    }),
    Dest::DestCase3 {
        a: DestStrField(String::from("hello world")),
        b: 100u8,
    },
);

Many-to-one conversion

Many-to-one conversion, demonstrating mapping from many source variants to a single destination variant, using whichever source variant’s try_into succeeds.

use enum_to_enum::FromEnum;
use std::convert::TryFrom;

#[derive(Debug)]
enum Src {
    Case1(SrcField),
    Case2(SrcField),
}

#[derive(FromEnum, Debug, PartialEq, Eq)]
#[from_enum(Src)]
enum Dest {
    #[from_case(Case1, Case2)]
    Big(BigDestField),

    #[from_case(Case1, Case2)]
    Small(SmallDestField),
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct SrcField(u32);

#[derive(Debug, PartialEq, Eq)]
struct BigDestField(u32);

#[derive(Debug, PartialEq, Eq)]
struct SmallDestField(u32);

impl TryFrom<SrcField> for SmallDestField {
    type Error = &'static str;
    fn try_from(src: SrcField) -> Result<SmallDestField, Self::Error> {
        if src.0 < 100 {
            Ok(SmallDestField(src.0 - 1))
        } else {
            Err("too big")
        }
    }
}

impl TryFrom<SrcField> for BigDestField {
    type Error = &'static str;
    fn try_from(src: SrcField) -> Result<BigDestField, Self::Error> {
        if src.0 >= 100 {
            Ok(BigDestField(src.0 + 1))
        } else {
            Err("too small")
        }
    }
}

assert_eq!(
    Dest::from(Src::Case1(SrcField(10))),
    Dest::Small(SmallDestField(9)),
);

Effectful conversion

Effectful conversion, demonstrating an effect_container and effect combination.

use enum_to_enum::{FromEnum, WithEffects};

#[derive(Debug)]
enum Src {
    Case1(SrcField),
    Case2(SrcField, SrcField),
}

#[derive(FromEnum, Debug, PartialEq, Eq)]
#[from_enum(Src, effect_container = EffectContainer)]
enum Dest {
    Case1(DestField),
    Case2(DestField, DestField),
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct SrcField(String);

#[derive(Debug, PartialEq, Eq)]
struct DestField(String);

#[derive(Debug, PartialEq, Eq)]
enum Eff {
    Log(String),
}

#[derive(Debug, PartialEq, Eq)]
struct EffectContainer<Value> {
    value: Value,
    effects: Vec<Eff>,
}

impl<Value> WithEffects for EffectContainer<Value> {
    type Value = Value;
    type Effect = Eff;

    fn new(value: Self::Value, effects: Vec<Self::Effect>) -> Self {
        Self { value, effects }
    }

    fn into_value_and_effects(self) -> (Self::Value, Box<dyn Iterator<Item = Self::Effect>>) {
        (self.value, Box::new(self.effects.into_iter()))
    }
}

impl From<SrcField> for EffectContainer<DestField> {
    fn from(src: SrcField) -> EffectContainer<DestField> {
        let log = Eff::Log(format!("Log: {}", &src.0));
        EffectContainer {
            value: DestField(src.0),
            effects: vec![log],
        }
    }
}

assert_eq!(
    EffectContainer::from(Src::Case1(SrcField(String::from("hello")))),
    EffectContainer {
        value: Dest::Case1(DestField(String::from("hello"))),
        effects: vec![Eff::Log(String::from("Log: hello"))],
    },
);

assert_eq!(
    EffectContainer::from(Src::Case2(SrcField(String::from("hi")), SrcField(String::from("bye")))),
    EffectContainer {
        value: Dest::Case2(DestField(String::from("hi")), DestField(String::from("bye"))),
        effects: vec![
            Eff::Log(String::from("Log: hi")),
            Eff::Log(String::from("Log: bye")),
        ],
    },
);