refactor(bar): add extend_enum! macro

This commit adds an extend_enum! macro and some unit tests to provide
an ergonomic way to specialize configs for specific (sub)widgets.

The macro takes the name of the existing enum, the desired name of the
new, extended enum, and a list of variants which should be added to the
extended enum.

The macro will add new variants to a new enum definition first, before
wrapping the existing enum in an Existing variant and tagging it with
the `#[serde(untagged)]` annotation.

From is also implemented for ergonomic from/into calls between shared
variants of the existing and extended enums.
This commit is contained in:
LGUG2Z
2025-02-23 12:13:29 -08:00
parent 47f4f47d3f
commit fc0da2c1d3

View File

@@ -416,3 +416,90 @@ pub enum DisplayFormat {
/// Show an icon and text for the selected element, and icons on the rest
IconAndTextOnSelected,
}
macro_rules! extend_enum {
($existing_enum:ident, $new_enum:ident, { $($(#[$meta:meta])* $variant:ident),* $(,)? }) => {
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, schemars::JsonSchema, PartialEq)]
pub enum $new_enum {
// Add new variants
$(
$(#[$meta])*
$variant,
)*
// Include a variant that wraps the existing enum and flatten it when deserializing
#[serde(untagged)]
Existing($existing_enum),
}
// Implement From for the existing enum
impl From<$existing_enum> for $new_enum {
fn from(value: $existing_enum) -> Self {
$new_enum::Existing(value)
}
}
};
}
extend_enum!(DisplayFormat, WorkspacesDisplayFormat, {
/// Show all icons only
AllIcons,
/// Show both all icons and text
AllIconsAndText,
/// Show all icons and text for the selected element, and all icons on the rest
AllIconsAndTextOnSelected,
});
#[cfg(test)]
mod tests {
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use serde_json::json;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub enum OriginalDisplayFormat {
/// Show None Of The Things
NoneOfTheThings,
}
extend_enum!(OriginalDisplayFormat, ExtendedDisplayFormat, {
/// Show Some Of The Things
SomeOfTheThings,
});
#[derive(serde::Deserialize)]
struct ExampleConfig {
#[allow(unused)]
format: ExtendedDisplayFormat,
}
#[test]
pub fn extend_new_variant() {
let raw = json!({
"format": "SomeOfTheThings",
})
.to_string();
assert!(serde_json::from_str::<ExampleConfig>(&raw).is_ok())
}
#[test]
pub fn extend_existing_variant() {
let raw = json!({
"format": "NoneOfTheThings",
})
.to_string();
assert!(serde_json::from_str::<ExampleConfig>(&raw).is_ok())
}
#[test]
pub fn extend_invalid_variant() {
let raw = json!({
"format": "ALLOFTHETHINGS",
})
.to_string();
assert!(serde_json::from_str::<ExampleConfig>(&raw).is_err())
}
}