diff --git a/pagetop/Cargo.toml b/pagetop/Cargo.toml index ad20879f..fc793f75 100644 --- a/pagetop/Cargo.toml +++ b/pagetop/Cargo.toml @@ -23,6 +23,7 @@ figlet-rs = "0.1.5" itoa = "1.0.14" nom = "7.1.3" paste = "1.0.15" +regex = "1.11.1" serde.workspace = true substring = "1.4.5" terminal_size = "0.4.1" diff --git a/pagetop/src/html/unit.rs b/pagetop/src/html/unit.rs index 5a153c55..5889b775 100644 --- a/pagetop/src/html/unit.rs +++ b/pagetop/src/html/unit.rs @@ -1,35 +1,38 @@ use crate::AutoDefault; +use regex::Regex; +use serde::{Deserialize, Deserializer}; + use std::fmt; -// About pixels: Pixels (px) are relative to the viewing device. For low-dpi devices, 1px is one -// device pixel (dot) of the display. For printers and high resolution screens 1px implies multiple -// device pixels. +// Sobre píxeles: Los píxeles (px) son relativos al dispositivo de visualización. En dispositivos +// con baja densidad de píxeles (dpi), 1px equivale a un píxel (punto) del dispositivo. En +// impresoras y pantallas de alta resolución, 1px implica múltiples píxeles del dispositivo. -// About em: 2em means 2 times the size of the current font. The em and rem units are practical in -// creating perfectly scalable layout! +// Sobre em: 2em significa 2 veces el tamaño de la fuente actual. Las unidades em y rem son muy +// útiles para crear diseños completamente escalables. -// About viewport: If the browser window size is 50cm wide, 1vw = 0.5cm. +// Sobre el viewport: Si el ancho de la ventana del navegador es de 50cm, 1vw equivale a 0.5cm. #[rustfmt::skip] -#[derive(AutoDefault)] +#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] pub enum Value { #[default] None, Auto, - Cm(isize), // Centimeters. - In(isize), // Inches (1in = 96px = 2.54cm). - Mm(isize), // Millimeters. - Pc(isize), // Picas (1pc = 12pt). - Pt(isize), // Points (1pt = 1/72 of 1in). - Px(isize), // Pixels (1px = 1/96th of 1in). + Cm(isize), // Centímetros. + In(isize), // Pulgadas (1in = 96px = 2.54cm). + Mm(isize), // Milímetros. + Pc(isize), // Picas (1pc = 12pt). + Pt(isize), // Puntos (1pt = 1/72 of 1in). + Px(isize), // Píxeles (1px = 1/96th of 1in). - RelEm(f32), // Relative to the font-size of the element. - RelPct(f32), // Percentage relative to the parent element. - RelRem(f32), // Relative to font-size of the root element. - RelVh(f32), // Relative to 1% of the height of the viewport. - RelVw(f32), // Relative to 1% of the value of the viewport. + RelEm(f32), // Relativo al tamaño de la fuente del elemento. + RelRem(f32), // Relativo al tamaño de la fuente del elemento raíz. + RelPct(f32), // Porcentaje relativo al elemento padre. + RelVh(f32), // Relativo al 1% de la altura del viewport. + RelVw(f32), // Relativo al 1% del valor del viewport. } #[rustfmt::skip] @@ -38,19 +41,66 @@ impl fmt::Display for Value { match self { Value::None => write!(f, ""), Value::Auto => write!(f, "auto"), - // Absolute value. + // Valor absoluto. Value::Cm(av) => write!(f, "{av}cm"), Value::In(av) => write!(f, "{av}in"), Value::Mm(av) => write!(f, "{av}mm"), Value::Pc(av) => write!(f, "{av}pc"), Value::Pt(av) => write!(f, "{av}pt"), Value::Px(av) => write!(f, "{av}px"), - // Relative value. + // Valor relativo. Value::RelEm(rv) => write!(f, "{rv}em"), - Value::RelPct(rv) => write!(f, "{rv}%"), Value::RelRem(rv) => write!(f, "{rv}rem"), + Value::RelPct(rv) => write!(f, "{rv}%"), Value::RelVh(rv) => write!(f, "{rv}vh"), Value::RelVw(rv) => write!(f, "{rv}vw"), } } } + +impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let raw = String::deserialize(deserializer)?; + let rex = Regex::new(r"^(auto|\d+(\.\d+)?(cm|in|mm|pc|pt|px|em|rem|%|vh|vw)?)$").unwrap(); + + if raw == "auto" { + return Ok(Value::Auto); + } else if let Some(captures) = rex.captures(&raw) { + if let Some(unit) = captures.get(3) { + let num_str = &raw[..raw.len() - unit.as_str().len()]; + + // Analizar como `isize` para unidades absolutas. + if matches!(unit.as_str(), "cm" | "in" | "mm" | "pc" | "pt" | "px") { + let num: isize = num_str.parse().map_err(serde::de::Error::custom)?; + return match unit.as_str() { + "cm" => Ok(Value::Cm(num)), + "in" => Ok(Value::In(num)), + "mm" => Ok(Value::Mm(num)), + "pc" => Ok(Value::Pc(num)), + "pt" => Ok(Value::Pt(num)), + "px" => Ok(Value::Px(num)), + _ => unreachable!(), + }; + } + + // Analizar como `f32` para unidades relativas. + let num: f32 = num_str.parse().map_err(serde::de::Error::custom)?; + return match unit.as_str() { + "em" => Ok(Value::RelEm(num)), + "rem" => Ok(Value::RelRem(num)), + "%" => Ok(Value::RelPct(num)), + "vh" => Ok(Value::RelVh(num)), + "vw" => Ok(Value::RelVw(num)), + _ => unreachable!(), + }; + } + } + Err(serde::de::Error::custom(format!( + "Invalid format for Value: {}", + raw + ))) + } +}