146 lines
3.7 KiB
Rust
146 lines
3.7 KiB
Rust
use serde::{de::Visitor, Deserialize, Serialize};
|
|
|
|
use termion::color;
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
|
pub struct Theme {
|
|
pub primary: ColorSet,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
|
pub struct ColorSet {
|
|
pub fg: Color,
|
|
pub bg: Color,
|
|
}
|
|
|
|
impl Default for ColorSet {
|
|
fn default() -> Self {
|
|
Self {
|
|
fg: "#ffffff".try_into().unwrap(),
|
|
bg: "#000000".try_into().unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToString for ColorSet {
|
|
#[inline(always)]
|
|
fn to_string(&self) -> String {
|
|
self.bg.bg() + &self.fg.fg()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub struct Color(u8, u8, u8);
|
|
|
|
impl Color {
|
|
pub const BLACK: Color = Color(0, 0, 0);
|
|
pub const WHITE: Color = Color(255, 255, 255);
|
|
pub const RED: Color = Color(255, 0, 0);
|
|
pub const GREEN: Color = Color(0, 255, 0);
|
|
pub const BLUE: Color = Color(0, 0, 255);
|
|
}
|
|
|
|
impl Serialize for Color {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: serde::Serializer,
|
|
{
|
|
serializer.serialize_str(&format!(
|
|
"#{:02X}{:02X}{:02X}",
|
|
self.0, self.1, self.2,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for Color {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
struct ColorCodeVisitor;
|
|
impl<'de> Visitor<'de> for ColorCodeVisitor {
|
|
fn expecting(
|
|
&self,
|
|
formatter: &mut std::fmt::Formatter,
|
|
) -> std::fmt::Result {
|
|
formatter.write_str("hex color code")
|
|
}
|
|
type Value = Color;
|
|
|
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
where
|
|
E: serde::de::Error,
|
|
{
|
|
match TryInto::<Color>::try_into(v) {
|
|
Ok(c) => Ok(c),
|
|
Err(e) => Err(E::custom(e.to_string())),
|
|
}
|
|
}
|
|
|
|
fn visit_string<E>(
|
|
self,
|
|
v: String,
|
|
) -> Result<Self::Value, E>
|
|
where
|
|
E: serde::de::Error,
|
|
{
|
|
match TryInto::<Color>::try_into(v) {
|
|
Ok(c) => Ok(c),
|
|
Err(e) => Err(E::custom(e.to_string())),
|
|
}
|
|
}
|
|
}
|
|
deserializer.deserialize_any(ColorCodeVisitor {})
|
|
}
|
|
}
|
|
|
|
impl Color {
|
|
pub fn bg(&self) -> String {
|
|
color::Rgb(self.0, self.1, self.2).bg_string()
|
|
}
|
|
pub fn fg(&self) -> String {
|
|
color::Rgb(self.0, self.1, self.2).fg_string()
|
|
}
|
|
}
|
|
|
|
impl TryFrom<String> for Color {
|
|
type Error = anyhow::Error;
|
|
|
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
Ok(value.as_str().try_into()?)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&str> for Color {
|
|
type Error = anyhow::Error;
|
|
|
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
if value.len() < 6 || value.len() > 7 {
|
|
panic!("hex code length invalid: {}", value.len());
|
|
}
|
|
let mut i = 0;
|
|
if value.starts_with('#') {
|
|
i = 1;
|
|
}
|
|
Ok(Self(
|
|
u8::from_str_radix(&value[i..i + 2], 16)
|
|
.map_err(|_| anyhow::anyhow!("red hex"))?,
|
|
u8::from_str_radix(&value[i + 2..i + 4], 16)
|
|
.map_err(|_| anyhow::anyhow!("green hex"))?,
|
|
u8::from_str_radix(&value[i + 4..i + 6], 16)
|
|
.map_err(|_| anyhow::anyhow!("blue hex"))?,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl Default for Theme {
|
|
fn default() -> Self {
|
|
Self {
|
|
primary: ColorSet {
|
|
fg: "#330033".try_into().unwrap(),
|
|
bg: "#a006d3".try_into().unwrap(),
|
|
},
|
|
}
|
|
}
|
|
}
|