use serde_derive::{Deserialize, Serialize};
use std::{fmt, ops};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Style {
pub foreground: Color,
pub background: Color,
pub font_style: FontStyle,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct StyleModifier {
pub foreground: Option<Color>,
pub background: Option<Color>,
pub font_style: Option<FontStyle>,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl std::fmt::Debug for Color {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Color { r, g, b, a } = self;
if f.alternate() {
write!(
f,
"Color {{ r/g/b/a: {: >3}/{: >3}/{: >3}/{: >3} }}",
r, g, b, a
)
} else {
write!(f, "Color {{ r/g/b/a: {}/{}/{}/{} }}", r, g, b, a)
}
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct FontStyle {
bits: u8,
}
impl FontStyle {
pub const BOLD: Self = Self { bits: 1 };
pub const UNDERLINE: Self = Self { bits: 2 };
pub const ITALIC: Self = Self { bits: 4 };
pub const fn empty() -> Self {
Self { bits: 0 }
}
pub const fn all() -> Self {
let bits = Self::BOLD.bits | Self::UNDERLINE.bits | Self::ITALIC.bits;
Self { bits }
}
pub const fn bits(&self) -> u8 {
self.bits
}
pub const fn from_bits(bits: u8) -> Option<Self> {
if (bits & !Self::all().bits()) == 0 {
Some(Self { bits })
} else {
None
}
}
pub const fn from_bits_truncate(bits: u8) -> Self {
let bits = bits & Self::all().bits;
Self { bits }
}
pub const unsafe fn from_bits_unchecked(bits: u8) -> Self {
Self { bits }
}
pub const fn is_empty(&self) -> bool {
self.bits() == Self::empty().bits()
}
pub const fn is_all(&self) -> bool {
self.bits() == Self::all().bits()
}
pub const fn intersects(&self, other: Self) -> bool {
let bits = self.bits & other.bits;
!(Self { bits }).is_empty()
}
pub const fn contains(&self, other: Self) -> bool {
(self.bits & other.bits) == other.bits
}
pub fn insert(&mut self, other: Self) {
self.bits |= other.bits;
}
pub fn remove(&mut self, other: Self) {
self.bits &= !other.bits;
}
pub fn toggle(&mut self, other: Self) {
self.bits ^= other.bits;
}
pub fn set(&mut self, other: Self, value: bool) {
if value {
self.insert(other);
} else {
self.remove(other);
}
}
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
let bits = self.bits & other.bits;
Self { bits }
}
#[must_use]
pub const fn union(self, other: Self) -> Self {
let bits = self.bits | other.bits;
Self { bits }
}
pub const fn difference(self, other: Self) -> Self {
let bits = self.bits & !other.bits;
Self { bits }
}
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
let bits = self.bits ^ other.bits;
Self { bits }
}
#[must_use]
pub const fn complement(self) -> Self {
Self::from_bits_truncate(!self.bits)
}
}
impl fmt::Debug for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut empty = true;
let pairs = [
(Self::BOLD, "BOLD"),
(Self::UNDERLINE, "UNDERLINE"),
(Self::ITALIC, "ITALIC"),
];
for (flag, flag_str) in pairs {
if self.contains(flag) {
if !std::mem::take(&mut empty) {
f.write_str(" | ")?;
}
f.write_str(flag_str)?;
}
}
let extra_bits = self.bits & !Self::all().bits();
if extra_bits != 0 {
if !std::mem::take(&mut empty) {
f.write_str(" | ")?;
}
f.write_str("0x")?;
fmt::LowerHex::fmt(&extra_bits, f)?;
}
if empty {
f.write_str("(empty)")?;
}
Ok(())
}
}
impl fmt::Binary for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Binary::fmt(&self.bits, f)
}
}
impl fmt::Octal for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Octal::fmt(&self.bits, f)
}
}
impl fmt::LowerHex for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::LowerHex::fmt(&self.bits, f)
}
}
impl fmt::UpperHex for FontStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::UpperHex::fmt(&self.bits, f)
}
}
impl ops::BitOr for FontStyle {
type Output = Self;
fn bitor(self, other: FontStyle) -> Self {
let bits = self.bits | other.bits;
Self { bits }
}
}
impl ops::BitOrAssign for FontStyle {
fn bitor_assign(&mut self, other: Self) {
self.bits |= other.bits;
}
}
impl ops::BitXor for FontStyle {
type Output = Self;
fn bitxor(self, other: Self) -> Self {
let bits = self.bits ^ other.bits;
Self { bits }
}
}
impl ops::BitXorAssign for FontStyle {
fn bitxor_assign(&mut self, other: Self) {
self.bits ^= other.bits;
}
}
impl ops::BitAnd for FontStyle {
type Output = Self;
fn bitand(self, other: Self) -> Self {
let bits = self.bits & other.bits;
Self { bits }
}
}
impl ops::BitAndAssign for FontStyle {
fn bitand_assign(&mut self, other: Self) {
self.bits &= other.bits;
}
}
impl ops::Sub for FontStyle {
type Output = Self;
fn sub(self, other: Self) -> Self {
let bits = self.bits & !other.bits;
Self { bits }
}
}
impl ops::SubAssign for FontStyle {
fn sub_assign(&mut self, other: Self) {
self.bits &= !other.bits;
}
}
impl ops::Not for FontStyle {
type Output = Self;
fn not(self) -> Self {
Self { bits: !self.bits } & Self::all()
}
}
impl Extend<FontStyle> for FontStyle {
fn extend<T: IntoIterator<Item = Self>>(&mut self, iterator: T) {
for item in iterator {
self.insert(item)
}
}
}
impl FromIterator<FontStyle> for FontStyle {
fn from_iter<T: IntoIterator<Item = Self>>(iterator: T) -> Self {
let mut result = Self::empty();
result.extend(iterator);
result
}
}
impl Color {
pub const BLACK: Color = Color {
r: 0x00,
g: 0x00,
b: 0x00,
a: 0xFF,
};
pub const WHITE: Color = Color {
r: 0xFF,
g: 0xFF,
b: 0xFF,
a: 0xFF,
};
}
impl Style {
pub fn apply(&self, modifier: StyleModifier) -> Style {
Style {
foreground: modifier.foreground.unwrap_or(self.foreground),
background: modifier.background.unwrap_or(self.background),
font_style: modifier.font_style.unwrap_or(self.font_style),
}
}
}
impl Default for Style {
fn default() -> Style {
Style {
foreground: Color::BLACK,
background: Color::WHITE,
font_style: FontStyle::empty(),
}
}
}
impl StyleModifier {
pub fn apply(&self, other: StyleModifier) -> StyleModifier {
StyleModifier {
foreground: other.foreground.or(self.foreground),
background: other.background.or(self.background),
font_style: other.font_style.or(self.font_style),
}
}
}