Cleanup FieldAccessByEnum

Michael Sloan created

Change summary

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(-)

Detailed changes

crates/theme/src/theme.rs 🔗

@@ -296,7 +296,7 @@ pub fn refine_theme_family(theme_family_content: ThemeFamilyContent) -> ThemeFam
 fn make_colors_unique<T, F>(colors: &mut T, color_to_count: &mut collections::HashMap<Hsla, u32>)
 where
     F: Copy + strum::IntoEnumIterator,
-    T: util::FieldAccessByEnum<Field = F, FieldValue = Hsla>,
+    T: util::FieldAccessByEnum<Hsla, Field = F>,
 {
     for field in F::iter() {
         let mut color = *colors.get_field_by_enum(field);

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<T>(option: Option<T>) -> Option<T> {
     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<Hsla> for Theme {
+///     type Field = ColorField;
+///     // ... get and set methods
+/// }
+/// ```
+pub trait FieldAccessByEnum<V> {
+    /// 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::*;

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)*
                 }