From c983000b510b050edef6847de53c2526a2ef6e20 Mon Sep 17 00:00:00 2001 From: kWeiZh Date: Fri, 2 Jul 2021 00:27:23 +0800 Subject: [PATCH] Implement custom deserialisation for ansi value and rgb (#576) --- Cargo.toml | 1 + src/style/types/color.rs | 200 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 197 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fae48ff..52c8f51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ tokio = { version = "1.5", features = ["full"] } futures = "0.3" futures-timer = "3.0" async-std = "1.9" +serde_json = "1.0.45" # # Examples diff --git a/src/style/types/color.rs b/src/style/types/color.rs index 705aafa..1ed04cc 100644 --- a/src/style/types/color.rs +++ b/src/style/types/color.rs @@ -1,7 +1,10 @@ use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use serde::Serialize; + +#[cfg(feature = "serde")] +use std::fmt; use crate::style::parse_next_u8; @@ -24,7 +27,7 @@ use crate::style::parse_next_u8; /// /// Most UNIX terminals and Windows 10 consoles support additional colors. /// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum Color { /// Resets the terminal color. @@ -214,13 +217,90 @@ impl From<(u8, u8, u8)> for Color { } } +#[cfg(feature = "serde")] +impl<'de> serde::de::Deserialize<'de> for Color { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct ColorVisitor; + impl<'de> serde::de::Visitor<'de> for ColorVisitor { + type Value = Color; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "`black`, `blue`, `dark_blue`, `cyan`, `dark_cyan`, `green`, `dark_green`, `grey`, `dark_grey`, `magenta`, `dark_magenta`, `red`, `dark_red`, `white`, `yellow`, `dark_yellow`, `u8`, or `3 u8 array`", + ) + } + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if let Ok(c) = Color::try_from(value) { + Ok(c) + } else { + Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + if value > 255 { + return Err(E::invalid_value( + serde::de::Unexpected::Unsigned(value), + &self, + )); + } + Ok(Color::AnsiValue(value as u8)) + } + + fn visit_seq(self, mut seq: M) -> Result + where + M: serde::de::SeqAccess<'de>, + { + let mut values = Vec::new(); + if let Some(size) = seq.size_hint() { + if size != 3 { + return Err(serde::de::Error::invalid_length( + size, + &"a list of size 3(RGB)", + )); + } + } + loop { + match seq.next_element::() { + Ok(Some(x)) => { + values.push(x); + } + Ok(None) => break, + Err(e) => { + return Err(e); + } + } + } + // recheck as size_hint sometimes not working + if values.len() != 3 { + return Err(serde::de::Error::invalid_length( + values.len(), + &"a list of size 3(RGB)", + )); + } + Ok(Color::from((values[0], values[1], values[2]))) + } + } + + deserializer.deserialize_any(ColorVisitor) + } +} + #[cfg(test)] mod tests { use super::Color; #[test] fn test_known_color_conversion() { - assert_eq!("black".parse(), Ok(Color::Black)); + assert_eq!("grey".parse(), Ok(Color::Grey)); assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); assert_eq!("red".parse(), Ok(Color::Red)); assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); @@ -235,7 +315,7 @@ mod tests { assert_eq!("cyan".parse(), Ok(Color::Cyan)); assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); assert_eq!("white".parse(), Ok(Color::White)); - assert_eq!("grey".parse(), Ok(Color::Grey)); + assert_eq!("black".parse(), Ok(Color::Black)); } #[test] @@ -256,3 +336,115 @@ mod tests { ); } } + +#[cfg(test)] +#[cfg(feature = "serde")] +mod serde_tests { + use super::Color; + use serde_json; + + #[test] + fn test_deserial_known_color_conversion() { + assert_eq!( + serde_json::from_str::("\"Red\"").unwrap(), + Color::Red + ); + assert_eq!( + serde_json::from_str::("\"red\"").unwrap(), + Color::Red + ); + assert_eq!( + serde_json::from_str::("\"dark_red\"").unwrap(), + Color::DarkRed + ); + assert_eq!( + serde_json::from_str::("\"grey\"").unwrap(), + Color::Grey + ); + assert_eq!( + serde_json::from_str::("\"dark_grey\"").unwrap(), + Color::DarkGrey + ); + assert_eq!( + serde_json::from_str::("\"green\"").unwrap(), + Color::Green + ); + assert_eq!( + serde_json::from_str::("\"dark_green\"").unwrap(), + Color::DarkGreen + ); + assert_eq!( + serde_json::from_str::("\"yellow\"").unwrap(), + Color::Yellow + ); + assert_eq!( + serde_json::from_str::("\"dark_yellow\"").unwrap(), + Color::DarkYellow + ); + assert_eq!( + serde_json::from_str::("\"blue\"").unwrap(), + Color::Blue + ); + assert_eq!( + serde_json::from_str::("\"dark_blue\"").unwrap(), + Color::DarkBlue + ); + assert_eq!( + serde_json::from_str::("\"magenta\"").unwrap(), + Color::Magenta + ); + assert_eq!( + serde_json::from_str::("\"dark_magenta\"").unwrap(), + Color::DarkMagenta + ); + assert_eq!( + serde_json::from_str::("\"cyan\"").unwrap(), + Color::Cyan + ); + assert_eq!( + serde_json::from_str::("\"dark_cyan\"").unwrap(), + Color::DarkCyan + ); + assert_eq!( + serde_json::from_str::("\"white\"").unwrap(), + Color::White + ); + assert_eq!( + serde_json::from_str::("\"black\"").unwrap(), + Color::Black + ); + } + + #[test] + fn test_deserial_unknown_color_conversion() { + assert!(serde_json::from_str::("\"unknown\"").is_err()); + } + + #[test] + fn test_deserial_ansi_value() { + assert_eq!( + serde_json::from_str::("255").unwrap(), + Color::AnsiValue(255) + ); + } + + #[test] + fn test_deserial_unvalid_ansi_value() { + assert!(serde_json::from_str::("256").is_err()); + assert!(serde_json::from_str::("-1").is_err()); + } + + #[test] + fn test_deserial_rgb() { + assert_eq!( + serde_json::from_str::("[255,255,255]").unwrap(), + Color::from((255, 255, 255)) + ); + } + + #[test] + fn test_deserial_unvalid_rgb() { + assert!(serde_json::from_str::("[255,255,255,255]").is_err()); + assert!(serde_json::from_str::("[256,255,255]").is_err()); + } +}