#![forbid(unsafe_code)]
pub trait Intent {
fn token(self) -> &'static str;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Bevel {
Raised,
Inset,
}
impl Bevel {
#[must_use]
pub const fn edges(self) -> (Edge, Edge) {
match self {
Self::Raised => (Edge::Light, Edge::Dark),
Self::Inset => (Edge::Dark, Edge::Light),
}
}
#[must_use]
pub const fn pressed(self) -> Self {
match self {
Self::Raised => Self::Inset,
Self::Inset => Self::Raised,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Edge {
Light,
Dark,
}
impl Intent for Edge {
fn token(self) -> &'static str {
match self {
Self::Light => "bevel-light",
Self::Dark => "bevel-dark",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Fill {
Page,
Raised,
Overlay,
Well,
}
impl Intent for Fill {
fn token(self) -> &'static str {
match self {
Self::Page => "surface-page",
Self::Raised => "surface-raised",
Self::Overlay => "surface-overlay",
Self::Well => "surface-well",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Depth {
Flat,
Raised,
Well,
}
impl Depth {
#[must_use]
pub const fn bevel(self) -> Option<Bevel> {
match self {
Self::Flat => None,
Self::Raised => Some(Bevel::Raised),
Self::Well => Some(Bevel::Inset),
}
}
#[must_use]
pub const fn fill(self) -> Option<Fill> {
match self {
Self::Flat => None,
Self::Raised => Some(Fill::Raised),
Self::Well => Some(Fill::Well),
}
}
#[must_use]
pub const fn pressed(self) -> Self {
match self {
Self::Raised => Self::Well,
other => other,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inset_is_raised_with_the_light_moved() {
let (rl, rd) = Bevel::Raised.edges();
let (il, id) = Bevel::Inset.edges();
assert_eq!((rl, rd), (Edge::Light, Edge::Dark));
assert_eq!((il, id), (rd, rl));
}
#[test]
fn pressing_twice_is_a_no_op() {
for b in [Bevel::Raised, Bevel::Inset] {
assert_eq!(b.pressed().pressed(), b);
}
}
#[test]
fn a_raised_region_is_never_filled_with_a_recessed_surface() {
assert_eq!(Depth::Raised.fill(), Some(Fill::Raised));
assert_eq!(Depth::Raised.bevel(), Some(Bevel::Raised));
assert_eq!(Depth::Well.bevel(), Some(Bevel::Inset));
assert_ne!(Depth::Well.fill(), Depth::Raised.fill());
}
#[test]
fn flat_has_neither_edge_nor_fill() {
assert_eq!(Depth::Flat.bevel(), None);
assert_eq!(Depth::Flat.fill(), None);
}
#[test]
fn pressing_a_card_makes_a_well() {
assert_eq!(Depth::Raised.pressed(), Depth::Well);
assert_eq!(
Depth::Raised.pressed().bevel(),
Depth::Raised.bevel().map(Bevel::pressed)
);
assert_eq!(Depth::Flat.pressed(), Depth::Flat);
assert_eq!(Depth::Well.pressed(), Depth::Well);
}
#[test]
fn intents_name_makeover_tokens_and_nothing_else() {
assert_eq!(Edge::Light.token(), "bevel-light");
assert_eq!(Edge::Dark.token(), "bevel-dark");
assert_eq!(Fill::Raised.token(), "surface-raised");
assert_eq!(Fill::Well.token(), "surface-well");
for t in [Edge::Light.token(), Edge::Dark.token()] {
assert!(!t.starts_with('#'), "{t} looks like a value");
}
}
}