1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
use crateAutoDefault;
use ;
use fmt;
use FromStr;
/// Representa una **unidad CSS** lista para formatear o deserializar.
///
/// # Unidades soportadas
///
/// - **Absolutas** *(valores enteros, `isize`)*:
/// - `Cm(isize)` - `cm` (centímetros)
/// - `In(isize)` - `in` (pulgadas; `1in = 96px = 2.54cm`)
/// - `Mm(isize)` - `mm` (milímetros)
/// - `Pc(isize)` - `pc` (picas; `1pc = 12pt`)
/// - `Pt(isize)` - `pt` (puntos; `1pt = 1/72in`)
/// - `Px(isize)` - `px` (píxeles; `1px = 1/96in`)
///
/// - **Relativas** *(valores decimales, `f32`)*:
/// - `RelEm(f32)` - `em` (relativa al tamaño de fuente del elemento)
/// - `RelRem(f32)` - `rem` (relativa al tamaño de fuente de `:root`)
/// - `RelPct(f32)` - `%` (porcentaje relativo al elemento padre)
/// - `RelVh(f32)` - `vh` (1% de la **altura** del viewport)
/// - `RelVw(f32)` - `vw` (1% del **ancho** del viewport)
///
/// # Valores especiales
///
/// - `None` - equivale a un texto vacío (`""`), útil para atributos opcionales.
/// - `Auto` - equivale a `"auto"`.
/// - `Zero` - equivale a `"0"` (cero sin unidad).
///
/// # Características
///
/// - Soporta unidades **absolutas** (`cm`, `in`, `mm`, `pc`, `pt`, `px`) y **relativas** (`em`,
/// `rem`, `%`, `vh`, `vw`).
/// - `FromStr` para convertir desde texto (p. ej., `"12px"`, `"1.25rem"`, `"auto"`).
/// - `Display` para formatear a cadena (p. ej., `UnitValue::Px(12)` genera `"12px"`).
/// - `Deserialize` delega en `FromStr`, garantizando una gramática única.
///
/// # Ejemplos
///
/// ```rust
/// # use pagetop::prelude::*;
/// use std::str::FromStr;
///
/// assert_eq!(UnitValue::from_str("16px").unwrap(), UnitValue::Px(16));
/// assert_eq!(UnitValue::from_str("1.25rem").unwrap(), UnitValue::RelRem(1.25));
/// assert_eq!(UnitValue::from_str("33%").unwrap(), UnitValue::RelPct(33.0));
/// assert_eq!(UnitValue::from_str("auto").unwrap(), UnitValue::Auto);
/// assert_eq!(UnitValue::from_str("").unwrap(), UnitValue::None);
/// assert_eq!(UnitValue::from_str("0").unwrap(), UnitValue::Zero);
/// ```
///
/// # Notas
///
/// - Las absolutas **no aceptan** decimales (p. ej., `"1.5px"` sería erróneo).
/// - Se aceptan signos `+`/`-` en todas las unidades (p. ej., `"-12px"`, `"+0.5em"`).
/// - La comparación de unidad es *case-insensitive* al interpretar el texto (`"PX"`, `"Px"`, ...).
/// - **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.
/// - **Sobre `em` y `rem`**:
/// - `em` es **relativo al tamaño de fuente *del propio elemento***. Si el elemento hereda o
/// cambia su `font-size`, todos los valores en `em` dentro de él **se escalan en cascada**.
/// - `rem` es **relativo al tamaño de fuente del elemento raíz** (`:root`/`html`), **no se verá
/// afectado** por cambios de `font-size` en elementos anidados.
/// - Ejemplo: si `:root { font-size: 16px }` y un contenedor tiene `font-size: 20px`, entonces
/// dentro del contenedor `1em == 20px` pero `1rem == 16px`.
/// - Uso típico: `rem` para tipografía y espaciados globales (consistencia al cambiar la base del
/// sitio); `em` para tamaños que deban escalar **con el propio componente** (p. ej.,
/// `padding: 0.5em` que crece si el componente aumenta su `font-size`).
/// - **Sobre el viewport**: Si el ancho de la ventana del navegador es de 50cm, 1vw equivale a
/// 0.5cm (1vw siempre es 1% del ancho del viewport, independientemente del zoom del navegador o
/// la densidad de píxeles del dispositivo).
/// Formatea la unidad como cadena CSS.
///
/// Reglas:
///
/// - `None` - `""` (cadena vacía).
/// - `Auto` - `"auto"`.
/// - `Zero` - `"0"` (cero sin unidad).
/// - Absolutas - entero con su unidad: `Px(12)` a `"12px"`.
/// - Relativas - número en punto flotante; si es entero, se imprime sin decimales:
/// - `RelEm(2.0)` a `"2em"`
/// - `RelPct(33.5)` a `"33.5%"`
/// Convierte una cadena a [`UnitValue`] siguiendo una gramática CSS acotada.
///
/// # Acepta
///
/// - `""` para `UnitValue::None`
/// - `"auto"`
/// - **Cero sin unidad**: `"0"`, `"+0"`, `"-0"`, `"0.0"`, `"0."`, `".0"` para `UnitValue::Zero`
/// - Porcentaje: `"<n>%"` (p. ej., `"33%"`, `"33 %"`)
/// - Absolutas enteras: `"<entero><unidad>"`, p. ej., `"12px"`, `"-5pt"`
/// - Relativas decimales: `"<float><unidad>"`, p. ej., `"1.25rem"`, `"-0.5vh"`, `".5em"`, `"1.rem"`
///
/// (Se toleran espacios entre número y unidad: `"12 px"`, `"1.5 rem"`).
///
/// # Ejemplo
///
/// ```rust
/// # use pagetop::prelude::*;
/// use std::str::FromStr;
///
/// assert_eq!(UnitValue::from_str("12px").unwrap(), UnitValue::Px(12));
/// assert!(UnitValue::from_str("12").is_err());
/// ```
///
/// # Errores de interpretación
///
/// - Falta la unidad cuando es necesaria (p. ej., `"12"`, excepto para el valor cero).
/// - Decimales en valores que deben ser absolutos (p. ej. `"1.5px"`).
/// - Unidades desconocidas (p. ej., `"10ch"`, no soportada aún).
/// - Notación científica o bases no decimales: `"1e3vw"`, `"0x10px"` (no soportadas). Los ceros a
/// la izquierda (p. ej. `"020px"`) se interpretan en **base 10** (`20px`).
///
/// La comparación de la unidad es *case-insensitive*.
/// Deserializa desde una cadena usando la misma gramática que [`FromStr`].
///
/// # Ejemplo con `serde_json`
/// ```rust
/// # use pagetop::prelude::*;
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct Style { width: UnitValue }
///
/// // "{\"width\":\"12px\"}" deserializa como `Style { width: UnitValue::Px(12) }`
/// ```