From 45efa1e89e179f04bdb733d16cf6a2746ca810cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Vandecr=C3=A8me?= Date: Sat, 30 Aug 2025 18:36:37 +0200 Subject: [PATCH] Improve readme and include it in the doc --- Cargo.lock | 57 ++++++++++++++++++++++- Cargo.toml | 2 + README.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 1 + 4 files changed, 184 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5a0334..c446651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,58 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + [[package]] name = "enum_convert" version = "0.1.0" dependencies = [ + "derive_more", + "enum_to_enum", "proc-macro2", "quote", - "syn", + "syn 2.0.105", "trybuild", ] +[[package]] +name = "enum_to_enum" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a98f748df7c1570c9c6bfbd3b422adeee429fd778538c66b79cccbb051b0d02" +dependencies = [ + "enum_to_enum_derive", +] + +[[package]] +name = "enum_to_enum_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51953530e29a0e9abfb23af10748b0328cabf3d0d0e1ffd34b61144c0e0440b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -93,7 +135,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.105", ] [[package]] @@ -117,6 +159,17 @@ dependencies = [ "serde", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.105" diff --git a/Cargo.toml b/Cargo.toml index 8d25db7..a565b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index f6a6106..4c5b76a 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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)] @@ -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 @@ -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 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; @@ -57,7 +60,7 @@ enum Source { enum Target { Unit, Different(i64), - Extra + Extra, } // Usage @@ -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 { @@ -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 { @@ -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 for MyEnum { + fn from(value: i32) -> MyEnum { + MyEnum::Variant1(value) + } +} + +// `enum_convert::EnumFrom` get expanded to +impl From 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. diff --git a/src/lib.rs b/src/lib.rs index 9e126f3..625a44e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![doc = include_str!("../README.md")] use proc_macro::TokenStream; mod enum_from;