//! Color representation and serde helpers for the Cosmic theme use hex_color::HexColor; use palette::{Srgb, Srgba}; use serde::{Deserialize, Serialize}; /// A color in the Cosmic theme for serialization and deserialization #[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum ColorRepr { /// A color represented as a hex string #[serde(with = "hex_color::rgba")] Hex(HexColor), /// A color represented as an RGBA value Rgba(Srgba), /// A color represented as an RGB value Rgb(Srgb), } /// An optional color in the Cosmic theme for serialization and deserialization #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] #[serde(transparent)] pub struct ColorReprOption(Option); impl From for ColorRepr { fn from(color: Srgb) -> Self { let rgb_u8: Srgb = color.into_format(); ColorRepr::Hex(HexColor { r: rgb_u8.red, g: rgb_u8.green, b: rgb_u8.blue, a: 255, }) } } impl From for ColorRepr { fn from(color: Srgba) -> Self { let rgba_u8: Srgba = color.into_format(); ColorRepr::Hex(HexColor { r: rgba_u8.red, g: rgba_u8.green, b: rgba_u8.blue, a: rgba_u8.alpha, }) } } impl From for Srgb { fn from(value: ColorRepr) -> Self { match value { ColorRepr::Hex(hex) => Srgb::::new(hex.r, hex.g, hex.b).into_format(), ColorRepr::Rgb(rgb) => rgb, ColorRepr::Rgba(rgba) => Srgb::new(rgba.red, rgba.green, rgba.blue), } } } impl From for Srgba { fn from(value: ColorRepr) -> Self { match value { ColorRepr::Hex(hex) => Srgba::::new(hex.r, hex.g, hex.b, hex.a).into_format(), ColorRepr::Rgb(rgb) => Srgba::new(rgb.red, rgb.green, rgb.blue, 1.0), ColorRepr::Rgba(rgba) => rgba, } } } impl From for Option { fn from(value: ColorReprOption) -> Self { value.0.map(std::convert::Into::into) } } impl From for Option { fn from(value: ColorReprOption) -> Self { value.0.map(std::convert::Into::into) } } impl From> for ColorReprOption { fn from(value: Option) -> Self { ColorReprOption(value.map(std::convert::Into::into)) } } impl From> for ColorReprOption { fn from(value: Option) -> Self { ColorReprOption(value.map(std::convert::Into::into)) } } /// A trait for converting between a color type and its representation for serialization and deserialization pub trait ConvColorRepr: Sized { /// Convert from a color representation to the color type fn from_repr(repr: ColorRepr) -> Self; /// Convert from the color type to its representation for serialization fn to_repr(&self) -> ColorRepr; } impl ConvColorRepr for Srgba { fn from_repr(repr: ColorRepr) -> Self { repr.into() } fn to_repr(&self) -> ColorRepr { (*self).into() } } impl ConvColorRepr for Srgb { fn from_repr(repr: ColorRepr) -> Self { repr.into() } fn to_repr(&self) -> ColorRepr { (*self).into() } } /// Serde helpers for serializing and deserializing colors in the Cosmic theme pub mod color_serde { use super::*; use serde::{Deserialize, Deserializer, Serializer}; /// Serialize a color to a hex string pub fn serialize(color: &T, serializer: S) -> Result where T: ConvColorRepr, S: Serializer, { let repr = color.to_repr(); repr.serialize(serializer) } /// Deserialize a color from a hex string or RGB/RGBA pub fn deserialize<'de, T, D>(deserializer: D) -> Result where T: ConvColorRepr, D: Deserializer<'de>, { let repr = ColorRepr::deserialize(deserializer)?; Ok(T::from_repr(repr)) } /// Serde helpers for serializing and deserializing optional colors in the Cosmic theme pub mod option { use super::*; /// Serialize an optional color pub fn serialize(value: &Option, serializer: S) -> Result where T: ConvColorRepr, S: Serializer, { match value { Some(v) => super::serialize(v, serializer), None => serializer.serialize_none(), } } /// Deserialize an optional color pub fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> where T: ConvColorRepr, D: Deserializer<'de>, { let opt = Option::::deserialize(deserializer)?; Ok(opt.map(T::from_repr)) } } }