From b41ea16b825f0f6ce2ecf5d3368e9ef1fe73034f Mon Sep 17 00:00:00 2001 From: Pantonshire Date: Sat, 22 May 2021 12:52:45 +0100 Subject: [PATCH] Remove unused import, copy README.md to enumscribe and enumscribe_derive crates for publishing --- enumscribe/Cargo.toml | 2 +- enumscribe/README.md | 157 +++++++++++++++++++++++++++++ enumscribe_derive/Cargo.toml | 2 +- enumscribe_derive/README.md | 157 +++++++++++++++++++++++++++++ enumscribe_derive/src/attribute.rs | 1 - 5 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 enumscribe/README.md create mode 100644 enumscribe_derive/README.md diff --git a/enumscribe/Cargo.toml b/enumscribe/Cargo.toml index 6ae423e..6e04993 100644 --- a/enumscribe/Cargo.toml +++ b/enumscribe/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Tom Panton "] edition = "2018" license = "MIT" repository = "https://github.com/Pantonshire/enumscribe" -readme = "../README.md" +readme = "README.md" description = "Derive macros for converting between enums and strings" categories = ["encoding"] keywords = ["enum", "derive", "serde"] diff --git a/enumscribe/README.md b/enumscribe/README.md new file mode 100644 index 0000000..e0a2360 --- /dev/null +++ b/enumscribe/README.md @@ -0,0 +1,157 @@ +# enumscribe + +[![Build Status](https://travis-ci.com/Pantonshire/enumscribe.svg?branch=main)](https://travis-ci.com/Pantonshire/enumscribe) + +This crate provides derive macros for converting between simple enums and strings. It also includes derive macros for [`serde::Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) 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 +```rust +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 +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: + +```rust +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`. + +```rust +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. + +```rust +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`](https://docs.serde.rs/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) using the same syntax: + +```rust +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_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. diff --git a/enumscribe_derive/Cargo.toml b/enumscribe_derive/Cargo.toml index 0c5b49c..162d405 100644 --- a/enumscribe_derive/Cargo.toml +++ b/enumscribe_derive/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Tom Panton "] edition = "2018" license = "MIT" repository = "https://github.com/Pantonshire/enumscribe" -readme = "../README.md" +readme = "README.md" description = "Derive macros for converting between enums and strings" [lib] diff --git a/enumscribe_derive/README.md b/enumscribe_derive/README.md new file mode 100644 index 0000000..e0a2360 --- /dev/null +++ b/enumscribe_derive/README.md @@ -0,0 +1,157 @@ +# enumscribe + +[![Build Status](https://travis-ci.com/Pantonshire/enumscribe.svg?branch=main)](https://travis-ci.com/Pantonshire/enumscribe) + +This crate provides derive macros for converting between simple enums and strings. It also includes derive macros for [`serde::Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) 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 +```rust +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 +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: + +```rust +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`. + +```rust +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. + +```rust +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`](https://docs.serde.rs/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) using the same syntax: + +```rust +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_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. diff --git a/enumscribe_derive/src/attribute.rs b/enumscribe_derive/src/attribute.rs index 6a153dc..cbbb086 100644 --- a/enumscribe_derive/src/attribute.rs +++ b/enumscribe_derive/src/attribute.rs @@ -5,7 +5,6 @@ use proc_macro2::Span; use syn::{Attribute, Ident, Lit, Token}; use syn::parse::{Parse, ParseBuffer, ParseStream}; use syn::parse::discouraged::Speculative; -use syn::token::Token; use crate::error::{MacroError, MacroResult, ValueTypeError, ValueTypeResult};