Proc macro crate for working with newtype enums: FromVariants

from_variants is a crate which automatically implements conversion traits for arms of a newtype enum.

//! Example
#![warn(missing_docs)]

#[macro_use]
extern crate from_variants;

/// A sample struct.
#[derive(Debug, Clone, FromVariants)]
pub enum Lorem {
    /// Hello world
    #[from_variants(skip)]
    Str(String),
    
    /// Hello world
    Num(u16),
}

fn main() {
    println!("{:?}", Lorem::from(10));
}

This generates the following code:

#[macro_use]
extern crate from_variants;

/// A sample struct.
pub enum Lorem {
    /// Hello world
    #[from_variants(skip)]
    Str(String),

    /// Hello world
    Num(u16),
}

#[doc = "Convert into a `Num` variant."]
impl ::std::convert::From<u16> for Lorem {
    fn from(v: u16) -> Self {
        Lorem::Num(v)
    }
}

fn main() {
    // ... elided ...
}

Usage

  • Add #[derive(FromVariants)] to any enum whose arms are all single-field tuple variants of different types (or skip non-compliant variants, as explained below).

Features

  • Ability to skip variants using #[from_variants(skip)]
  • no_std support (thanks to @colin_kiegel for example on how to implement this)
6 Likes

#[from_variants(no_std)] is obnoxious to include on every invocation. Here is one way to not require it. Serde does a more elaborate version of this to support no_std derive Serialize and Deserialize (which rely on a lot of library functionality).

from_variants/src/lib.rs

#[allow(unused_imports)]
#[macro_use]
extern crate from_variants_impl;
pub use from_variants_impl::*;

#[doc(hidden)]
pub mod export {
    #[cfg(feature = "std")]
    pub use std::convert::From;

    #[cfg(not(feature = "std")]
    pub use core::convert::From;
}

from_variants_impl/src/lib.rs

#[proc_macro_derive(FromVariants, attributes(from_variants))]
pub fn from_variants(input: TokenStream) -> TokenStream {
    bla bla bla
    quote! {
        impl ::from_variants::export::From<u16> for Lorem {
        }
    }
}
2 Likes

Thanks for the tip! I've started going down this road. @hanna-kruppe pointed out on IRC that I can unconditionally use ::core::convert::From, as the From trait in std is just a reexport of the trait from core.

2 Likes

Update on this: I've published v0.2.0 which includes @dtolnay's suggestion. The no_std attribute is removed completely; all conversions now seamlessly work with or without std.