about summary refs log tree commit diff
path: root/src/display/color.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/color.rs')
-rw-r--r--src/display/color.rs149
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())
+    }
+}