use std::error::Error;
use std::fmt;
use crate::gauge::Gauge;
use crate::kit::OCTANTS;
use crate::stage::Shader;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Glyphs(u8);
impl Glyphs {
pub const BASE: Self = Self(0);
pub const OCTANTS: Self = Self(1);
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn contains(self, other: Self) -> bool {
other.0 & !self.0 == 0
}
const fn without(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
pub fn octant(pixels: u8) -> char {
OCTANTS[usize::from(pixels)]
}
}
const REPERTOIRES: [(Glyphs, &str); 1] = [(Glyphs::OCTANTS, "octants")];
#[cfg(test)]
pub(crate) fn repertoire(glyph: char) -> Glyphs {
if ('\u{1cc00}'..='\u{1cebf}').contains(&glyph) || ('\u{1fb00}'..='\u{1fbff}').contains(&glyph)
{
Glyphs::OCTANTS
} else {
Glyphs::BASE
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Unfit {
missing: Glyphs,
narrow: bool,
}
impl Unfit {
pub fn missing(self) -> Glyphs {
self.missing
}
pub fn narrow(self) -> bool {
self.narrow
}
}
impl fmt::Display for Unfit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut clauses = REPERTOIRES
.iter()
.filter(|(repertoire, _)| self.missing.contains(*repertoire))
.map(|(_, name)| format!("the terminal lacks {name}"))
.chain(
self.narrow
.then(|| "the recipe paints only narrow graphemes".to_owned()),
);
if let Some(first) = clauses.next() {
f.write_str(&first)?;
}
clauses.try_for_each(|clause| write!(f, "; {clause}"))
}
}
impl Error for Unfit {}
pub struct Recipe {
pub(crate) name: &'static str,
pub(crate) blurb: &'static str,
pub(crate) credit: &'static str,
pub(crate) needs: Glyphs,
pub(crate) wide: bool,
pub(crate) summon: fn(u64) -> Box<dyn Shader>,
}
impl Recipe {
pub fn name(&self) -> &'static str {
self.name
}
pub fn blurb(&self) -> &'static str {
self.blurb
}
pub fn credit(&self) -> &'static str {
self.credit
}
pub fn needs(&self) -> Glyphs {
self.needs
}
pub fn wide(&self) -> bool {
self.wide
}
pub fn fits<G: Gauge>(&self, glyphs: Glyphs) -> Result<(), Unfit> {
let unfit = Unfit {
missing: self.needs.without(glyphs),
narrow: G::WIDE && !self.wide,
};
if unfit.missing == Glyphs::BASE && !unfit.narrow {
Ok(())
} else {
Err(unfit)
}
}
}
impl fmt::Debug for Recipe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Recipe")
.field("name", &self.name)
.field("needs", &self.needs)
.field("wide", &self.wide)
.finish_non_exhaustive()
}
}