1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
use std::any::Any;
use std::fmt::Debug;
use std::panic::{RefUnwindSafe, UnwindSafe};
use crate::{Identifier, Name};
/// A style component. Implementors can be stored within
/// [`Style`](crate::Style).
///
/// # Deriving this trait
///
/// This trait can be derived. It can be customized using the `style` attribute
/// with these parameters:
///
/// - `inherited`: A boolean value, `false` by default.
/// - `name`: An identifier. By default, the type's name converted to snake case
/// is used. For example, a type named `StyleComponent` would return
/// `Name::new(StyleComponent::authority(), "style_component")`.
/// - `authority`: An identifier. By default, this is [`Identifier::private()`].
/// - `merge`: An expression to evaluate when merging. `self` and `other` are
/// defined. By default, components do not merge.
pub trait StyleComponent: Any + RefUnwindSafe + UnwindSafe + Send + Sync + Debug + 'static {
/// The unique name of this style component.
///
/// This function returns a qualified name. The default implementation uses
/// the type's name converted to snake case. E.g., `StyleCompoment` becomes
/// `style_component`, and [`StyleComponent::authority()`] for the
/// authority.
#[must_use]
fn name() -> Name {
let type_name = std::any::type_name::<Self>();
let Some((_, name)) = type_name.rsplit_once("::") else { unreachable!("Invalid type name") };
Name::new(
Self::authority(),
stylecs_shared::pascal_case_to_snake_case(name.to_string())
.expect("struct name contains invalid characters"),
)
.expect("already validated")
}
/// Returns the authority of this component. By default, this returns
/// [`Identifier::private()`].
#[must_use]
fn authority() -> Identifier {
Identifier::private()
}
/// Returns whether the component should be inherited. Affects the behavior
/// of [`Style::inherited_from`](crate::Style::inherited_from)
///
/// This provided imiplementation returns `false`.
#[must_use]
fn inherited() -> bool {
false
}
/// Merges `self` with `other`, if it makes sense to do so for this type.
/// The default implementation does nothing, preserving the `self` value.
#[allow(unused_variables)]
fn merge(&mut self, other: &Self) {}
}
/// A style component that can be powered by data contained in the structure.
///
/// This trait allows style components to be defined that didn't originate from
/// Rust code -- e.g., a scripting language.
pub trait DynamicComponent:
Any + Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static
{
/// The unique name of this style component.
///
/// Each name component must be a valid identifier: `a-z`, `A-Z`, or `_`.
///
/// The [`Name`] is namespaced. If this trait is implemented for a type that
/// is distributed as part of a crate, the implementation should use a
/// unique [authority](Name::authority) based on the crate it comes from.
fn name(&self) -> Name;
/// Returns whether the component should be inherited. Affects the behavior
/// of [`Style::inherited_from`](crate::Style::inherited_from)
#[must_use]
fn inherited(&self) -> bool {
false
}
/// Merges `self` with `other`, if it makes sense to do so for this type.
/// The default implementation does not do anything, preserving the value in
/// self.
#[allow(unused_variables)]
fn merge(&mut self, other: &Self) {}
}
impl<T> DynamicComponent for T
where
T: StyleComponent,
{
fn name(&self) -> Name {
T::name()
}
fn inherited(&self) -> bool {
T::inherited()
}
fn merge(&mut self, other: &Self) {
<T as StyleComponent>::merge(self, other);
}
}