From 974e5a2b206c166086614de06f2e82c8908d1b55 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 23 Feb 2025 12:13:29 -0800 Subject: [PATCH] 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. --- komorebi-bar/src/config.rs | 87 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/komorebi-bar/src/config.rs b/komorebi-bar/src/config.rs index bd521ca5..e2066b30 100644 --- a/komorebi-bar/src/config.rs +++ b/komorebi-bar/src/config.rs @@ -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::(&raw).is_ok()) + } + + #[test] + pub fn extend_existing_variant() { + let raw = json!({ + "format": "NoneOfTheThings", + }) + .to_string(); + + assert!(serde_json::from_str::(&raw).is_ok()) + } + + #[test] + pub fn extend_invalid_variant() { + let raw = json!({ + "format": "ALLOFTHETHINGS", + }) + .to_string(); + + assert!(serde_json::from_str::(&raw).is_err()) + } +}