diff options
Diffstat (limited to 'src/display/color.rs')
-rw-r--r-- | src/display/color.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/display/color.rs b/src/display/color.rs new file mode 100644 index 000000000000..7de1f124b704 --- /dev/null +++ b/src/display/color.rs @@ -0,0 +1,149 @@ +use serde::de::{self, Unexpected, Visitor}; +use std::fmt; +use std::marker::PhantomData; +use termion::color; + +#[derive(Debug)] +pub struct Color(Box<dyn color::Color>); + +unsafe impl Sync for Color {} +unsafe impl Send for Color {} + +impl Color { + pub fn new<C: color::Color + 'static>(c: C) -> Self { + Color(Box::new(c)) + } +} + +impl color::Color for Color { + fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_fg(f) + } + + fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_bg(f) + } +} + +impl<'a> color::Color for &'a Color { + fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_fg(f) + } + + fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.write_bg(f) + } +} + +pub struct ColorVisitor { + marker: PhantomData<fn() -> Color>, +} + +impl ColorVisitor { + fn new() -> Self { + ColorVisitor { + marker: PhantomData, + } + } +} + +impl<'de> Visitor<'de> for ColorVisitor { + type Value = Color; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("A color") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + match v.to_lowercase().as_ref() { + "black" => Ok(Color(Box::new(color::Black))), + "blue" => Ok(Color(Box::new(color::Blue))), + "cyan" => Ok(Color(Box::new(color::Cyan))), + "green" => Ok(Color(Box::new(color::Green))), + "light black" | "light_black" => { + Ok(Color(Box::new(color::LightBlack))) + } + "light blue" | "light_blue" => { + Ok(Color(Box::new(color::LightBlue))) + } + "light cyan" | "light_cyan" => { + Ok(Color(Box::new(color::LightCyan))) + } + "light green" | "light_green" => { + Ok(Color(Box::new(color::LightGreen))) + } + "light magenta" | "light_magenta" => { + Ok(Color(Box::new(color::LightMagenta))) + } + "light red" | "light_red" => Ok(Color(Box::new(color::LightRed))), + "light white" | "light_white" => { + Ok(Color(Box::new(color::LightWhite))) + } + "light yellow" | "light_yellow" => { + Ok(Color(Box::new(color::LightYellow))) + } + "magenta" => Ok(Color(Box::new(color::Magenta))), + "magenta" => Ok(Color(Box::new(color::Magenta))), + "red" => Ok(Color(Box::new(color::Red))), + "white" => Ok(Color(Box::new(color::White))), + "yellow" => Ok(Color(Box::new(color::Yellow))), + _ => Err(de::Error::invalid_value( + Unexpected::Str(v), + &"a valid color", + )), + } + } + + fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> + where + A: de::MapAccess<'de>, + { + let mut red = None; + let mut green = None; + let mut blue = None; + while let Some((k, v)) = map.next_entry()? { + match k { + "red" => { + red = Some(v); + } + "green" => { + green = Some(v); + } + "blue" => { + blue = Some(v); + } + _ => { + return Err(de::Error::unknown_field( + k, + &["red", "green", "blue"], + )); + } + } + } + + match (red, green, blue) { + (Some(r), Some(g), Some(b)) => { + Ok(Color(Box::new(color::Rgb(r, g, b)))) + } + (None, _, _) => Err(de::Error::missing_field("red")), + (_, None, _) => Err(de::Error::missing_field("green")), + (_, _, None) => Err(de::Error::missing_field("blue")), + } + } + + fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> { + Ok(Color(Box::new(color::AnsiValue(v)))) + } +} + +impl<'de> serde::Deserialize<'de> for Color { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(ColorVisitor::new()) + } +} |