use std::{fmt::Display, str::FromStr};
#[derive(Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct Family {
pub name: String,
pub style: Style,
pub weight: Weight,
pub stretch: Stretch,
}
impl Family {
pub fn name(self, name: impl Into<String>) -> Self {
Self { name: name.into(), ..self }
}
pub fn style(self, style: Style) -> Self {
Self { style, ..self }
}
pub fn weight(self, weight: Weight) -> Self {
Self { weight, ..self }
}
pub fn stretch(self, stretch: Stretch) -> Self {
Self { stretch, ..self }
}
}
impl<T: Into<String>> From<T> for Family {
fn from(value: T) -> Self {
Self {
name: value.into(),
style: Default::default(),
weight: Default::default(),
stretch: Default::default(),
}
}
}
impl Display for Family {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{};{};{};{}", self.name, self.weight, self.style, self.stretch)
}
}
impl FromStr for Family {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lines = s.split(";");
Ok(Self {
name: lines.next().unwrap_or_default().parse().unwrap_or_default(),
weight: lines.next().unwrap_or_default().parse().unwrap_or_default(),
style: lines.next().unwrap_or_default().parse().unwrap_or_default(),
stretch: lines.next().unwrap_or_default().parse().unwrap_or_default(),
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Style {
Normal,
Italic,
Oblique,
}
impl Default for Style {
#[inline]
fn default() -> Style {
Style::Normal
}
}
impl FromStr for Style {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.trim() {
"normal" => Self::Normal,
"italic" => Self::Italic,
"oblique" => Self::Oblique,
_ => return Err("fail"),
})
}
}
impl Display for Style {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::Normal => "normal",
Self::Italic => "italic",
Self::Oblique => "oblique",
};
write!(f, "{}", name)
}
}
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
pub struct Weight(pub u16);
impl Weight {
pub const THIN: Weight = Weight(100);
pub const EXTRA_LIGHT: Weight = Weight(200);
pub const LIGHT: Weight = Weight(300);
pub const NORMAL: Weight = Weight(400);
pub const MEDIUM: Weight = Weight(500);
pub const SEMIBOLD: Weight = Weight(600);
pub const BOLD: Weight = Weight(700);
pub const EXTRA_BOLD: Weight = Weight(800);
pub const BLACK: Weight = Weight(900);
}
impl Default for Weight {
#[inline]
fn default() -> Weight {
Weight::NORMAL
}
}
impl FromStr for Weight {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.trim() {
"thin" => Self::THIN,
"extra-light" => Self::EXTRA_LIGHT,
"light" => Self::LIGHT,
"normal" => Self::NORMAL,
"medium" => Self::MEDIUM,
"semibold" => Self::SEMIBOLD,
"bold" => Self::BOLD,
"extra-bold" => Self::EXTRA_BOLD,
"black" => Self::BLACK,
_ => return Err("fail"),
})
}
}
impl Display for Weight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match *self {
Self::THIN => "thin",
Self::EXTRA_LIGHT => "extra-light",
Self::LIGHT => "light",
Self::NORMAL => "normal",
Self::MEDIUM => "medium",
Self::SEMIBOLD => "semibold",
Self::BOLD => "bold",
Self::EXTRA_BOLD => "extra-bold",
Self::BLACK => "black",
_ => "unknown",
};
write!(f, "{}", name)
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Stretch(pub u16);
impl Stretch {
pub const ULTRA_CONDENSED: Stretch = Stretch(500);
pub const EXTRA_CONDENSED: Stretch = Stretch(625);
pub const CONDENSED: Stretch = Stretch(750);
pub const SEMI_CONDENSED: Stretch = Stretch(875);
pub const NORMAL: Stretch = Stretch(1000);
pub const SEMI_EXPANDED: Stretch = Stretch(1125);
pub const EXPANDED: Stretch = Stretch(1250);
pub const EXTRA_EXPANDED: Stretch = Stretch(1500);
pub const ULTRA_EXPANDED: Stretch = Stretch(2000);
}
impl From<f32> for Stretch {
fn from(value: f32) -> Self {
Self((value * 1000.0) as u16)
}
}
impl From<Stretch> for f32 {
fn from(value: Stretch) -> Self {
value.0 as f32 / 1000.0
}
}
impl Default for Stretch {
#[inline]
fn default() -> Self {
Stretch::NORMAL
}
}
impl FromStr for Stretch {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.trim() {
"ultra-condensed" => Self::ULTRA_CONDENSED,
"extra-condensed" => Self::EXTRA_CONDENSED,
"condensed" => Self::CONDENSED,
"semi-condensed" => Self::SEMI_CONDENSED,
"normal" => Self::NORMAL,
"semi-expanded" => Self::SEMI_EXPANDED,
"expanded" => Self::EXPANDED,
"extra-expanded" => Self::EXTRA_EXPANDED,
"ultra-expanded" => Self::ULTRA_EXPANDED,
_ => return Err("fail"),
})
}
}
impl Display for Stretch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match *self {
Self::ULTRA_CONDENSED => "ultra-condensed",
Self::EXTRA_CONDENSED => "extra-condensed",
Self::CONDENSED => "condensed",
Self::SEMI_CONDENSED => "semi-condensed",
Self::NORMAL => "normal",
Self::SEMI_EXPANDED => "semi-condensed",
Self::EXPANDED => "expanded",
Self::EXTRA_EXPANDED => "extra-condensed",
Self::ULTRA_EXPANDED => "ultra-condensed",
_ => "unknown",
};
write!(f, "{}", name)
}
}