Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ quote = "1.0"
proc-macro2 = "1.0"

[dev-dependencies]
enum_to_enum = "0.1"
derive_more = { version = "2.0", features = ["from"] }
trybuild = "1.0"
130 changes: 126 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# enum_convert

A Rust procedural macro library for deriving automatic conversions between enums variants.
Crate to derive [From](https://doc.rust-lang.org/core/convert/trait.From.html) implementations between enums.

## Features

Expand All @@ -21,7 +21,7 @@ use enum_convert::EnumFrom;
enum Source {
Unit,
Tuple(i32, String),
Struct { x: i32, y: i32 }
Struct { x: i32, y: i32 },
}

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

// Usage
Expand All @@ -43,6 +43,9 @@ 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](https://doc.rust-lang.org/core/convert/trait.Into.html).
This is similar to `derive_more`'s [Into](https://docs.rs/derive_more/latest/derive_more/derive.Into.html).

```rust
use enum_convert::EnumInto;

Expand All @@ -57,7 +60,7 @@ enum Source {
enum Target {
Unit,
Different(i64),
Extra
Extra,
}

// Usage
Expand All @@ -70,6 +73,17 @@ let target: Target = source.into();
#### Multiple source/target enums

```rust
use enum_convert::EnumFrom;

enum FirstSource {
Unit,
Data(String),
}

enum SecondSource {
Empty,
}

#[derive(EnumFrom)]
#[enum_from(FirstSource, SecondSource)]
enum Target {
Expand All @@ -83,6 +97,15 @@ enum Target {
#### Field mapping

```rust
use enum_convert::EnumFrom;

enum Source {
Record {
name: String,
value: i32,
}
}

#[derive(EnumFrom)]
#[enum_from(Source)]
enum Target {
Expand All @@ -94,3 +117,102 @@ enum Target {
}
}
```

## Related and similar crates

### derive_more

This crate has some similarities with [derive_more](https://docs.rs/derive_more/latest/derive_more/index.html)'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.

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

enum OtherEnum {
Variant1(i32),
}
```
```rust compile_fail
// `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](https://docs.rs/enum_to_enum/latest/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 ](https://docs.rs/enum_to_enum/latest/enum_to_enum/derive.FromEnum.html#many-to-one-conversion).
- `enum_convert` does not support [effectful conversion](https://docs.rs/enum_to_enum/latest/enum_to_enum/derive.FromEnum.html#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:

```rust
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](https://docs.rs/subenum/latest/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.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;

mod enum_from;
Expand Down