use std::fmt::Write as _;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum Chrome {
Bullet,
Gutter,
Fill,
Edge,
Shadow,
Padding,
Type,
Pointing,
}
const ORDER: [(Chrome, &str); 8] = [
(Chrome::Bullet, "list-style: none"),
(Chrome::Gutter, "margin: 0"),
(Chrome::Fill, "background: none"),
(Chrome::Edge, "border: none"),
(Chrome::Shadow, "box-shadow: none"),
(Chrome::Padding, "padding: 0"),
(Chrome::Type, "font: inherit"),
(Chrome::Pointing, "cursor: pointer"),
];
const fn bit(chrome: Chrome) -> u8 {
match chrome {
Chrome::Bullet => 1 << 0,
Chrome::Gutter => 1 << 1,
Chrome::Fill => 1 << 2,
Chrome::Edge => 1 << 3,
Chrome::Shadow => 1 << 4,
Chrome::Padding => 1 << 5,
Chrome::Type => 1 << 6,
Chrome::Pointing => 1 << 7,
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Reset(u8);
impl Reset {
pub const NOTHING: Self = Self(0);
pub const BULLETS: Self = Self::NOTHING
.and(Chrome::Bullet)
.and(Chrome::Gutter)
.and(Chrome::Padding);
pub const FLAT_BUTTON: Self = Self::NOTHING
.and(Chrome::Fill)
.and(Chrome::Edge)
.and(Chrome::Shadow);
pub const TEXT_BUTTON: Self = Self::NOTHING
.and(Chrome::Fill)
.and(Chrome::Edge)
.and(Chrome::Padding)
.and(Chrome::Type)
.and(Chrome::Pointing);
#[must_use]
pub const fn and(self, chrome: Chrome) -> Self {
Self(self.0 | bit(chrome))
}
#[must_use]
pub const fn carries(self, chrome: Chrome) -> bool {
self.0 & bit(chrome) != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[must_use]
pub fn declarations(self) -> String {
let mut css = String::new();
for (chrome, declaration) in ORDER {
if self.carries(chrome) {
let _ = writeln!(css, " {declaration};");
}
}
css
}
#[must_use]
pub fn rule(self, selector: &str) -> String {
if self.is_empty() {
return String::new();
}
format!("{selector} {{\n{}}}\n", self.declarations())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nothing_emits_nothing() {
assert!(Reset::NOTHING.is_empty());
assert_eq!(Reset::NOTHING.rule(".x"), "");
}
#[test]
fn the_selector_is_verbatim() {
assert!(
Reset::TEXT_BUTTON
.rule("button.link")
.starts_with("button.link {")
);
}
#[test]
fn the_named_sets_emit_what_they_replaced() {
assert_eq!(
Reset::BULLETS.rule(".list"),
".list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n"
);
assert_eq!(
Reset::TEXT_BUTTON.rule("button.link"),
"button.link {\n background: none;\n border: none;\n \
padding: 0;\n font: inherit;\n cursor: pointer;\n}\n"
);
assert_eq!(
Reset::FLAT_BUTTON.rule(".facet-take"),
".facet-take {\n background: none;\n border: none;\n \
box-shadow: none;\n}\n"
);
}
#[test]
fn order_is_the_emitters() {
let forwards = Reset::NOTHING.and(Chrome::Fill).and(Chrome::Bullet);
let backwards = Reset::NOTHING.and(Chrome::Bullet).and(Chrome::Fill);
assert_eq!(forwards, backwards);
assert_eq!(
forwards.declarations(),
" list-style: none;\n background: none;\n"
);
}
#[test]
fn every_member_has_a_declaration() {
for (chrome, _) in ORDER {
assert!(Reset::NOTHING.and(chrome).carries(chrome), "{chrome:?}");
}
let all = ORDER
.iter()
.fold(Reset::NOTHING, |set, (chrome, _)| set.and(*chrome));
assert_eq!(all.0, u8::MAX);
assert_eq!(all.declarations().lines().count(), ORDER.len());
}
}