Scribe static str, documentation
This commit is contained in:
1 parent
95683c7ce7
commit
56d790559e
3 files changed
+216
-11
No files matched your search
+97
-6
@@ -1,34 +1,125 @@
|
|||||||
|
//! Traits for converting between enums and strings. This is only useful alongside the
|
||||||
|
//! [enumscribe_derive] crate, which provides derive macros for these traits.
|
||||||
|
//!
|
||||||
|
//! Here is a table to show which traits you should derive for your enum:
|
||||||
|
//!
|
||||||
|
//! | `ignore` used? | `other` used? | Conversion to string | Conversion from string |
|
||||||
|
//! |----------------|---------------|----------------------|------------------------|
|
||||||
|
//! | No | No | [ScribeStaticStr] | [TryUnscribe] |
|
||||||
|
//! | Yes | No | [TryScribeStaticStr] | [TryUnscribe] |
|
||||||
|
//! | No | Yes | [ScribeCowStr] | [Unscribe] |
|
||||||
|
//! | 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.
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate enumscribe_derive;
|
extern crate enumscribe_derive;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
pub use enumscribe_derive::*;
|
pub use enumscribe_derive::*;
|
||||||
|
|
||||||
//TODO
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
/// Trait for converting an enum to a static string slice.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
|
///
|
||||||
|
/// When deriving this trait, you may specify the string that a particular variant should be
|
||||||
|
/// converted to by annotating it with `#[enumscribe(str = "foo")]`. If this is omitted, the name
|
||||||
|
/// of the variant will be used instead.
|
||||||
|
///
|
||||||
|
/// This trait can only be used if none of the enum's variants use `ignore` or `other`. If you have
|
||||||
|
/// variants that use `ignore`, use [TryScribeStaticStr] instead. If you have variants that use
|
||||||
|
/// `other`, use [ScribeCowStr]. If you have variants that use both, use [TryScribeCowStr].
|
||||||
pub trait ScribeStaticStr {
|
pub trait ScribeStaticStr {
|
||||||
fn scribe(&self) -> &'static str;
|
fn scribe(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/// Trait for converting an enum to a static string slice, or `None` if the conversion fails.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
|
///
|
||||||
|
/// When deriving this trait, you may specify the string that a particular variant should be
|
||||||
|
/// converted to by annotating it with `#[enumscribe(str = "foo")]`. If this is omitted, the name
|
||||||
|
/// of the variant will be used instead.
|
||||||
|
///
|
||||||
|
/// You may also annotate a variant with `#[enumscribe(ignore)]`, in which case attempting to
|
||||||
|
/// convert the variant to a string will always result in `None`.
|
||||||
|
///
|
||||||
|
/// This trait can only be used if none of the enum's variants use `other`. If you have variants
|
||||||
|
/// that use `other`, use [TryScribeCowStr] instead.
|
||||||
pub trait TryScribeStaticStr {
|
pub trait TryScribeStaticStr {
|
||||||
fn try_scribe(&self) -> Option<&'static str>;
|
fn try_scribe(&self) -> Option<&'static str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for converting an enum to an allocated string. Generally, [ScribeCowStr] should be
|
||||||
|
/// preferred over this trait because it avoids unnecessary allocations.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
|
///
|
||||||
|
/// This trait can only be used if none of the enum's variants use `ignore`.
|
||||||
pub trait ScribeString {
|
pub trait ScribeString {
|
||||||
fn scribe(&self) -> String;
|
fn scribe(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for converting an enum to an allocated string, or `None` if the conversion fails.
|
||||||
|
/// Generally, [TryScribeCowStr] should be preferred over this trait because it avoids unnecessary
|
||||||
|
/// allocations.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
pub trait TryScribeString {
|
pub trait TryScribeString {
|
||||||
fn try_scribe(&self) -> Option<String>;
|
fn try_scribe(&self) -> Option<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/// Trait for converting an enum to a clone-on-write string.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
|
///
|
||||||
|
/// When deriving this trait, you may specify the string that a particular variant should be
|
||||||
|
/// converted to by annotating it with `#[enumscribe(str = "foo")]`. If this is omitted, the name
|
||||||
|
/// of the variant will be used instead.
|
||||||
|
///
|
||||||
|
/// A maximum of one variant can be annotated with `#[enumscribe(other)]`. This variant must have
|
||||||
|
/// exactly one field, which must implement `Into<String>`. Converting this variant to a string
|
||||||
|
/// will result in whatever the value of its field is.
|
||||||
|
///
|
||||||
|
/// This trait can only be used if none of the enum's variants use `ignore`. If you have variants
|
||||||
|
/// that use `ignore`, use [TryScribeCowStr] instead.
|
||||||
pub trait ScribeCowStr {
|
pub trait ScribeCowStr {
|
||||||
fn scribe(&self) -> Cow<'static, str>;
|
fn scribe(&self) -> Cow<'static, str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
// Trait for converting an enum to a clone-on-write string, or `None` if the conversion fails.
|
||||||
|
///
|
||||||
|
/// Like all of the traits provided by enumscribe, this should not be implemented manually; use
|
||||||
|
/// `#[derive(ScribeStaticStr)]` provided by the [enumscribe_derive] crate instead.
|
||||||
|
///
|
||||||
|
/// When deriving this trait, you may specify the string that a particular variant should be
|
||||||
|
/// converted to by annotating it with `#[enumscribe(str = "foo")]`. If this is omitted, the name
|
||||||
|
/// of the variant will be used instead.
|
||||||
|
///
|
||||||
|
/// A maximum of one variant can be annotated with `#[enumscribe(other)]`. This variant must have
|
||||||
|
/// exactly one field, which must implement `Into<String>`. Converting this variant to a string
|
||||||
|
/// will result in whatever the value of its field is.
|
||||||
|
///
|
||||||
|
/// You may also annotate a variant with `#[enumscribe(ignore)]`, in which case attempting to
|
||||||
|
/// convert the variant to a string will always result in `None`.
|
||||||
pub trait TryScribeCowStr {
|
pub trait TryScribeCowStr {
|
||||||
fn try_scribe(&self) -> Option<Cow<'static, str>>;
|
fn try_scribe(&self) -> Option<Cow<'static, str>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Unscribe {
|
||||||
|
fn unscribe(to_unscribe: &str) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TryUnscribe {
|
||||||
|
fn try_unscribe(to_unscribe: &str) -> Option<Self>;
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//! Derive macros for the traits provided by enumscribe, to help you easily convert your enums
|
||||||
|
//! to strings and vice-versa.
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
@@ -26,6 +29,117 @@ macro_rules! proc_try {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(ScribeStaticStr, attributes(enumscribe))]
|
||||||
|
pub fn derive_scribe_static_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input: DeriveInput = syn::parse(input)
|
||||||
|
.expect("failed to parse input");
|
||||||
|
|
||||||
|
let enum_data = proc_try!(get_enum_data(&input));
|
||||||
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
||||||
|
|
||||||
|
let enum_ident = &input.ident;
|
||||||
|
let enum_idents = iter::repeat(enum_ident);
|
||||||
|
|
||||||
|
let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len());
|
||||||
|
let mut match_results = Vec::with_capacity(parsed_enum.variants.len());
|
||||||
|
|
||||||
|
for variant in parsed_enum.variants.iter() {
|
||||||
|
match variant.match_variant(
|
||||||
|
|name| Ok(quote! {
|
||||||
|
#name
|
||||||
|
}),
|
||||||
|
|_| Err(MacroError::new(format!(
|
||||||
|
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}, so \
|
||||||
|
there is no &'static str associated with it\n\
|
||||||
|
hint: try deriving ScribeCowStr instead",
|
||||||
|
enum_ident.to_string(), variant.data.ident.to_string(), OTHER
|
||||||
|
), variant.span)),
|
||||||
|
) {
|
||||||
|
Ok(Some((pattern, result))) => {
|
||||||
|
match_patterns.push(pattern);
|
||||||
|
match_results.push(result);
|
||||||
|
},
|
||||||
|
|
||||||
|
Ok(None) => return MacroError::new(format!(
|
||||||
|
"cannot derive ScribeStaticStr for {} because the variant {} is marked as {}\n\
|
||||||
|
explanation: since {} is ignored, it cannot be guaranteed that the enum can \
|
||||||
|
always be successfully converted to a String\n\
|
||||||
|
hint: try deriving TryScribeStaticStr instead",
|
||||||
|
enum_ident.to_string(), variant.data.ident.to_string(), IGNORE,
|
||||||
|
variant.data.ident.to_string(),
|
||||||
|
), variant.span).into(),
|
||||||
|
|
||||||
|
Err(err) => return err.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
impl ::enumscribe::ScribeStaticStr for #enum_ident {
|
||||||
|
fn scribe(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#(#enum_idents::#match_patterns => #match_results,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(TryScribeStaticStr, attributes(enumscribe))]
|
||||||
|
pub fn derive_try_scribe_static_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input: DeriveInput = syn::parse(input)
|
||||||
|
.expect("failed to parse input");
|
||||||
|
|
||||||
|
let enum_data = proc_try!(get_enum_data(&input));
|
||||||
|
let parsed_enum = proc_try!(enums::parse_enum(enum_data));
|
||||||
|
|
||||||
|
let enum_ident = &input.ident;
|
||||||
|
let enum_idents = iter::repeat(enum_ident);
|
||||||
|
|
||||||
|
let mut ignore_variant = false;
|
||||||
|
let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len());
|
||||||
|
let mut match_results = Vec::with_capacity(parsed_enum.variants.len());
|
||||||
|
|
||||||
|
for variant in parsed_enum.variants.iter() {
|
||||||
|
match variant.match_variant(
|
||||||
|
|name| Ok(quote! {
|
||||||
|
::std::option::Option::Some(#name)
|
||||||
|
}),
|
||||||
|
|_| Err(MacroError::new(format!(
|
||||||
|
"cannot derive TryScribeStaticStr for {} because the variant {} is marked as {}, so \
|
||||||
|
there is no &'static str associated with it\n\
|
||||||
|
hint: try deriving ScribeCowStr instead",
|
||||||
|
enum_ident.to_string(), variant.data.ident.to_string(), OTHER
|
||||||
|
), variant.span)),
|
||||||
|
) {
|
||||||
|
Ok(Some((pattern, result))) => {
|
||||||
|
match_patterns.push(pattern);
|
||||||
|
match_results.push(result);
|
||||||
|
},
|
||||||
|
|
||||||
|
Ok(None) => ignore_variant = true,
|
||||||
|
|
||||||
|
Err(err) => return err.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ignore_arm = if ignore_variant {
|
||||||
|
quote! { _ => ::std::option::Option::None, }
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
};
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
impl ::enumscribe::TryScribeStaticStr for #enum_ident {
|
||||||
|
fn try_scribe(&self) -> ::std::option::Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
#(#enum_idents::#match_patterns => #match_results,)*
|
||||||
|
#ignore_arm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).into()
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(ScribeString, attributes(enumscribe))]
|
#[proc_macro_derive(ScribeString, attributes(enumscribe))]
|
||||||
pub fn derive_scribe_string(input: TokenStream) -> TokenStream {
|
pub fn derive_scribe_string(input: TokenStream) -> TokenStream {
|
||||||
let input: DeriveInput = syn::parse(input)
|
let input: DeriveInput = syn::parse(input)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use enumscribe::*;
|
|||||||
|
|
||||||
// #[derive(ScribeString)]
|
// #[derive(ScribeString)]
|
||||||
|
|
||||||
#[derive(TryScribeCowStr)]
|
#[derive(TryScribeStaticStr)]
|
||||||
enum Airport {
|
enum Airport {
|
||||||
#[enumscribe(str = "LHR", case_insensitive)]
|
#[enumscribe(str = "LHR", case_insensitive)]
|
||||||
Heathrow,
|
Heathrow,
|
||||||
@@ -12,14 +12,14 @@ enum Airport {
|
|||||||
Luton,
|
Luton,
|
||||||
#[enumscribe(str = "BHX", case_insensitive, ignore)]
|
#[enumscribe(str = "BHX", case_insensitive, ignore)]
|
||||||
BirminghamInternational,
|
BirminghamInternational,
|
||||||
#[enumscribe(other)]
|
// #[enumscribe(other)]
|
||||||
Other(String),
|
// Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let luton = Airport::Luton;
|
let luton = Airport::Luton;
|
||||||
println!("Hello, {:?}!", luton.try_scribe());
|
println!("Hello, {:?}!", luton.try_scribe());
|
||||||
|
|
||||||
let other = Airport::Other("Dedicated EasyJet-only airport".to_owned());
|
// let other = Airport::Other("Dedicated EasyJet-only airport".to_owned());
|
||||||
println!("Hello, {:?}!", other.try_scribe());
|
// println!("Hello, {:?}!", other.try_scribe());
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user