common-macros: Add derive macros for string-wrapping enums
This commit is contained in:
parent
e91a7b7cbf
commit
1f8e8c2e93
48
ruma-common-macros/src/attr.rs
Normal file
48
ruma-common-macros/src/attr.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
LitStr, Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::case::RenameRule;
|
||||||
|
|
||||||
|
mod kw {
|
||||||
|
syn::custom_keyword!(rename);
|
||||||
|
syn::custom_keyword!(rename_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenameAttr(LitStr);
|
||||||
|
|
||||||
|
impl RenameAttr {
|
||||||
|
pub fn into_inner(self) -> LitStr {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for RenameAttr {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let _: kw::rename = input.parse()?;
|
||||||
|
let _: Token![=] = input.parse()?;
|
||||||
|
Ok(Self(input.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenameAllAttr(RenameRule);
|
||||||
|
|
||||||
|
impl RenameAllAttr {
|
||||||
|
pub fn into_inner(self) -> RenameRule {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for RenameAllAttr {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let _: kw::rename_all = input.parse()?;
|
||||||
|
let _: Token![=] = input.parse()?;
|
||||||
|
let s: LitStr = input.parse()?;
|
||||||
|
Ok(Self(
|
||||||
|
s.value()
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| syn::Error::new_spanned(s, "invalid value for rename_all"))?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
22
ruma-common-macros/src/deserialize_from_cow_str.rs
Normal file
22
ruma-common-macros/src/deserialize_from_cow_str.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::util::import_ruma_common;
|
||||||
|
|
||||||
|
pub fn expand_deserialize_from_cow_str(ident: &Ident) -> syn::Result<TokenStream> {
|
||||||
|
let ruma_common = import_ruma_common();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl<'de> #ruma_common::exports::serde::de::Deserialize<'de> for #ident {
|
||||||
|
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: #ruma_common::exports::serde::de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
type CowStr<'a> = ::std::borrow::Cow<'a, ::std::primitive::str>;
|
||||||
|
|
||||||
|
let cow = #ruma_common::exports::ruma_serde::deserialize_cow_str(deserializer)?;
|
||||||
|
Ok(::std::convert::From::<CowStr<'_>>::from(cow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
12
ruma-common-macros/src/display_as_ref_str.rs
Normal file
12
ruma-common-macros/src/display_as_ref_str.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
pub fn expand_display_as_ref_str(ident: &Ident) -> syn::Result<TokenStream> {
|
||||||
|
Ok(quote! {
|
||||||
|
impl ::std::fmt::Display for #ident {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
f.write_str(<Self as ::std::convert::AsRef<::std::primitive::str>>::as_ref(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
58
ruma-common-macros/src/enum_as_ref_str.rs
Normal file
58
ruma-common-macros/src/enum_as_ref_str.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum};
|
||||||
|
|
||||||
|
use crate::util::{get_rename, get_rename_rule};
|
||||||
|
|
||||||
|
pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result<TokenStream> {
|
||||||
|
let enum_name = &input.ident;
|
||||||
|
let rename_rule = get_rename_rule(&input)?;
|
||||||
|
let branches: Vec<_> = input
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
let variant_name = &v.ident;
|
||||||
|
let (field_capture, variant_str) = match (get_rename(v)?, &v.fields) {
|
||||||
|
(None, Fields::Unit) => (
|
||||||
|
None,
|
||||||
|
rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(),
|
||||||
|
),
|
||||||
|
(Some(rename), Fields::Unit) => (None, rename.into_token_stream()),
|
||||||
|
(None, Fields::Named(FieldsNamed { named: fields, .. }))
|
||||||
|
| (None, Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. })) => {
|
||||||
|
if fields.len() != 1 {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
v,
|
||||||
|
"multiple data fields are not supported",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let capture = match &fields[0].ident {
|
||||||
|
Some(name) => quote! { { #name: inner } },
|
||||||
|
None => quote! { (inner) },
|
||||||
|
};
|
||||||
|
|
||||||
|
(Some(capture), quote! { inner })
|
||||||
|
}
|
||||||
|
(Some(_), _) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
v,
|
||||||
|
"ruma_enum(rename) is only allowed on unit variants",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
#enum_name :: #variant_name #field_capture => #variant_str
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl ::std::convert::AsRef<::std::primitive::str> for #enum_name {
|
||||||
|
fn as_ref(&self) -> &::std::primitive::str {
|
||||||
|
match self { #(#branches),* }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
84
ruma-common-macros/src/enum_from_string.rs
Normal file
84
ruma-common-macros/src/enum_from_string.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum};
|
||||||
|
|
||||||
|
use crate::util::{get_rename, get_rename_rule};
|
||||||
|
|
||||||
|
pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result<TokenStream> {
|
||||||
|
let enum_name = &input.ident;
|
||||||
|
let rename_rule = get_rename_rule(&input)?;
|
||||||
|
let mut fallback = None;
|
||||||
|
let mut fallback_ty = None;
|
||||||
|
let branches: Vec<_> = input
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
let variant_name = &v.ident;
|
||||||
|
let variant_str = match (get_rename(v)?, &v.fields) {
|
||||||
|
(None, Fields::Unit) => Some(
|
||||||
|
rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(),
|
||||||
|
),
|
||||||
|
(Some(rename), Fields::Unit) => Some(rename.into_token_stream()),
|
||||||
|
(None, Fields::Named(FieldsNamed { named: fields, .. }))
|
||||||
|
| (None, Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. })) => {
|
||||||
|
if fields.len() != 1 {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
v,
|
||||||
|
"multiple data fields are not supported",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if fallback.is_some() {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
v,
|
||||||
|
"multiple data-carrying variants are not supported",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let member = match &fields[0].ident {
|
||||||
|
Some(name) => name.into_token_stream(),
|
||||||
|
None => quote! { 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
fallback = Some(quote! {
|
||||||
|
_ => #enum_name :: #variant_name { #member: s.into() }
|
||||||
|
});
|
||||||
|
|
||||||
|
fallback_ty = Some(&fields[0].ty);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(Some(_), _) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
v,
|
||||||
|
"ruma_enum(rename) is only allowed on unit variants",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(variant_str.map(|s| quote! { #s => #enum_name :: #variant_name }))
|
||||||
|
})
|
||||||
|
.collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
|
// Remove `None` from the iterator to avoid emitting consecutive commas in repetition
|
||||||
|
let branches = branches.iter().flatten();
|
||||||
|
|
||||||
|
if fallback.is_none() {
|
||||||
|
return Err(syn::Error::new(Span::call_site(), "required fallback variant not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl<T> ::std::convert::From<T> for #enum_name
|
||||||
|
where
|
||||||
|
T: ::std::convert::AsRef<::std::primitive::str>
|
||||||
|
+ ::std::convert::Into<#fallback_ty>
|
||||||
|
{
|
||||||
|
fn from(s: T) -> Self {
|
||||||
|
match s.as_ref() {
|
||||||
|
#( #branches, )*
|
||||||
|
#fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,9 +1,22 @@
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, DeriveInput, ItemEnum};
|
||||||
|
|
||||||
|
use deserialize_from_cow_str::expand_deserialize_from_cow_str;
|
||||||
|
use display_as_ref_str::expand_display_as_ref_str;
|
||||||
|
use enum_as_ref_str::expand_enum_as_ref_str;
|
||||||
|
use enum_from_string::expand_enum_from_string;
|
||||||
use outgoing::expand_derive_outgoing;
|
use outgoing::expand_derive_outgoing;
|
||||||
|
use serialize_as_ref_str::expand_serialize_as_ref_str;
|
||||||
|
|
||||||
|
mod attr;
|
||||||
|
mod case;
|
||||||
|
mod deserialize_from_cow_str;
|
||||||
|
mod display_as_ref_str;
|
||||||
|
mod enum_as_ref_str;
|
||||||
|
mod enum_from_string;
|
||||||
mod outgoing;
|
mod outgoing;
|
||||||
|
mod serialize_as_ref_str;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
|
/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
|
||||||
@ -53,3 +66,62 @@ pub fn derive_outgoing(input: TokenStream) -> TokenStream {
|
|||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into()
|
expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(AsRefStr, attributes(ruma_enum))]
|
||||||
|
pub fn derive_enum_as_ref_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as ItemEnum);
|
||||||
|
expand_enum_as_ref_str(&input).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(FromString, attributes(ruma_enum))]
|
||||||
|
pub fn derive_enum_from_string(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as ItemEnum);
|
||||||
|
expand_enum_from_string(&input).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: The following macros aren't actually interested in type details beyond name (and possibly
|
||||||
|
// generics in the future). They probably shouldn't use `DeriveInput`.
|
||||||
|
|
||||||
|
#[proc_macro_derive(DisplayAsRefStr)]
|
||||||
|
pub fn derive_display_as_ref_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
expand_display_as_ref_str(&input.ident).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(SerializeAsRefStr)]
|
||||||
|
pub fn derive_serialize_as_ref_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
expand_serialize_as_ref_str(&input.ident).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(DeserializeFromCowStr)]
|
||||||
|
pub fn derive_deserialize_from_cow_str(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
expand_deserialize_from_cow_str(&input.ident)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shorthand for the derives `AsRefStr`, `FromString`, `DisplayAsRefStr`, `SerializeAsRefStr` and
|
||||||
|
/// `DeserializeFromCowStr`.
|
||||||
|
#[proc_macro_derive(StringEnum, attributes(ruma_enum))]
|
||||||
|
pub fn derive_string_enum(input: TokenStream) -> TokenStream {
|
||||||
|
fn expand_all(input: ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
|
let as_ref_str_impl = expand_enum_as_ref_str(&input)?;
|
||||||
|
let from_string_impl = expand_enum_from_string(&input)?;
|
||||||
|
let display_impl = expand_display_as_ref_str(&input.ident)?;
|
||||||
|
let serialize_impl = expand_serialize_as_ref_str(&input.ident)?;
|
||||||
|
let deserialize_impl = expand_deserialize_from_cow_str(&input.ident)?;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
#as_ref_str_impl
|
||||||
|
#from_string_impl
|
||||||
|
#display_impl
|
||||||
|
#serialize_impl
|
||||||
|
#deserialize_impl
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let input = parse_macro_input!(input as ItemEnum);
|
||||||
|
expand_all(input).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
|
}
|
||||||
|
20
ruma-common-macros/src/serialize_as_ref_str.rs
Normal file
20
ruma-common-macros/src/serialize_as_ref_str.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::util::import_ruma_common;
|
||||||
|
|
||||||
|
pub fn expand_serialize_as_ref_str(ident: &Ident) -> syn::Result<TokenStream> {
|
||||||
|
let ruma_common = import_ruma_common();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl #ruma_common::exports::serde::ser::Serialize for #ident {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: #ruma_common::exports::serde::ser::Serializer,
|
||||||
|
{
|
||||||
|
<Self as ::std::convert::AsRef<::std::primitive::str>>::as_ref(self)
|
||||||
|
.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,6 +1,12 @@
|
|||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use proc_macro_crate::crate_name;
|
use proc_macro_crate::crate_name;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::{ItemEnum, LitStr, Variant};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
attr::{RenameAllAttr, RenameAttr},
|
||||||
|
case::RenameRule,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn import_ruma_common() -> TokenStream {
|
pub fn import_ruma_common() -> TokenStream {
|
||||||
if let Ok(possibly_renamed) = crate_name("ruma-common") {
|
if let Ok(possibly_renamed) = crate_name("ruma-common") {
|
||||||
@ -13,3 +19,35 @@ pub fn import_ruma_common() -> TokenStream {
|
|||||||
quote! { ::ruma_common }
|
quote! { ::ruma_common }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rename_rule(input: &ItemEnum) -> syn::Result<RenameRule> {
|
||||||
|
let rules: Vec<_> = input
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path.is_ident("ruma_enum"))
|
||||||
|
.map(|attr| attr.parse_args::<RenameAllAttr>().map(RenameAllAttr::into_inner))
|
||||||
|
.collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
|
match rules.len() {
|
||||||
|
0 => Ok(RenameRule::None),
|
||||||
|
1 => Ok(rules[0]),
|
||||||
|
_ => Err(syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"found multiple ruma_enum(rename_all) attributes",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rename(input: &Variant) -> syn::Result<Option<LitStr>> {
|
||||||
|
let renames: Vec<_> = input
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path.is_ident("ruma_enum"))
|
||||||
|
.map(|attr| attr.parse_args::<RenameAttr>().map(RenameAttr::into_inner))
|
||||||
|
.collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
|
match renames.len() {
|
||||||
|
0 | 1 => Ok(renames.into_iter().next()),
|
||||||
|
_ => Err(syn::Error::new(Span::call_site(), "found multiple ruma_enum(rename) attributes")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ pub mod push;
|
|||||||
mod raw;
|
mod raw;
|
||||||
pub mod thirdparty;
|
pub mod thirdparty;
|
||||||
|
|
||||||
pub use ruma_common_macros::Outgoing;
|
pub use ruma_common_macros::*;
|
||||||
|
|
||||||
pub use self::raw::Raw;
|
pub use self::raw::Raw;
|
||||||
|
|
||||||
@ -33,5 +33,6 @@ extern crate self as ruma_common;
|
|||||||
/// It is not considered part of ruma-common's public API.
|
/// It is not considered part of ruma-common's public API.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod exports {
|
pub mod exports {
|
||||||
|
pub use ruma_serde;
|
||||||
pub use serde;
|
pub use serde;
|
||||||
}
|
}
|
||||||
|
57
ruma-common/tests/enum_derive.rs
Normal file
57
ruma-common/tests/enum_derive.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use ruma_common::StringEnum;
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, StringEnum)]
|
||||||
|
#[ruma_enum(rename_all = "snake_case")]
|
||||||
|
enum MyEnum {
|
||||||
|
First,
|
||||||
|
Second,
|
||||||
|
#[ruma_enum(rename = "m.third")]
|
||||||
|
Third,
|
||||||
|
HelloWorld,
|
||||||
|
_Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_ref_str() {
|
||||||
|
assert_eq!(MyEnum::First.as_ref(), "first");
|
||||||
|
assert_eq!(MyEnum::Second.as_ref(), "second");
|
||||||
|
assert_eq!(MyEnum::Third.as_ref(), "m.third");
|
||||||
|
assert_eq!(MyEnum::HelloWorld.as_ref(), "hello_world");
|
||||||
|
assert_eq!(MyEnum::_Custom("HelloWorld".into()).as_ref(), "HelloWorld");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display() {
|
||||||
|
assert_eq!(MyEnum::First.to_string(), "first");
|
||||||
|
assert_eq!(MyEnum::Second.to_string(), "second");
|
||||||
|
assert_eq!(MyEnum::Third.to_string(), "m.third");
|
||||||
|
assert_eq!(MyEnum::HelloWorld.to_string(), "hello_world");
|
||||||
|
assert_eq!(MyEnum::_Custom("HelloWorld".into()).to_string(), "HelloWorld");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_string() {
|
||||||
|
assert_eq!(MyEnum::from("first"), MyEnum::First);
|
||||||
|
assert_eq!(MyEnum::from("second"), MyEnum::Second);
|
||||||
|
assert_eq!(MyEnum::from("m.third"), MyEnum::Third);
|
||||||
|
assert_eq!(MyEnum::from("hello_world"), MyEnum::HelloWorld);
|
||||||
|
assert_eq!(MyEnum::from("HelloWorld"), MyEnum::_Custom("HelloWorld".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
assert_eq!(to_json_value(MyEnum::First).unwrap(), json!("first"));
|
||||||
|
assert_eq!(to_json_value(MyEnum::HelloWorld).unwrap(), json!("hello_world"));
|
||||||
|
assert_eq!(to_json_value(MyEnum::_Custom("\\\n\\".into())).unwrap(), json!("\\\n\\"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
assert_eq!(from_json_value::<MyEnum>(json!("first")).unwrap(), MyEnum::First);
|
||||||
|
assert_eq!(from_json_value::<MyEnum>(json!("hello_world")).unwrap(), MyEnum::HelloWorld);
|
||||||
|
assert_eq!(
|
||||||
|
from_json_value::<MyEnum>(json!("\\\n\\")).unwrap(),
|
||||||
|
MyEnum::_Custom("\\\n\\".into())
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user