use serde::{Deserialize, Deserializer}; use std::fmt; use std::str::FromStr; // TODO: parse -, + (100vw - 10px) // TODO: parse 100vw [min] 10 // TODO: parse 100vw [max] 10 #[derive(Debug, Clone, PartialEq)] pub enum PixelDim { Pixels(u32), WidthPercent(f32), HeightPercent(f32), } impl FromStr for PixelDim { type Err = String; fn from_str(s: &str) -> Result { let numeric_end = s.find(|c: char| !c.is_ascii_digit() && c != '.'); let (quantity, unit) = numeric_end.map(|x| s.split_at(x)).unwrap_or((s, "px")); let quantity = quantity.trim(); let unit = unit.trim(); match unit { "vw" => Ok(PixelDim::WidthPercent( quantity .parse() .map_err(|_err| format!("invalid quantity {quantity}"))?, )), "vh" => Ok(PixelDim::HeightPercent( quantity .parse() .map_err(|_err| format!("invalid quantity {quantity}"))?, )), "px" => Ok(PixelDim::Pixels( quantity .parse() .map_err(|_err| format!("invalid quantity {quantity}"))?, )), _ => Err(format!("invalid unit {unit}")), } } } impl<'de> Deserialize<'de> for PixelDim { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; FromStr::from_str(&s).map_err(serde::de::Error::custom) } } impl fmt::Display for PixelDim { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PixelDim::Pixels(px) => write!(f, "{px}"), PixelDim::WidthPercent(p) => write!(f, "{p:.2}vw"), PixelDim::HeightPercent(p) => write!(f, "{p:.2}vh"), } } }