#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::missing_errors_doc
)]
use crate::{HexColor, ParseMode};
use core::fmt::{self, Write};
use arrayvec::ArrayString;
use serde::{
de::{Error, Unexpected, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
impl HexColor {
fn to_rgb_string(self) -> ArrayString<7> {
let mut string = ArrayString::new();
unsafe { write!(string, "{self}").unwrap_unchecked() };
string
}
fn to_rgba_string(self) -> ArrayString<9> {
let mut string = ArrayString::new();
unsafe { write!(string, "{self:#}").unwrap_unchecked() };
string
}
}
struct HexColorStringVisitor {
mode: ParseMode,
}
impl HexColorStringVisitor {
fn new(mode: ParseMode) -> Self {
HexColorStringVisitor { mode }
}
}
impl<'de> Visitor<'de> for HexColorStringVisitor {
type Value = HexColor;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let message = match self.mode {
ParseMode::Any => "an RGB(A) hexadecimal color",
ParseMode::Rgb => "an RGB hexadecimal color",
ParseMode::Rgba => "an RGBA hexadecimal color",
};
formatter.write_str(message)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
HexColor::parse_internals(s, self.mode)
.map_err(|_| E::invalid_value(Unexpected::Str(s), &self))
}
}
enum NumberMode {
U24,
U32,
}
struct HexColorNumberVisitor {
mode: NumberMode,
}
impl HexColorNumberVisitor {
fn new(mode: NumberMode) -> Self {
HexColorNumberVisitor { mode }
}
}
impl<'de> Visitor<'de> for HexColorNumberVisitor {
type Value = HexColor;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let message = match self.mode {
NumberMode::U24 => "a value in the range 0x0000_0000..=0x00FF_FFFF",
NumberMode::U32 => "a value in the range 0x0000_0000..=0xFFFF_FFFF",
};
formatter.write_str(message)
}
fn visit_i64<E>(self, n: i64) -> Result<Self::Value, E>
where
E: Error,
{
if n < 0x0000_0000 {
return Err(E::invalid_type(Unexpected::Signed(n), &self));
}
match self.mode {
NumberMode::U24 if n <= 0x00FF_FFFF => Ok(HexColor::from_u24(n as u32)),
NumberMode::U32 if n <= 0xFFFF_FFFF => Ok(HexColor::from_u32(n as u32)),
_ => Err(E::invalid_value(Unexpected::Unsigned(n as u64), &self)),
}
}
fn visit_u64<E>(self, n: u64) -> Result<Self::Value, E>
where
E: Error,
{
match self.mode {
NumberMode::U24 if n <= 0x00FF_FFFF => Ok(HexColor::from_u24(n as u32)),
NumberMode::U32 if n <= 0xFFFF_FFFF => Ok(HexColor::from_u32(n as u32)),
_ => Err(E::invalid_value(Unexpected::Unsigned(n), &self)),
}
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub mod rgb {
use super::HexColorStringVisitor;
use crate::{HexColor, ParseMode};
use serde::{Deserializer, Serializer};
#[inline]
pub fn deserialize<'de, D>(deserializer: D) -> Result<HexColor, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(HexColorStringVisitor::new(ParseMode::Rgb))
}
#[inline]
pub fn serialize<S>(color: &HexColor, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&color.to_rgb_string())
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub mod rgba {
use super::HexColorStringVisitor;
use crate::{HexColor, ParseMode};
use serde::{Deserializer, Serializer};
#[inline]
pub fn deserialize<'de, D>(deserializer: D) -> Result<HexColor, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(HexColorStringVisitor::new(ParseMode::Rgba))
}
#[inline]
pub fn serialize<S>(color: &HexColor, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&color.to_rgba_string())
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub mod u24 {
use super::{HexColorNumberVisitor, NumberMode};
use crate::HexColor;
use serde::{Deserializer, Serializer};
#[inline]
pub fn deserialize<'de, D>(deserializer: D) -> Result<HexColor, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u64(HexColorNumberVisitor::new(NumberMode::U24))
}
#[inline]
pub fn serialize<S>(color: &HexColor, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_u32(color.to_u24())
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub mod u32 {
use super::{HexColorNumberVisitor, NumberMode};
use crate::HexColor;
use serde::{Deserializer, Serializer};
#[inline]
pub fn deserialize<'de, D>(deserializer: D) -> Result<HexColor, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u64(HexColorNumberVisitor::new(NumberMode::U32))
}
#[inline]
pub fn serialize<S>(color: &HexColor, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_u32(color.to_u32())
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for HexColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(HexColorStringVisitor::new(ParseMode::Any))
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl Serialize for HexColor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.a == u8::MAX {
serializer.serialize_str(&self.to_rgb_string())
} else {
serializer.serialize_str(&self.to_rgba_string())
}
}
}