Skip to content

Commit

Permalink
feat: Add support for string-like enums
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Sep 20, 2024
1 parent 93d164f commit 5954387
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 7 deletions.
28 changes: 28 additions & 0 deletions merde/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,34 @@ fn main() {
}
```

"string-like" enums are also supported, like so:

```rust
#[derive(Debug)]
enum Emergency {
Cuddle,
Smoothie,
Naptime,
Playtime,
}

merde::derive! {
impl (JsonSerialize, ValueDeserialize) for enum Emergency
string_like {
"cuddle" => Cuddle,
"smoothie" => Smoothie,
"naptime" => Naptime,
"playtime" => Playtime,
}
}

fn main() {
let input = r#"["cuddle", "smoothie", "playtime"]"#;
let emergencies: Vec<Emergency> = merde::json::from_str_via_value(input).unwrap();
println!("Emergencies: {:?}", emergencies);
}
```

### Interlude: why not `&'s str`?

You'll notice that `ValueDeserialize` is not implemented for `&'s str`, ie.
Expand Down
19 changes: 19 additions & 0 deletions merde/examples/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ fn main() {
Event::TextInput(TextInput {
text: "Hello".into(),
}),
Event::StringStuff(StringStuff("Some string".into())),
Event::Emergency(Emergency::NoPizzaLeft),
];

for event in events {
Expand All @@ -28,6 +30,7 @@ enum Event<'s> {
MouseDown(MouseDown),
TextInput(TextInput<'s>),
StringStuff(StringStuff<'s>),
Emergency(Emergency),
}

merde::derive! {
Expand All @@ -37,6 +40,7 @@ merde::derive! {
"mousedown" => MouseDown,
"textinput" => TextInput,
"stringstuff" => StringStuff,
"emergency" => Emergency,
}
}

Expand Down Expand Up @@ -79,3 +83,18 @@ struct StringStuff<'s>(CowStr<'s>);
merde::derive! {
impl (JsonSerialize, ValueDeserialize) for struct StringStuff<'s> transparent
}

#[derive(Debug, PartialEq, Eq)]
enum Emergency {
NoPizzaLeft,
CuddlesRequired,
SmoothieReady,
}

merde::derive! {
impl (JsonSerialize, ValueDeserialize) for enum Emergency string_like {
"nopizza" => NoPizzaLeft,
"cuddles" => CuddlesRequired,
"smoothie" => SmoothieReady,
}
}
157 changes: 150 additions & 7 deletions merde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,40 @@ macro_rules! impl_value_deserialize {
}
}
};

// owned enum (externally tagged, string-like)
(enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
#[automatically_derived]
impl<'s> $crate::ValueDeserialize<'s> for $enum_name {
fn from_value_ref(
value: Option<&$crate::Value<'s>>,
) -> Result<Self, $crate::MerdeError> {
#[allow(unused_imports)]
use $crate::MerdeError;

let s = value.ok_or(MerdeError::MissingValue)?.as_str()?;
match s.as_ref() {
$($variant_str => Ok($enum_name::$variant),)*
_ => Err(MerdeError::UnknownProperty(s.to_string())),
}
}

fn from_value(
value: Option<$crate::Value<'s>>,
) -> Result<Self, $crate::MerdeError> {
#[allow(unused_imports)]
use $crate::MerdeError;

let s = value.ok_or(MerdeError::MissingValue)?.into_str()?;
match s.as_ref() {
$($variant_str => Ok($enum_name::$variant),)*
_ => Err(MerdeError::UnknownProperty(s.to_string())),
}
}
}
};
}

#[doc(hidden)]
Expand Down Expand Up @@ -222,11 +256,11 @@ macro_rules! impl_into_static {
(struct $struct_name:ident <$lifetime:lifetime> transparent) => {
#[automatically_derived]
impl<$lifetime> $crate::IntoStatic for $struct_name<$lifetime> {
type Output = $struct_name<$lifetime>;
type Output = $struct_name<'static>;

#[inline(always)]
fn into_static(self) -> Self::Output {
Self(self.0.into_static())
$struct_name(self.0.into_static())
}
}
};
Expand Down Expand Up @@ -282,18 +316,33 @@ macro_rules! impl_into_static {
}) => {
#[automatically_derived]
impl<$lifetime> $crate::IntoStatic for $enum_name<$lifetime> {
type Output = $enum_name<$lifetime>;
type Output = $enum_name<'static>;

#[inline(always)]
fn into_static(self) -> Self::Output {
match self {
$(
Self::$variant(value) => Self::$variant(value.into_static()),
Self::$variant(value) => $enum_name::$variant(value.into_static()),
)+
}
}
}
};

// owned enum (string-like)
(enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
#[automatically_derived]
impl $crate::IntoStatic for $enum_name {
type Output = $enum_name;

#[inline(always)]
fn into_static(self) -> Self::Output {
self
}
}
};
}

#[doc(hidden)]
Expand Down Expand Up @@ -356,8 +405,20 @@ macro_rules! impl_with_lifetime {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
#[automatically_derived]
impl<'s> $crate::WithLifetime<'s> for $enum_name<$lifetime> {
type Lifetimed = $enum_name<$lifetime>;
impl<$lifetime, 'instantiated_lifetime> $crate::WithLifetime<'instantiated_lifetime>
for $enum_name<$lifetime>
{
type Lifetimed = $enum_name<'instantiated_lifetime>;
}
};

// owned enum (string-like)
(enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
#[automatically_derived]
impl<'s> $crate::WithLifetime<'s> for $enum_name {
type Lifetimed = $enum_name;
}
};
}
Expand Down Expand Up @@ -468,6 +529,27 @@ macro_rules! impl_json_serialize {
}
}
};

// owned enum (string-like)
(enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
#[automatically_derived]
impl $crate::json::JsonSerialize for $enum_name {
fn json_serialize(&self, serializer: &mut $crate::json::JsonSerializer) {
#[allow(unused_imports)]
use $crate::MerdeError;

match self {
$(
Self::$variant => {
serializer.write_str($variant_str);
}
)+
}
}
}
};
}

#[doc(hidden)]
Expand All @@ -480,6 +562,8 @@ macro_rules! impl_json_serialize {
#[doc(hidden)]
#[macro_export]
macro_rules! impl_trait {
//------------------------------------------------------------------------------------

// owned struct
(@impl JsonSerialize, struct $struct_name:ident { $($field:ident),+ }) => {
$crate::impl_json_serialize!(struct $struct_name { $($field),+ });
Expand All @@ -504,6 +588,14 @@ macro_rules! impl_trait {
$($variant_str => $variant),+
});
};
// owned enum (string-like)
(@impl JsonSerialize, enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
$crate::impl_json_serialize!(enum $enum_name string_like {
$($variant_str => $variant),+
});
};
// owned tuple struct (transparent)
(@impl JsonSerialize, struct $struct_name:ident transparent) => {
$crate::impl_json_serialize!(struct $struct_name transparent);
Expand All @@ -513,6 +605,8 @@ macro_rules! impl_trait {
$crate::impl_json_serialize!(struct $struct_name <$lifetime> transparent);
};

//------------------------------------------------------------------------------------

// owned struct
(@impl ValueDeserialize, struct $struct_name:ident { $($field:ident),+ }) => {
$crate::impl_value_deserialize!(struct $struct_name { $($field),+ });
Expand All @@ -532,6 +626,12 @@ macro_rules! impl_trait {
$crate::impl_value_deserialize!(enum $enum_name externally_tagged {
$($variant_str => $variant),+
});
$crate::impl_into_static!(enum $enum_name externally_tagged {
$($variant_str => $variant),+
});
$crate::impl_with_lifetime!(enum $enum_name externally_tagged {
$($variant_str => $variant),+
});
};
// lifetimed enum (externally tagged)
(@impl ValueDeserialize, enum $enum_name:ident <$lifetime:lifetime> externally_tagged {
Expand All @@ -540,14 +640,38 @@ macro_rules! impl_trait {
$crate::impl_value_deserialize!(enum $enum_name <$lifetime> externally_tagged {
$($variant_str => $variant),+
});
$crate::impl_into_static!(enum $enum_name <$lifetime> externally_tagged {
$($variant_str => $variant),+
});
$crate::impl_with_lifetime!(enum $enum_name <$lifetime> externally_tagged {
$($variant_str => $variant),+
});
};
// owned enum (string-like)
(@impl ValueDeserialize, enum $enum_name:ident string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
$crate::impl_value_deserialize!(enum $enum_name string_like {
$($variant_str => $variant),+
});
$crate::impl_into_static!(enum $enum_name string_like {
$($variant_str => $variant),+
});
$crate::impl_with_lifetime!(enum $enum_name string_like {
$($variant_str => $variant),+
});
};
// owned tuple struct (transparent)
(@impl ValueDeserialize, struct $struct_name:ident transparent) => {
$crate::impl_value_deserialize!(struct $struct_name transparent);
$crate::impl_into_static!(struct $struct_name transparent);
$crate::impl_with_lifetime!(struct $struct_name transparent);
};
// lifetimed tuple struct (transparent)
(@impl ValueDeserialize, struct $struct_name:ident <$lifetime:lifetime> transparent) => {
$crate::impl_value_deserialize!(struct $struct_name <$lifetime> transparent);
$crate::impl_into_static!(struct $struct_name <$lifetime> transparent);
$crate::impl_with_lifetime!(struct $struct_name <$lifetime> transparent);
};
}

Expand Down Expand Up @@ -628,7 +752,7 @@ macro_rules! impl_trait {
/// }
///
/// merde::derive! {
/// impl (JsonSerialize, ValueDeserialize) for enum MyLifetimedEnum<'a>
/// impl (JsonSerialize, ValueDeserialize) for enum MyLifetimedEnum<'wot>
/// externally_tagged {
/// "variant1" => Variant1,
/// "variant2" => Variant2,
Expand Down Expand Up @@ -729,6 +853,25 @@ macro_rules! derive {
$crate::impl_trait!(@impl $trait, enum $enum_name <$lifetime> externally_tagged $variants);
};
(@step1 { } enum $enum_name:ident <$lifetime:lifetime> externally_tagged $variants:tt) => {};

// owned enums (string-like)
(impl ($($trait:ident),+) for enum $enum_name:ident
string_like {
$($variant_str:literal => $variant:ident),+ $(,)?
}) => {
$crate::derive!(@step1 { $($trait),+ } enum $enum_name string_like {
$($variant_str => $variant),+
});
};
(@step1 { $trait:ident, $($rest_traits:ident),* } enum $enum_name:ident
string_like $variants:tt) => {
$crate::impl_trait!(@impl $trait, enum $enum_name string_like $variants);
$crate::derive!(@step1 { $($rest_traits),* } enum $enum_name string_like $variants);
};
(@step1 { $trait:ident } enum $enum_name:ident string_like $variants:tt) => {
$crate::impl_trait!(@impl $trait, enum $enum_name string_like $variants);
};
(@step1 { } enum $enum_name:ident string_like $variants:tt) => {};
}

#[cfg(test)]
Expand Down

0 comments on commit 5954387

Please sign in to comment.