8 unstable releases (3 breaking)
0.4.0 | Nov 19, 2023 |
---|---|
0.3.1 | Oct 2, 2023 |
0.3.0 | Jul 27, 2023 |
0.2.1 | Feb 8, 2023 |
0.1.1 | May 22, 2021 |
#68 in #serde-derive
1,347 downloads per month
Used in enumscribe
72KB
1.5K
SLoC
enumscribe
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.
Adding enumscribe to your project
Add to your Cargo.toml file:
[dependencies]
enumscribe = "0.4"
Derive macros and serde
support are enabled by default. They can be disabled by
setting default-features = false
.
It is also possible to use the enumscribe_derive
crate on its own without using the enumscribe
crate. However,
doing so means that you will only be able to derive serde::Serialize
and serde::Deserialize
.
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));
The same attribute can be used on the enum itself to make all variants case-insensitive. Individual fields may opt back
in to case sensitivity with #[enumscribe(case_sensitive)]
.
use enumscribe::TryUnscribe;
#[derive(TryUnscribe, PartialEq, Eq, Debug)]
#[enumscribe(case_insensitive)]
enum Website {
#[enumscribe(str = "github.com")]
Github,
#[enumscribe(str = "crates.io")]
CratesDotIo,
}
assert_eq!(Website::try_unscribe("CrAtEs.Io"), Some(Website::CratesDotIo));
"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.
Dependencies
~1.5MB
~36K SLoC