use std::fmt;
use std::num::NonZero;
use flagset::{FlagSet, flags};
use crate::arguments::{ArgumentScanner, ExpectArg, FromArgs};
use crate::color::RgbColor;
use crate::parse::UnrecognizedVariant;
flags! {
pub enum FontStyle: u8 {
Blink,
Bold,
Italic,
Underline,
Inverse,
}
}
impl_parse_enum!(FontStyle, Blink, Bold, Italic, Underline, Inverse);
impl fmt::Display for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Font<S = String> {
pub face: Option<S>,
pub size: Option<NonZero<u8>>,
pub color: Option<RgbColor>,
pub style: FlagSet<FontStyle>,
pub back: Option<RgbColor>,
}
impl<S> Font<S> {
pub fn map_text<T, F>(self, mut f: F) -> Font<T>
where
F: FnMut(S) -> T,
{
Font {
face: self.face.map(&mut f),
size: self.size,
color: self.color,
style: self.style,
back: self.back,
}
}
}
impl_into_owned!(Font);
impl<S: AsRef<str>> Font<S> {
pub fn borrow_text(&self) -> Font<&str> {
Font {
face: self.face.as_ref().map(AsRef::as_ref),
size: self.size,
color: self.color,
style: self.style,
back: self.back,
}
}
}
impl_partial_eq!(Font);
impl<'a, S: AsRef<str>> FromArgs<'a, S> for Font<S> {
fn from_args<A: ArgumentScanner<'a, Decoded = S>>(mut scanner: A) -> crate::Result<Self> {
let face = scanner.get_next_or("face")?;
let size = scanner
.get_next_or("size")?
.expect_number()?
.and_then(NonZero::new);
let fore = scanner.get_next_or("color")?;
let back = scanner.get_next_or("back")?.expect_color()?;
let mut color: Option<RgbColor> = None;
let mut style: FlagSet<FontStyle> = FlagSet::empty();
if let Some(fore) = fore {
for effect in fore.as_ref().split(',') {
let effect = effect.trim_ascii();
if let Ok(flag) = effect.parse::<FontStyle>() {
style |= flag;
} else if let Some(rgb) = RgbColor::named(effect) {
color = Some(rgb);
}
}
}
scanner.expect_end()?;
Ok(Self {
face,
size,
color,
style,
back,
})
}
}
impl_from_str!(Font);
impl<S: AsRef<str>> fmt::Display for Font<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Font {
face,
size,
color,
style,
back,
} = self.borrow_text();
crate::display::ElementFormatter {
name: "FONT",
arguments: &[&face, &size, &(color, style), &back],
keywords: &[],
}
.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_fg_color() {
let el = "<font color=\"a,bold,b,blink,black,c\">"
.parse::<Font>()
.expect("parse error");
assert_eq!(el.color, Some(RgbColor::BLACK));
assert_eq!(el.style, FontStyle::Blink | FontStyle::Bold);
}
}