enumscribe 0.1.0

Derive macros for converting between enums and strings
Documentation

enumscribe

Build Status

This crate provides derive macros for converting between simple enums and strings. It also includes derive macros for serde::Serialize and serde::Deserialize for simple enums.

Usage

There are a variety of different traits that you can derive. The "Scribe" traits are for converting from an enum to a string, and the "Unscribe" traits are for converting a string to an enum.

Basic usage

use enumscribe::{ScribeStaticStr, TryUnscribe};

#[derive(ScribeStaticStr, TryUnscribe, PartialEq, Eq, Debug)]
enum Airport {
    #[enumscribe(str = "LHR")]
    Heathrow,
    #[enumscribe(str = "LGW")]
    Gatwick,
    #[enumscribe(str = "LTN")]
    Luton,
}

// Convert an Airport to a &'static str
assert_eq!(Airport::Heathrow.scribe(), "LHR");
    
// Convert a &str to a Option<Airport>
assert_eq!(Airport::try_unscribe("LGW"), Some(Airport::Gatwick));

The #[enumscribe(str = "...")] allows us to specify what string should be used to represent a particular variant. If this is omitted, the name of the variant will be used instead.

Case insensitivity

The #[enumscribe(case_insensitive)] attribute can be used to make the "Unscribe" traits perform case-insensitive matching for a variant:

use enumscribe::TryUnscribe;

#[derive(TryUnscribe, PartialEq, Eq, Debug)]
enum Website {
    #[enumscribe(str = "github.com", case_insensitive)]
    Github,
    #[enumscribe(str = "crates.io", case_insensitive)]
    CratesDotIo,
}

assert_eq!(Website::try_unscribe("GiThUb.CoM"), Some(Website::Github));

"other" variant

You can also have a variant which stores strings that could not be matched to any other variant. This is done using the #[enumscribe(other)] attribute. The variant should have a single field, which is a String.

use std::borrow::Cow;

use enumscribe::{Unscribe, ScribeCowStr};

#[derive(ScribeCowStr, Unscribe, PartialEq, Eq, Debug)]
enum Website {
    #[enumscribe(str = "github.com", case_insensitive)]
    Github,
    #[enumscribe(str = "crates.io", case_insensitive)]
    CratesDotIo,
    #[enumscribe(other)]
    Other(String),
}

// Note that we don't need to use an Option anymore!
assert_eq!(Website::unscribe("github.com"), Website::Github);

// Unbelievably, websites exist other than github and crates.io
assert_eq!(Website::unscribe("stackoverflow.com"), Website::Other("stackoverflow.com".to_owned()));

// We can't scribe to a &'static str anymore, so we use a Cow<'static, str> instead
assert_eq!(Website::Github.scribe(), Cow::Borrowed::<'static, str>("github.com"));

assert_eq!(Website::Other("owasp.org".to_owned()).scribe(), Cow::Owned::<'static, str>("owasp.org".to_owned()));

Ignoring variants

If you need to, you can use #[enumscribe(ignore)] to prevent a variant from being used by Scribe or Unscribe traits.

However, this means that converting the enum to a string can fail, so you must use TryScribe instead of Scribe in this case.

use enumscribe::TryScribeStaticStr;

#[derive(TryScribeStaticStr, PartialEq, Eq, Debug)]
enum Airport {
    #[enumscribe(str = "LHR")]
    Heathrow,
    #[enumscribe(str = "LGW")]
    Gatwick,
    #[enumscribe(str = "LTN")]
    Luton,
    #[enumscribe(ignore)]
    SecretExtraVariant(i32), // we have to ignore this variant because of the i32 field
}

assert_eq!(Airport::SecretExtraVariant(123).try_scribe(), None);

assert_eq!(Airport::Luton.try_scribe(), Some("LTN"));

Serde

You can derive serde::Serialize and serde::Deserialize using the same syntax:

use serde::{Serialize, Deserialize};

use enumscribe::{EnumSerialize, EnumDeserialize};

#[derive(EnumSerialize, EnumDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
enum Airport {
    #[enumscribe(str = "LHR")]
    Heathrow,
    #[enumscribe(str = "LGW")]
    Gatwick,
    #[enumscribe(str = "LTN")]
    Luton,
}

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct Flight {
    takeoff: Airport,
    landing: Airport,
}

// There are probably much more economical ways of making this journey
let flight = Flight {
    takeoff: Airport::Heathrow,
    landing: Airport::Gatwick,
};

let flight_json = r#"{"takeoff":"LHR","landing":"LGW"}"#;

assert_eq!(serde_json::to_string(&flight).unwrap(), flight_json.to_owned());

assert_eq!(serde_json::from_str::<Flight>(flight_json).unwrap(), flight);

Traits table

Here is a table to show which traits you should derive, depending on your enum:

ignore used? other used? Conversion to string Conversion from string
No No ScribeStaticStr TryUnscribe
No Yes ScribeCowStr Unscribe
Yes No TryScribeStaticStr TryUnscribe
Yes Yes TryScribeCowStr Unscribe

There are also ScribeString and TryScribeString traits which can be used in the same situations as ScribeCowStr and TryScribeCowStr, respectively. These traits produce a String rather than a Cow<'static, str>, so they will always perform an allocation. Therefore, you should prefer the ScribeCowStr traits over the ScribeString traits, unless you really don't want to use a Cow for whatever reason.