From d4bee57aacb68bb195b3a4a8fb78f5bf6d5ea826 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sun, 21 Sep 2025 18:11:53 -0600 Subject: [PATCH] Cleanup FieldAccessByEnum --- crates/theme/src/theme.rs | 2 +- crates/util/src/util.rs | 91 ++++++++++++++------------- crates/util_macros/src/util_macros.rs | 50 +++------------ 3 files changed, 57 insertions(+), 86 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index af422643782c97353033a68089a5d81f84e304b8..9304e493ac601156c0764e4596e0c878404b4481 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -296,7 +296,7 @@ pub fn refine_theme_family(theme_family_content: ThemeFamilyContent) -> ThemeFam fn make_colors_unique(colors: &mut T, color_to_count: &mut collections::HashMap) where F: Copy + strum::IntoEnumIterator, - T: util::FieldAccessByEnum, + T: util::FieldAccessByEnum, { for field in F::iter() { let mut color = *colors.get_field_by_enum(field); diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 15393d76f941d57458655956f3883871c949dbbc..981b8a7d8a6461071a77b615977b8d96c58045a4 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -35,50 +35,6 @@ 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)* ) => { @@ -1149,6 +1105,53 @@ pub fn some_or_debug_panic(option: Option) -> Option { option } +/// Trait that provides field access specified by enum. +/// +/// The derive macro only supports structs with named fields that all have 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; +/// // ... get and set methods +/// } +/// ``` +pub trait FieldAccessByEnum { + /// The enum type representing the available fields + type Field; + + /// Get a reference to the value of the specified field + fn get_field_by_enum(&self, field: Self::Field) -> &V; + /// Set the value of the specified field + fn set_field_by_enum(&mut self, field: Self::Field, value: V); +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/util_macros/src/util_macros.rs b/crates/util_macros/src/util_macros.rs index 11bdfded7c8cad9655da192dea72fe4c07e7aa2c..f79b1d6b550fd232759d3f596f8ae915cbc2c9c1 100644 --- a/crates/util_macros/src/util_macros.rs +++ b/crates/util_macros/src/util_macros.rs @@ -91,43 +91,6 @@ pub fn line_endings(input: TokenStream) -> TokenStream { }) } -/// 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); @@ -161,6 +124,12 @@ pub fn derive_field_access_by_enum(input: TokenStream) -> TokenStream { } _ => panic!("Expected array literal in enum_attr attribute"), } + } else { + if let Some(ident) = name_value.path.get_ident() { + panic!("Unrecognized argument name {}", ident); + } else { + panic!("Unrecognized argument {:?}", name_value.path); + } } } } @@ -220,17 +189,16 @@ pub fn derive_field_access_by_enum(input: TokenStream) -> TokenStream { #(#enum_variants),* } - impl util::FieldAccessByEnum for #struct_name { + impl util::FieldAccessByEnum<#field_value_type> for #struct_name { type Field = #enum_ident; - type FieldValue = #field_value_type; - fn get_field_by_enum(&self, field: Self::Field) -> &Self::FieldValue { + fn get_field_by_enum(&self, field: Self::Field) -> &#field_value_type { match field { #(#get_match_arms)* } } - fn set_field_by_enum(&mut self, field: Self::Field, value: Self::FieldValue) { + fn set_field_by_enum(&mut self, field: Self::Field, value: #field_value_type) { match field { #(#set_match_arms)* }