Implement custom deserialisation for ansi value and rgb (#576)

This commit is contained in:
kWeiZh 2021-07-02 00:27:23 +08:00 committed by GitHub
parent 70a3f6ff49
commit c983000b51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 197 additions and 4 deletions

View File

@ -67,6 +67,7 @@ tokio = { version = "1.5", features = ["full"] }
futures = "0.3" futures = "0.3"
futures-timer = "3.0" futures-timer = "3.0"
async-std = "1.9" async-std = "1.9"
serde_json = "1.0.45"
# #
# Examples # Examples

View File

@ -1,7 +1,10 @@
use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr}; use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::Serialize;
#[cfg(feature = "serde")]
use std::fmt;
use crate::style::parse_next_u8; 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. /// Most UNIX terminals and Windows 10 consoles support additional colors.
/// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info. /// 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)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Color { pub enum Color {
/// Resets the terminal 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<D>(deserializer: D) -> Result<Color, D::Error>
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<E>(self, value: &str) -> Result<Color, E>
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<E>(self, value: u64) -> Result<Color, E>
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<M>(self, mut seq: M) -> Result<Color, M::Error>
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::<u8>() {
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)] #[cfg(test)]
mod tests { mod tests {
use super::Color; use super::Color;
#[test] #[test]
fn test_known_color_conversion() { 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!("dark_grey".parse(), Ok(Color::DarkGrey));
assert_eq!("red".parse(), Ok(Color::Red)); assert_eq!("red".parse(), Ok(Color::Red));
assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); assert_eq!("dark_red".parse(), Ok(Color::DarkRed));
@ -235,7 +315,7 @@ mod tests {
assert_eq!("cyan".parse(), Ok(Color::Cyan)); assert_eq!("cyan".parse(), Ok(Color::Cyan));
assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan));
assert_eq!("white".parse(), Ok(Color::White)); assert_eq!("white".parse(), Ok(Color::White));
assert_eq!("grey".parse(), Ok(Color::Grey)); assert_eq!("black".parse(), Ok(Color::Black));
} }
#[test] #[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::<Color>("\"Red\"").unwrap(),
Color::Red
);
assert_eq!(
serde_json::from_str::<Color>("\"red\"").unwrap(),
Color::Red
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_red\"").unwrap(),
Color::DarkRed
);
assert_eq!(
serde_json::from_str::<Color>("\"grey\"").unwrap(),
Color::Grey
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_grey\"").unwrap(),
Color::DarkGrey
);
assert_eq!(
serde_json::from_str::<Color>("\"green\"").unwrap(),
Color::Green
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_green\"").unwrap(),
Color::DarkGreen
);
assert_eq!(
serde_json::from_str::<Color>("\"yellow\"").unwrap(),
Color::Yellow
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_yellow\"").unwrap(),
Color::DarkYellow
);
assert_eq!(
serde_json::from_str::<Color>("\"blue\"").unwrap(),
Color::Blue
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_blue\"").unwrap(),
Color::DarkBlue
);
assert_eq!(
serde_json::from_str::<Color>("\"magenta\"").unwrap(),
Color::Magenta
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_magenta\"").unwrap(),
Color::DarkMagenta
);
assert_eq!(
serde_json::from_str::<Color>("\"cyan\"").unwrap(),
Color::Cyan
);
assert_eq!(
serde_json::from_str::<Color>("\"dark_cyan\"").unwrap(),
Color::DarkCyan
);
assert_eq!(
serde_json::from_str::<Color>("\"white\"").unwrap(),
Color::White
);
assert_eq!(
serde_json::from_str::<Color>("\"black\"").unwrap(),
Color::Black
);
}
#[test]
fn test_deserial_unknown_color_conversion() {
assert!(serde_json::from_str::<Color>("\"unknown\"").is_err());
}
#[test]
fn test_deserial_ansi_value() {
assert_eq!(
serde_json::from_str::<Color>("255").unwrap(),
Color::AnsiValue(255)
);
}
#[test]
fn test_deserial_unvalid_ansi_value() {
assert!(serde_json::from_str::<Color>("256").is_err());
assert!(serde_json::from_str::<Color>("-1").is_err());
}
#[test]
fn test_deserial_rgb() {
assert_eq!(
serde_json::from_str::<Color>("[255,255,255]").unwrap(),
Color::from((255, 255, 255))
);
}
#[test]
fn test_deserial_unvalid_rgb() {
assert!(serde_json::from_str::<Color>("[255,255,255,255]").is_err());
assert!(serde_json::from_str::<Color>("[256,255,255]").is_err());
}
}