Add StatusColorField enum and ability to set fields via enum

Michael Sloan created

Change summary

Cargo.lock                            |   2 
crates/theme/src/styles/colors.rs     | 254 +---------------------------
crates/theme/src/styles/status.rs     |  11 +
crates/util/Cargo.toml                |   5 
crates/util/src/util.rs               |  45 +++++
crates/util_macros/Cargo.toml         |   2 
crates/util_macros/src/util_macros.rs | 161 +++++++++++++++++
7 files changed, 229 insertions(+), 251 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -18579,6 +18579,8 @@ dependencies = [
 name = "util_macros"
 version = "0.1.0"
 dependencies = [
+ "convert_case 0.8.0",
+ "proc-macro2",
  "quote",
  "syn 2.0.101",
  "workspace-hack",

crates/theme/src/styles/colors.rs 🔗

@@ -4,14 +4,22 @@ use gpui::{App, Hsla, SharedString, WindowBackgroundAppearance};
 use refineable::Refineable;
 use std::sync::Arc;
 use strum::{AsRefStr, EnumIter, IntoEnumIterator};
+use util::FieldAccessByEnum;
 
 use crate::{
     AccentColors, ActiveTheme, PlayerColors, StatusColors, StatusColorsRefinement, SyntaxTheme,
     SystemColors,
 };
 
-#[derive(Refineable, Clone, Debug, PartialEq)]
+#[derive(Refineable, FieldAccessByEnum, Clone, Debug, PartialEq)]
 #[refineable(Debug, serde::Deserialize)]
+#[field_access_by_enum(
+    enum_name = "ThemeColorField",
+    enum_attrs = [
+        derive(Debug, Clone, Copy, EnumIter, AsRefStr),
+        strum(serialize_all = "snake_case")
+    ]
+)]
 pub struct ThemeColors {
     /// Border color. Used for most borders, is usually a high contrast color.
     pub border: Hsla,
@@ -288,249 +296,9 @@ pub struct ThemeColors {
     pub version_control_conflict_marker_theirs: Hsla,
 }
 
-#[derive(EnumIter, Debug, Clone, Copy, AsRefStr)]
-#[strum(serialize_all = "snake_case")]
-pub enum ThemeColorField {
-    Border,
-    BorderVariant,
-    BorderFocused,
-    BorderSelected,
-    BorderTransparent,
-    BorderDisabled,
-    ElevatedSurfaceBackground,
-    SurfaceBackground,
-    Background,
-    ElementBackground,
-    ElementHover,
-    ElementActive,
-    ElementSelected,
-    ElementDisabled,
-    DropTargetBackground,
-    DropTargetBorder,
-    GhostElementBackground,
-    GhostElementHover,
-    GhostElementActive,
-    GhostElementSelected,
-    GhostElementDisabled,
-    Text,
-    TextMuted,
-    TextPlaceholder,
-    TextDisabled,
-    TextAccent,
-    Icon,
-    IconMuted,
-    IconDisabled,
-    IconPlaceholder,
-    IconAccent,
-    StatusBarBackground,
-    TitleBarBackground,
-    TitleBarInactiveBackground,
-    ToolbarBackground,
-    TabBarBackground,
-    TabInactiveBackground,
-    TabActiveBackground,
-    SearchMatchBackground,
-    PanelBackground,
-    PanelFocusedBorder,
-    PanelIndentGuide,
-    PanelIndentGuideHover,
-    PanelIndentGuideActive,
-    PanelOverlayBackground,
-    PanelOverlayHover,
-    PaneFocusedBorder,
-    PaneGroupBorder,
-    ScrollbarThumbBackground,
-    ScrollbarThumbHoverBackground,
-    ScrollbarThumbActiveBackground,
-    ScrollbarThumbBorder,
-    ScrollbarTrackBackground,
-    ScrollbarTrackBorder,
-    MinimapThumbBackground,
-    MinimapThumbHoverBackground,
-    MinimapThumbActiveBackground,
-    MinimapThumbBorder,
-    EditorForeground,
-    EditorBackground,
-    EditorGutterBackground,
-    EditorSubheaderBackground,
-    EditorActiveLineBackground,
-    EditorHighlightedLineBackground,
-    EditorLineNumber,
-    EditorActiveLineNumber,
-    EditorInvisible,
-    EditorWrapGuide,
-    EditorActiveWrapGuide,
-    EditorIndentGuide,
-    EditorIndentGuideActive,
-    EditorDocumentHighlightReadBackground,
-    EditorDocumentHighlightWriteBackground,
-    EditorDocumentHighlightBracketBackground,
-    TerminalBackground,
-    TerminalForeground,
-    TerminalBrightForeground,
-    TerminalDimForeground,
-    TerminalAnsiBackground,
-    TerminalAnsiBlack,
-    TerminalAnsiBrightBlack,
-    TerminalAnsiDimBlack,
-    TerminalAnsiRed,
-    TerminalAnsiBrightRed,
-    TerminalAnsiDimRed,
-    TerminalAnsiGreen,
-    TerminalAnsiBrightGreen,
-    TerminalAnsiDimGreen,
-    TerminalAnsiYellow,
-    TerminalAnsiBrightYellow,
-    TerminalAnsiDimYellow,
-    TerminalAnsiBlue,
-    TerminalAnsiBrightBlue,
-    TerminalAnsiDimBlue,
-    TerminalAnsiMagenta,
-    TerminalAnsiBrightMagenta,
-    TerminalAnsiDimMagenta,
-    TerminalAnsiCyan,
-    TerminalAnsiBrightCyan,
-    TerminalAnsiDimCyan,
-    TerminalAnsiWhite,
-    TerminalAnsiBrightWhite,
-    TerminalAnsiDimWhite,
-    LinkTextHover,
-    VersionControlAdded,
-    VersionControlDeleted,
-    VersionControlModified,
-    VersionControlRenamed,
-    VersionControlConflict,
-    VersionControlIgnored,
-}
-
 impl ThemeColors {
-    pub fn color(&self, field: ThemeColorField) -> Hsla {
-        match field {
-            ThemeColorField::Border => self.border,
-            ThemeColorField::BorderVariant => self.border_variant,
-            ThemeColorField::BorderFocused => self.border_focused,
-            ThemeColorField::BorderSelected => self.border_selected,
-            ThemeColorField::BorderTransparent => self.border_transparent,
-            ThemeColorField::BorderDisabled => self.border_disabled,
-            ThemeColorField::ElevatedSurfaceBackground => self.elevated_surface_background,
-            ThemeColorField::SurfaceBackground => self.surface_background,
-            ThemeColorField::Background => self.background,
-            ThemeColorField::ElementBackground => self.element_background,
-            ThemeColorField::ElementHover => self.element_hover,
-            ThemeColorField::ElementActive => self.element_active,
-            ThemeColorField::ElementSelected => self.element_selected,
-            ThemeColorField::ElementDisabled => self.element_disabled,
-            ThemeColorField::DropTargetBackground => self.drop_target_background,
-            ThemeColorField::DropTargetBorder => self.drop_target_border,
-            ThemeColorField::GhostElementBackground => self.ghost_element_background,
-            ThemeColorField::GhostElementHover => self.ghost_element_hover,
-            ThemeColorField::GhostElementActive => self.ghost_element_active,
-            ThemeColorField::GhostElementSelected => self.ghost_element_selected,
-            ThemeColorField::GhostElementDisabled => self.ghost_element_disabled,
-            ThemeColorField::Text => self.text,
-            ThemeColorField::TextMuted => self.text_muted,
-            ThemeColorField::TextPlaceholder => self.text_placeholder,
-            ThemeColorField::TextDisabled => self.text_disabled,
-            ThemeColorField::TextAccent => self.text_accent,
-            ThemeColorField::Icon => self.icon,
-            ThemeColorField::IconMuted => self.icon_muted,
-            ThemeColorField::IconDisabled => self.icon_disabled,
-            ThemeColorField::IconPlaceholder => self.icon_placeholder,
-            ThemeColorField::IconAccent => self.icon_accent,
-            ThemeColorField::StatusBarBackground => self.status_bar_background,
-            ThemeColorField::TitleBarBackground => self.title_bar_background,
-            ThemeColorField::TitleBarInactiveBackground => self.title_bar_inactive_background,
-            ThemeColorField::ToolbarBackground => self.toolbar_background,
-            ThemeColorField::TabBarBackground => self.tab_bar_background,
-            ThemeColorField::TabInactiveBackground => self.tab_inactive_background,
-            ThemeColorField::TabActiveBackground => self.tab_active_background,
-            ThemeColorField::SearchMatchBackground => self.search_match_background,
-            ThemeColorField::PanelBackground => self.panel_background,
-            ThemeColorField::PanelFocusedBorder => self.panel_focused_border,
-            ThemeColorField::PanelIndentGuide => self.panel_indent_guide,
-            ThemeColorField::PanelIndentGuideHover => self.panel_indent_guide_hover,
-            ThemeColorField::PanelIndentGuideActive => self.panel_indent_guide_active,
-            ThemeColorField::PanelOverlayBackground => self.panel_overlay_background,
-            ThemeColorField::PanelOverlayHover => self.panel_overlay_hover,
-            ThemeColorField::PaneFocusedBorder => self.pane_focused_border,
-            ThemeColorField::PaneGroupBorder => self.pane_group_border,
-            ThemeColorField::ScrollbarThumbBackground => self.scrollbar_thumb_background,
-            ThemeColorField::ScrollbarThumbHoverBackground => self.scrollbar_thumb_hover_background,
-            ThemeColorField::ScrollbarThumbActiveBackground => {
-                self.scrollbar_thumb_active_background
-            }
-            ThemeColorField::ScrollbarThumbBorder => self.scrollbar_thumb_border,
-            ThemeColorField::ScrollbarTrackBackground => self.scrollbar_track_background,
-            ThemeColorField::ScrollbarTrackBorder => self.scrollbar_track_border,
-            ThemeColorField::MinimapThumbBackground => self.minimap_thumb_background,
-            ThemeColorField::MinimapThumbHoverBackground => self.minimap_thumb_hover_background,
-            ThemeColorField::MinimapThumbActiveBackground => self.minimap_thumb_active_background,
-            ThemeColorField::MinimapThumbBorder => self.minimap_thumb_border,
-            ThemeColorField::EditorForeground => self.editor_foreground,
-            ThemeColorField::EditorBackground => self.editor_background,
-            ThemeColorField::EditorGutterBackground => self.editor_gutter_background,
-            ThemeColorField::EditorSubheaderBackground => self.editor_subheader_background,
-            ThemeColorField::EditorActiveLineBackground => self.editor_active_line_background,
-            ThemeColorField::EditorHighlightedLineBackground => {
-                self.editor_highlighted_line_background
-            }
-            ThemeColorField::EditorLineNumber => self.editor_line_number,
-            ThemeColorField::EditorActiveLineNumber => self.editor_active_line_number,
-            ThemeColorField::EditorInvisible => self.editor_invisible,
-            ThemeColorField::EditorWrapGuide => self.editor_wrap_guide,
-            ThemeColorField::EditorActiveWrapGuide => self.editor_active_wrap_guide,
-            ThemeColorField::EditorIndentGuide => self.editor_indent_guide,
-            ThemeColorField::EditorIndentGuideActive => self.editor_indent_guide_active,
-            ThemeColorField::EditorDocumentHighlightReadBackground => {
-                self.editor_document_highlight_read_background
-            }
-            ThemeColorField::EditorDocumentHighlightWriteBackground => {
-                self.editor_document_highlight_write_background
-            }
-            ThemeColorField::EditorDocumentHighlightBracketBackground => {
-                self.editor_document_highlight_bracket_background
-            }
-            ThemeColorField::TerminalBackground => self.terminal_background,
-            ThemeColorField::TerminalForeground => self.terminal_foreground,
-            ThemeColorField::TerminalBrightForeground => self.terminal_bright_foreground,
-            ThemeColorField::TerminalDimForeground => self.terminal_dim_foreground,
-            ThemeColorField::TerminalAnsiBackground => self.terminal_ansi_background,
-            ThemeColorField::TerminalAnsiBlack => self.terminal_ansi_black,
-            ThemeColorField::TerminalAnsiBrightBlack => self.terminal_ansi_bright_black,
-            ThemeColorField::TerminalAnsiDimBlack => self.terminal_ansi_dim_black,
-            ThemeColorField::TerminalAnsiRed => self.terminal_ansi_red,
-            ThemeColorField::TerminalAnsiBrightRed => self.terminal_ansi_bright_red,
-            ThemeColorField::TerminalAnsiDimRed => self.terminal_ansi_dim_red,
-            ThemeColorField::TerminalAnsiGreen => self.terminal_ansi_green,
-            ThemeColorField::TerminalAnsiBrightGreen => self.terminal_ansi_bright_green,
-            ThemeColorField::TerminalAnsiDimGreen => self.terminal_ansi_dim_green,
-            ThemeColorField::TerminalAnsiYellow => self.terminal_ansi_yellow,
-            ThemeColorField::TerminalAnsiBrightYellow => self.terminal_ansi_bright_yellow,
-            ThemeColorField::TerminalAnsiDimYellow => self.terminal_ansi_dim_yellow,
-            ThemeColorField::TerminalAnsiBlue => self.terminal_ansi_blue,
-            ThemeColorField::TerminalAnsiBrightBlue => self.terminal_ansi_bright_blue,
-            ThemeColorField::TerminalAnsiDimBlue => self.terminal_ansi_dim_blue,
-            ThemeColorField::TerminalAnsiMagenta => self.terminal_ansi_magenta,
-            ThemeColorField::TerminalAnsiBrightMagenta => self.terminal_ansi_bright_magenta,
-            ThemeColorField::TerminalAnsiDimMagenta => self.terminal_ansi_dim_magenta,
-            ThemeColorField::TerminalAnsiCyan => self.terminal_ansi_cyan,
-            ThemeColorField::TerminalAnsiBrightCyan => self.terminal_ansi_bright_cyan,
-            ThemeColorField::TerminalAnsiDimCyan => self.terminal_ansi_dim_cyan,
-            ThemeColorField::TerminalAnsiWhite => self.terminal_ansi_white,
-            ThemeColorField::TerminalAnsiBrightWhite => self.terminal_ansi_bright_white,
-            ThemeColorField::TerminalAnsiDimWhite => self.terminal_ansi_dim_white,
-            ThemeColorField::LinkTextHover => self.link_text_hover,
-            ThemeColorField::VersionControlAdded => self.version_control_added,
-            ThemeColorField::VersionControlDeleted => self.version_control_deleted,
-            ThemeColorField::VersionControlModified => self.version_control_modified,
-            ThemeColorField::VersionControlRenamed => self.version_control_renamed,
-            ThemeColorField::VersionControlConflict => self.version_control_conflict,
-            ThemeColorField::VersionControlIgnored => self.version_control_ignored,
-        }
-    }
-
     pub fn iter(&self) -> impl Iterator<Item = (ThemeColorField, Hsla)> + '_ {
-        ThemeColorField::iter().map(move |field| (field, self.color(field)))
+        ThemeColorField::iter().map(move |field| (field, *self.get_field_by_enum(field)))
     }
 
     pub fn to_vec(&self) -> Vec<(ThemeColorField, Hsla)> {
@@ -542,7 +310,7 @@ pub fn all_theme_colors(cx: &mut App) -> Vec<(Hsla, SharedString)> {
     let theme = cx.theme();
     ThemeColorField::iter()
         .map(|field| {
-            let color = theme.colors().color(field);
+            let color = *theme.colors().get_field_by_enum(field);
             let name = field.as_ref().to_string();
             (color, SharedString::from(name))
         })

crates/theme/src/styles/status.rs 🔗

@@ -2,11 +2,20 @@
 
 use gpui::Hsla;
 use refineable::Refineable;
+use strum::{AsRefStr, EnumIter};
+use util::FieldAccessByEnum;
 
 use crate::{blue, grass, neutral, red, yellow};
 
-#[derive(Refineable, Clone, Debug, PartialEq)]
+#[derive(Refineable, FieldAccessByEnum, Clone, Debug, PartialEq)]
 #[refineable(Debug, serde::Deserialize)]
+#[field_access_by_enum(
+    enum_name = "StatusColorField",
+    enum_attrs = [
+        derive(Debug, Clone, Copy, EnumIter, AsRefStr),
+        strum(serialize_all = "snake_case")
+    ]
+)]
 pub struct StatusColors {
     /// Indicates some kind of conflict, like a file changed on disk while it was open, or
     /// merge conflicts in a Git repository.

crates/util/Cargo.toml 🔗

@@ -13,7 +13,7 @@ path = "src/util.rs"
 doctest = true
 
 [features]
-test-support = ["git2", "rand", "util_macros"]
+test-support = ["git2", "rand"]
 
 [dependencies]
 anyhow.workspace = true
@@ -39,7 +39,7 @@ smol.workspace = true
 take-until.workspace = true
 tempfile.workspace = true
 unicase.workspace = true
-util_macros = { workspace = true, optional = true }
+util_macros = { workspace = true }
 walkdir.workspace = true
 workspace-hack.workspace = true
 
@@ -56,4 +56,3 @@ dunce = "1.0"
 git2.workspace = true
 indoc.workspace = true
 rand.workspace = true
-util_macros.workspace = true

crates/util/src/util.rs 🔗

@@ -31,9 +31,54 @@ use std::{
 use unicase::UniCase;
 
 pub use take_until::*;
+pub use util_macros::FieldAccessByEnum;
 #[cfg(any(test, feature = "test-support"))]
 pub use util_macros::{line_endings, path, uri};
 
+// todo! dedupe docs
+
+/// Trait for types that can access their fields via an enum.
+///
+/// This trait provides a way to dynamically access and modify fields of a struct
+/// using an enum that represents the field names. It's particularly useful for
+/// implementing generic field access patterns, such as theme color management
+/// or configuration field manipulation.
+///
+/// # Example
+///
+/// ```rust
+/// use util::FieldAccessByEnum;
+///
+/// #[derive(util_macros::FieldAccessByEnum)]
+/// #[field_access_by_enum(enum_name = "ColorField", field_type = "String")]
+/// struct Theme {
+///     background: String,
+///     foreground: String,
+///     border: String,
+/// }
+///
+/// let mut theme = Theme {
+///     background: "white".to_string(),
+///     foreground: "black".to_string(),
+///     border: "gray".to_string(),
+/// };
+///
+/// // Access field via enum
+/// let bg = theme.get(ColorField::Background);
+/// theme.set(ColorField::Foreground, "blue".to_string());
+/// ```
+pub trait FieldAccessByEnum {
+    /// The enum type representing the available fields
+    type Field;
+    /// The type of values stored in the fields
+    type FieldValue;
+
+    /// Get a reference to the value of the specified field
+    fn get_field_by_enum(&self, field: Self::Field) -> &Self::FieldValue;
+    /// Set the value of the specified field
+    fn set_field_by_enum(&mut self, field: Self::Field, value: Self::FieldValue);
+}
+
 #[macro_export]
 macro_rules! debug_panic {
     ( $($fmt_arg:tt)* ) => {

crates/util_macros/Cargo.toml 🔗

@@ -14,6 +14,8 @@ proc-macro = true
 doctest = false
 
 [dependencies]
+convert_case.workspace = true
 quote.workspace = true
 syn.workspace = true
 workspace-hack.workspace = true
+proc-macro2.workspace = true

crates/util_macros/src/util_macros.rs 🔗

@@ -1,8 +1,11 @@
-#![cfg_attr(not(target_os = "windows"), allow(unused))]
-
+use convert_case::{Case, Casing};
 use proc_macro::TokenStream;
-use quote::quote;
-use syn::{LitStr, parse_macro_input};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+    Data, DeriveInput, Expr, ExprArray, ExprLit, Fields, Lit, LitStr, MetaNameValue, Token,
+    parse_macro_input, punctuated::Punctuated,
+};
 
 /// A macro used in tests for cross-platform path string literals in tests. On Windows it replaces
 /// `/` with `\\` and adds `C:` to the beginning of absolute paths. On other platforms, the path is
@@ -87,3 +90,153 @@ pub fn line_endings(input: TokenStream) -> TokenStream {
         #text
     })
 }
+
+/// Derive macro that generates an enum and implements `FieldAccessByEnum`. Only works for structs
+/// with named fields where every field has the same type.
+///
+/// # Example
+///
+/// ```rust
+/// #[derive(FieldAccessByEnum)]
+/// #[field_access_by_enum(
+///     enum_name = "ColorField",
+///     enum_attrs = [
+///         derive(Debug, Clone, Copy, EnumIter, AsRefStr),
+///         strum(serialize_all = "snake_case")
+///     ],
+/// )]
+/// struct Theme {
+///     background: Hsla,
+///     foreground: Hsla,
+///     border_color: Hsla,
+/// }
+/// ```
+///
+/// This generates:
+/// ```rust
+/// #[derive(Debug, Clone, Copy, EnumIter, AsRefStr)]
+/// #[strum(serialize_all = "snake_case")]
+/// enum ColorField {
+///     Background,
+///     Foreground,
+///     BorderColor,
+/// }
+///
+/// impl FieldAccessByEnum for Theme {
+///     type Field = ColorField;
+///     type FieldValue = Hsla;
+///     // ... get and set methods
+/// }
+/// ```
+#[proc_macro_derive(FieldAccessByEnum, attributes(field_access_by_enum))]
+pub fn derive_field_access_by_enum(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as DeriveInput);
+
+    let struct_name = &input.ident;
+
+    let mut enum_name = None;
+    let mut enum_attrs: Vec<TokenStream2> = Vec::new();
+
+    for attr in &input.attrs {
+        if attr.path().is_ident("field_access_by_enum") {
+            let name_values: Punctuated<MetaNameValue, Token![,]> =
+                attr.parse_args_with(Punctuated::parse_terminated).unwrap();
+            for name_value in name_values {
+                if name_value.path.is_ident("enum_name") {
+                    let value = name_value.value;
+                    match value {
+                        Expr::Lit(ExprLit {
+                            lit: Lit::Str(name),
+                            ..
+                        }) => enum_name = Some(name.value()),
+                        _ => panic!("Expected string literal in enum_name attribute"),
+                    }
+                } else if name_value.path.is_ident("enum_attrs") {
+                    let value = name_value.value;
+                    match value {
+                        Expr::Array(ExprArray { elems, .. }) => {
+                            for elem in elems {
+                                enum_attrs.push(quote!(#[#elem]));
+                            }
+                        }
+                        _ => panic!("Expected array literal in enum_attr attribute"),
+                    }
+                }
+            }
+        }
+    }
+    let Some(enum_name) = enum_name else {
+        panic!("#[field_access_by_enum(enum_name = \"...\")] attribute is required");
+    };
+    let enum_ident = format_ident!("{}", enum_name);
+
+    let fields = match input.data {
+        Data::Struct(data_struct) => match data_struct.fields {
+            Fields::Named(fields) => fields.named,
+            _ => panic!("FieldAccessByEnum can only be derived for structs with named fields"),
+        },
+        _ => panic!("FieldAccessByEnum can only be derived for structs"),
+    };
+
+    if fields.is_empty() {
+        panic!("FieldAccessByEnum cannot be derived for structs with no fields");
+    }
+
+    let mut enum_variants = Vec::new();
+    let mut get_match_arms = Vec::new();
+    let mut set_match_arms = Vec::new();
+    let mut field_types = Vec::new();
+
+    for field in fields.iter() {
+        let field_name = field.ident.as_ref().unwrap();
+        let variant_name = field_name.to_string().to_case(Case::Pascal);
+        let variant_ident = format_ident!("{}", variant_name);
+        let field_type = &field.ty;
+
+        enum_variants.push(variant_ident.clone());
+        field_types.push(field_type);
+
+        get_match_arms.push(quote! {
+            #enum_ident::#variant_ident => &self.#field_name,
+        });
+
+        set_match_arms.push(quote! {
+            #enum_ident::#variant_ident => self.#field_name = value,
+        });
+    }
+
+    let first_type = &field_types[0];
+    let all_same_type = field_types
+        .iter()
+        .all(|ty| quote!(#ty).to_string() == quote!(#first_type).to_string());
+    if !all_same_type {
+        panic!("Fields have different types.");
+    }
+    let field_value_type = quote! { #first_type };
+
+    let expanded = quote! {
+        #(#enum_attrs)*
+        pub enum #enum_ident {
+            #(#enum_variants),*
+        }
+
+        impl util::FieldAccessByEnum for #struct_name {
+            type Field = #enum_ident;
+            type FieldValue = #field_value_type;
+
+            fn get_field_by_enum(&self, field: Self::Field) -> &Self::FieldValue {
+                match field {
+                    #(#get_match_arms)*
+                }
+            }
+
+            fn set_field_by_enum(&mut self, field: Self::Field, value: Self::FieldValue) {
+                match field {
+                    #(#set_match_arms)*
+                }
+            }
+        }
+    };
+
+    TokenStream::from(expanded)
+}