refineable.rs

  1pub use derive_refineable::Refineable;
  2
  3/// A trait for types that can be refined with partial updates.
  4///
  5/// The `Refineable` trait enables hierarchical configuration patterns where a base configuration
  6/// can be selectively overridden by refinements. This is particularly useful for styling and
  7/// settings, and theme hierarchies.
  8///
  9/// # Derive Macro
 10///
 11/// The `#[derive(Refineable)]` macro automatically generates a companion refinement type and
 12/// implements this trait. For a struct `Style`, it creates `StyleRefinement` where each field is
 13/// wrapped appropriately:
 14///
 15/// - **Refineable fields** (marked with `#[refineable]`): Become the corresponding refinement type
 16///   (e.g., `Bar` becomes `BarRefinement`)
 17/// - **Optional fields** (`Option<T>`): Remain as `Option<T>`
 18/// - **Regular fields**: Become `Option<T>`
 19///
 20/// ## Example
 21///
 22/// ```rust
 23/// #[derive(Refineable, Clone, Default)]
 24/// struct Example {
 25///     color: String,
 26///     font_size: Option<u32>,
 27///     #[refineable]
 28///     margin: Margin,
 29/// }
 30///
 31/// #[derive(Refineable, Clone, Default)]
 32/// struct Margin {
 33///     top: u32,
 34///     left: u32,
 35/// }
 36///
 37///
 38/// fn example() {
 39///     let mut example = Example::default();
 40///     let refinement = ExampleRefinement {
 41///         color: Some("red".to_string()),
 42///         font_size: None,
 43///         margin: MarginRefinement {
 44///             top: Some(10),
 45///             left: None,
 46///         },
 47///     };
 48///
 49///     base_style.refine(&refinement);
 50/// }
 51/// ```
 52///
 53/// This generates `ExampleRefinement` with:
 54/// - `color: Option<String>`
 55/// - `font_size: Option<u32>` (unchanged)
 56/// - `margin: MarginRefinement`
 57///
 58/// ## Attributes
 59///
 60/// The derive macro supports these attributes on the struct:
 61/// - `#[refineable(Debug)]`: Implements `Debug` for the refinement type
 62/// - `#[refineable(Serialize)]`: Derives `Serialize` which skips serializing `None`
 63/// - `#[refineable(OtherTrait)]`: Derives additional traits on the refinement type
 64///
 65/// Fields can be marked with:
 66/// - `#[refineable]`: Field is itself refineable (uses nested refinement type)
 67pub trait Refineable: Clone {
 68    type Refinement: Refineable<Refinement = Self::Refinement> + IsEmpty + Default;
 69
 70    /// Applies the given refinement to this instance, modifying it in place.
 71    ///
 72    /// Only non-empty values in the refinement are applied.
 73    ///
 74    /// * For refineable fields, this recursively calls `refine`.
 75    /// * For other fields, the value is replaced if present in the refinement.
 76    fn refine(&mut self, refinement: &Self::Refinement);
 77
 78    /// Returns a new instance with the refinement applied, equivalent to cloning `self` and calling
 79    /// `refine` on it.
 80    fn refined(self, refinement: Self::Refinement) -> Self;
 81
 82    /// Creates an instance from a cascade by merging all refinements atop the default value.
 83    fn from_cascade(cascade: &Cascade<Self>) -> Self
 84    where
 85        Self: Default + Sized,
 86    {
 87        Self::default().refined(cascade.merged())
 88    }
 89
 90    /// Returns `true` if this instance would contain all values from the refinement.
 91    ///
 92    /// For refineable fields, this recursively checks `is_superset_of`. For other fields, this
 93    /// checks if the refinement's `Some` values match this instance's values.
 94    fn is_superset_of(&self, refinement: &Self::Refinement) -> bool;
 95
 96    /// Returns a refinement that represents the difference between this instance and the given
 97    /// refinement.
 98    ///
 99    /// For refineable fields, this recursively calls `subtract`. For other fields, the field is
100    /// `None` if the field's value is equal to the refinement.
101    fn subtract(&self, refinement: &Self::Refinement) -> Self::Refinement;
102}
103
104pub trait IsEmpty {
105    /// Returns `true` if applying this refinement would have no effect.
106    fn is_empty(&self) -> bool;
107}
108
109/// A cascade of refinements that can be merged in priority order.
110///
111/// A cascade maintains a sequence of optional refinements where later entries
112/// take precedence over earlier ones. The first slot (index 0) is always the
113/// base refinement and is guaranteed to be present.
114///
115/// This is useful for implementing configuration hierarchies like CSS cascading,
116/// where styles from different sources (user agent, user, author) are combined
117/// with specific precedence rules.
118pub struct Cascade<S: Refineable>(Vec<Option<S::Refinement>>);
119
120impl<S: Refineable + Default> Default for Cascade<S> {
121    fn default() -> Self {
122        Self(vec![Some(Default::default())])
123    }
124}
125
126/// A handle to a specific slot in a cascade.
127///
128/// Slots are used to identify specific positions in the cascade where
129/// refinements can be set or updated.
130#[derive(Copy, Clone)]
131pub struct CascadeSlot(usize);
132
133impl<S: Refineable + Default> Cascade<S> {
134    /// Reserves a new slot in the cascade and returns a handle to it.
135    ///
136    /// The new slot is initially empty (`None`) and can be populated later
137    /// using `set()`.
138    pub fn reserve(&mut self) -> CascadeSlot {
139        self.0.push(None);
140        CascadeSlot(self.0.len() - 1)
141    }
142
143    /// Returns a mutable reference to the base refinement (slot 0).
144    ///
145    /// The base refinement is always present and serves as the foundation
146    /// for the cascade.
147    pub fn base(&mut self) -> &mut S::Refinement {
148        self.0[0].as_mut().unwrap()
149    }
150
151    /// Sets the refinement for a specific slot in the cascade.
152    ///
153    /// Setting a slot to `None` effectively removes it from consideration
154    /// during merging.
155    pub fn set(&mut self, slot: CascadeSlot, refinement: Option<S::Refinement>) {
156        self.0[slot.0] = refinement
157    }
158
159    /// Merges all refinements in the cascade into a single refinement.
160    ///
161    /// Refinements are applied in order, with later slots taking precedence.
162    /// Empty slots (`None`) are skipped during merging.
163    pub fn merged(&self) -> S::Refinement {
164        let mut merged = self.0[0].clone().unwrap();
165        for refinement in self.0.iter().skip(1).flatten() {
166            merged.refine(refinement);
167        }
168        merged
169    }
170}