1use core::fmt;
5use std::num::ParseFloatError;
6
7use crate::corety::AzString;
8
9pub const FP_PRECISION_MULTIPLIER: f32 = 1000.0;
15const FP_PRECISION_MULTIPLIER_CONST: isize = FP_PRECISION_MULTIPLIER as isize;
16
17#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(C)]
21pub struct PercentageValue {
22 number: FloatValue,
23}
24
25impl_option!(
26 PercentageValue,
27 OptionPercentageValue,
28 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
29);
30
31impl fmt::Display for PercentageValue {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 write!(f, "{}%", self.normalized() * 100.0)
34 }
35}
36
37impl PercentageValue {
38 #[inline]
41 pub const fn const_new(value: isize) -> Self {
42 Self {
43 number: FloatValue::const_new(value),
44 }
45 }
46
47 #[inline]
59 pub const fn const_new_fractional(pre_comma: isize, post_comma: isize) -> Self {
60 Self {
61 number: FloatValue::const_new_fractional(pre_comma, post_comma),
62 }
63 }
64
65 #[inline]
66 pub fn new(value: f32) -> Self {
67 Self {
68 number: value.into(),
69 }
70 }
71
72 #[inline]
75 pub fn normalized(&self) -> f32 {
76 self.number.get() / 100.0
77 }
78
79 #[inline]
80 pub fn interpolate(&self, other: &Self, t: f32) -> Self {
81 Self {
82 number: self.number.interpolate(&other.number, t),
83 }
84 }
85}
86
87#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90#[repr(C)]
91pub struct FloatValue {
92 pub(crate) number: isize,
93}
94
95impl fmt::Display for FloatValue {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 write!(f, "{}", self.get())
98 }
99}
100
101impl ::core::fmt::Debug for FloatValue {
102 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
103 write!(f, "{}", self)
104 }
105}
106
107impl Default for FloatValue {
108 fn default() -> Self {
109 const DEFAULT_FLV: FloatValue = FloatValue::const_new(0);
110 DEFAULT_FLV
111 }
112}
113
114impl FloatValue {
115 #[inline]
118 pub const fn const_new(value: isize) -> Self {
119 Self {
120 number: value * FP_PRECISION_MULTIPLIER_CONST,
121 }
122 }
123
124 #[inline]
147 pub const fn const_new_fractional(pre_comma: isize, post_comma: isize) -> Self {
148 let abs_post = if post_comma < 0 {
150 -post_comma
151 } else {
152 post_comma
153 };
154
155 let (normalized_post, divisor) = if abs_post < 10 {
158 (abs_post, 10)
160 } else if abs_post < 100 {
161 (abs_post, 100)
163 } else if abs_post < 1000 {
164 (abs_post, 1000)
166 } else if abs_post < 10000 {
167 (abs_post / 10, 1000)
169 } else if abs_post < 100000 {
170 (abs_post / 100, 1000)
171 } else if abs_post < 1000000 {
172 (abs_post / 1000, 1000)
173 } else if abs_post < 10000000 {
174 (abs_post / 10000, 1000)
175 } else if abs_post < 100000000 {
176 (abs_post / 100000, 1000)
177 } else if abs_post < 1000000000 {
178 (abs_post / 1000000, 1000)
179 } else {
180 (abs_post / 10000000, 1000)
183 };
184
185 let fractional_part = normalized_post * (FP_PRECISION_MULTIPLIER_CONST / divisor);
187
188 let signed_fractional = if post_comma < 0 {
190 -fractional_part
191 } else {
192 fractional_part
193 };
194
195 let final_fractional = if pre_comma < 0 && post_comma >= 0 {
198 -signed_fractional
199 } else {
200 signed_fractional
201 };
202
203 Self {
204 number: pre_comma * FP_PRECISION_MULTIPLIER_CONST + final_fractional,
205 }
206 }
207
208 #[inline]
209 pub fn new(value: f32) -> Self {
210 Self {
211 number: (value * FP_PRECISION_MULTIPLIER) as isize,
212 }
213 }
214
215 #[inline]
216 pub fn get(&self) -> f32 {
217 self.number as f32 / FP_PRECISION_MULTIPLIER
218 }
219
220 #[inline]
225 pub fn number(&self) -> isize {
226 self.number
227 }
228
229 #[inline]
230 pub fn interpolate(&self, other: &Self, t: f32) -> Self {
231 let self_val_f32 = self.get();
232 let other_val_f32 = other.get();
233 let interpolated = self_val_f32 + ((other_val_f32 - self_val_f32) * t);
234 Self::new(interpolated)
235 }
236}
237
238impl From<f32> for FloatValue {
239 #[inline]
240 fn from(val: f32) -> Self {
241 Self::new(val)
242 }
243}
244
245#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
247#[repr(C)]
248#[derive(Default)]
249pub enum SizeMetric {
250 #[default]
251 Px,
252 Pt,
253 Em,
254 Rem,
255 In,
256 Cm,
257 Mm,
258 Percent,
259 Vw,
261 Vh,
263 Vmin,
265 Vmax,
267}
268
269
270impl fmt::Display for SizeMetric {
271 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272 use self::SizeMetric::*;
273 match self {
274 Px => write!(f, "px"),
275 Pt => write!(f, "pt"),
276 Em => write!(f, "em"),
277 Rem => write!(f, "rem"),
278 In => write!(f, "in"),
279 Cm => write!(f, "cm"),
280 Mm => write!(f, "mm"),
281 Percent => write!(f, "%"),
282 Vw => write!(f, "vw"),
283 Vh => write!(f, "vh"),
284 Vmin => write!(f, "vmin"),
285 Vmax => write!(f, "vmax"),
286 }
287 }
288}
289
290pub fn parse_float_value(input: &str) -> Result<FloatValue, ParseFloatError> {
291 Ok(FloatValue::new(input.trim().parse::<f32>()?))
292}
293
294#[derive(Clone, PartialEq, Eq)]
295#[repr(C, u8)]
296pub enum PercentageParseError {
297 ValueParseErr(crate::props::basic::error::ParseFloatError),
298 NoPercentSign,
299 InvalidUnit(AzString),
300}
301
302impl_debug_as_display!(PercentageParseError);
303
304impl From<ParseFloatError> for PercentageParseError {
305 fn from(e: ParseFloatError) -> Self {
306 PercentageParseError::ValueParseErr(crate::props::basic::error::ParseFloatError::from(e))
307 }
308}
309
310impl_display! { PercentageParseError, {
311 ValueParseErr(e) => format!("\"{}\"", e),
312 NoPercentSign => format!("No percent sign after number"),
313 InvalidUnit(u) => format!("Error parsing percentage: invalid unit \"{}\"", u.as_str()),
314}}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
317#[repr(C, u8)]
318pub enum PercentageParseErrorOwned {
319 ValueParseErr(crate::props::basic::error::ParseFloatError),
320 NoPercentSign,
321 InvalidUnit(AzString),
322}
323
324impl PercentageParseError {
325 pub fn to_contained(&self) -> PercentageParseErrorOwned {
326 match self {
327 Self::ValueParseErr(e) => PercentageParseErrorOwned::ValueParseErr(*e),
328 Self::NoPercentSign => PercentageParseErrorOwned::NoPercentSign,
329 Self::InvalidUnit(u) => PercentageParseErrorOwned::InvalidUnit(u.clone()),
330 }
331 }
332}
333
334impl PercentageParseErrorOwned {
335 pub fn to_shared(&self) -> PercentageParseError {
336 match self {
337 Self::ValueParseErr(e) => PercentageParseError::ValueParseErr(*e),
338 Self::NoPercentSign => PercentageParseError::NoPercentSign,
339 Self::InvalidUnit(u) => PercentageParseError::InvalidUnit(u.clone()),
340 }
341 }
342}
343
344pub fn parse_percentage_value(input: &str) -> Result<PercentageValue, PercentageParseError> {
346 let input = input.trim();
347
348 if input.is_empty() {
349 return Err(PercentageParseError::ValueParseErr(
350 crate::props::basic::error::ParseFloatError::from("empty string".parse::<f32>().unwrap_err()),
351 ));
352 }
353
354 let mut split_pos = 0;
355 let mut found_numeric = false;
356 for (idx, ch) in input.char_indices() {
357 if ch.is_numeric() || ch == '.' || ch == '-' {
358 split_pos = idx;
359 found_numeric = true;
360 }
361 }
362
363 if !found_numeric {
364 return Err(PercentageParseError::ValueParseErr(
365 crate::props::basic::error::ParseFloatError::from("no numeric value".parse::<f32>().unwrap_err()),
366 ));
367 }
368
369 split_pos += 1;
370
371 let unit = input[split_pos..].trim();
372 let mut number = input[..split_pos]
373 .trim()
374 .parse::<f32>()
375 .map_err(|e| PercentageParseError::ValueParseErr(crate::props::basic::error::ParseFloatError::from(e)))?;
376
377 match unit {
378 "" => {
379 number *= 100.0;
380 } "%" => {} other => {
383 return Err(PercentageParseError::InvalidUnit(other.to_string().into()));
384 }
385 }
386
387 Ok(PercentageValue::new(number))
388}
389
390#[cfg(all(test, feature = "parser"))]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_parse_float_value() {
396 assert_eq!(parse_float_value("10").unwrap().get(), 10.0);
397 assert_eq!(parse_float_value("2.5").unwrap().get(), 2.5);
398 assert_eq!(parse_float_value("-50.2").unwrap().get(), -50.2);
399 assert_eq!(parse_float_value(" 0 ").unwrap().get(), 0.0);
400 assert!(parse_float_value("10a").is_err());
401 assert!(parse_float_value("").is_err());
402 }
403
404 #[test]
405 fn test_parse_percentage_value() {
406 assert_eq!(parse_percentage_value("50%").unwrap().normalized(), 0.5);
408 assert_eq!(parse_percentage_value("120%").unwrap().normalized(), 1.2);
409 assert_eq!(parse_percentage_value("-25%").unwrap().normalized(), -0.25);
410 assert_eq!(
411 parse_percentage_value(" 75.5% ").unwrap().normalized(),
412 0.755
413 );
414
415 assert!((parse_percentage_value("0.5").unwrap().normalized() - 0.5).abs() < 1e-6);
417 assert!((parse_percentage_value("1.2").unwrap().normalized() - 1.2).abs() < 1e-6);
418 assert!((parse_percentage_value("1").unwrap().normalized() - 1.0).abs() < 1e-6);
419
420 assert!(matches!(
422 parse_percentage_value("50px").err().unwrap(),
423 PercentageParseError::InvalidUnit(_)
424 ));
425 assert!(parse_percentage_value("fifty%").is_err());
426 assert!(parse_percentage_value("").is_err());
427 }
428
429 #[test]
430 fn test_const_new_fractional_single_digit() {
431 let val = FloatValue::const_new_fractional(1, 5);
433 assert_eq!(val.get(), 1.5);
434
435 let val = FloatValue::const_new_fractional(0, 5);
436 assert_eq!(val.get(), 0.5);
437
438 let val = FloatValue::const_new_fractional(2, 3);
439 assert_eq!(val.get(), 2.3);
440
441 let val = FloatValue::const_new_fractional(0, 0);
442 assert_eq!(val.get(), 0.0);
443
444 let val = FloatValue::const_new_fractional(10, 9);
445 assert_eq!(val.get(), 10.9);
446 }
447
448 #[test]
449 fn test_const_new_fractional_two_digits() {
450 let val = FloatValue::const_new_fractional(0, 83);
452 assert!((val.get() - 0.83).abs() < 0.001);
453
454 let val = FloatValue::const_new_fractional(1, 17);
455 assert!((val.get() - 1.17).abs() < 0.001);
456
457 let val = FloatValue::const_new_fractional(1, 52);
458 assert!((val.get() - 1.52).abs() < 0.001);
459
460 let val = FloatValue::const_new_fractional(0, 33);
461 assert!((val.get() - 0.33).abs() < 0.001);
462
463 let val = FloatValue::const_new_fractional(2, 67);
464 assert!((val.get() - 2.67).abs() < 0.001);
465
466 let val = FloatValue::const_new_fractional(0, 10);
467 assert!((val.get() - 0.10).abs() < 0.001);
468
469 let val = FloatValue::const_new_fractional(0, 99);
470 assert!((val.get() - 0.99).abs() < 0.001);
471 }
472
473 #[test]
474 fn test_const_new_fractional_three_digits() {
475 let val = FloatValue::const_new_fractional(1, 523);
477 assert!((val.get() - 1.523).abs() < 0.001);
478
479 let val = FloatValue::const_new_fractional(0, 123);
480 assert!((val.get() - 0.123).abs() < 0.001);
481
482 let val = FloatValue::const_new_fractional(2, 999);
483 assert!((val.get() - 2.999).abs() < 0.001);
484
485 let val = FloatValue::const_new_fractional(0, 100);
486 assert!((val.get() - 0.100).abs() < 0.001);
487
488 let val = FloatValue::const_new_fractional(5, 1);
489 assert!((val.get() - 5.1).abs() < 0.001);
490 }
491
492 #[test]
493 fn test_const_new_fractional_truncation() {
494 let val = FloatValue::const_new_fractional(0, 5234);
498 assert!((val.get() - 0.523).abs() < 0.001);
499
500 let val = FloatValue::const_new_fractional(1, 12345);
502 assert!((val.get() - 1.123).abs() < 0.001);
503
504 let val = FloatValue::const_new_fractional(1, 123456);
506 assert!((val.get() - 1.123).abs() < 0.001);
507
508 let val = FloatValue::const_new_fractional(0, 9876543);
510 assert!((val.get() - 0.987).abs() < 0.001);
511
512 let val = FloatValue::const_new_fractional(2, 1234567890);
514 assert!((val.get() - 2.123).abs() < 0.001);
515 }
516
517 #[test]
518 fn test_const_new_fractional_negative() {
519 let val = FloatValue::const_new_fractional(-1, 5);
521 assert_eq!(val.get(), -1.5);
522
523 let val = FloatValue::const_new_fractional(0, 83);
524 assert!((val.get() - 0.83).abs() < 0.001);
525
526 let val = FloatValue::const_new_fractional(-2, 123);
527 assert!((val.get() - -2.123).abs() < 0.001);
528
529 let val = FloatValue::const_new_fractional(1, -5);
531 assert_eq!(val.get(), 0.5); let val = FloatValue::const_new_fractional(0, -50);
534 assert!((val.get() - -0.5).abs() < 0.001); }
536
537 #[test]
538 fn test_const_new_fractional_edge_cases() {
539 let val = FloatValue::const_new_fractional(0, 0);
541 assert_eq!(val.get(), 0.0);
542
543 let val = FloatValue::const_new_fractional(100, 5);
545 assert_eq!(val.get(), 100.5);
546
547 let val = FloatValue::const_new_fractional(1000, 99);
548 assert!((val.get() - 1000.99).abs() < 0.001);
549
550 let val = FloatValue::const_new_fractional(0, 999);
552 assert!((val.get() - 0.999).abs() < 0.001);
553
554 let val = FloatValue::const_new_fractional(1, 1);
556 assert!((val.get() - 1.1).abs() < 0.001);
557
558 let val = FloatValue::const_new_fractional(1, 10);
559 assert!((val.get() - 1.10).abs() < 0.001);
560 }
561
562 #[test]
563 fn test_const_new_fractional_ua_css_values() {
564 let val = FloatValue::const_new_fractional(2, 0);
568 assert_eq!(val.get(), 2.0);
569
570 let val = FloatValue::const_new_fractional(1, 5);
572 assert_eq!(val.get(), 1.5);
573
574 let val = FloatValue::const_new_fractional(1, 17);
576 assert!((val.get() - 1.17).abs() < 0.001);
577
578 let val = FloatValue::const_new_fractional(1, 0);
580 assert_eq!(val.get(), 1.0);
581
582 let val = FloatValue::const_new_fractional(0, 83);
584 assert!((val.get() - 0.83).abs() < 0.001);
585
586 let val = FloatValue::const_new_fractional(0, 67);
588 assert!((val.get() - 0.67).abs() < 0.001);
589
590 let val = FloatValue::const_new_fractional(0, 67);
592 assert!((val.get() - 0.67).abs() < 0.001);
593
594 let val = FloatValue::const_new_fractional(0, 83);
596 assert!((val.get() - 0.83).abs() < 0.001);
597
598 let val = FloatValue::const_new_fractional(1, 33);
600 assert!((val.get() - 1.33).abs() < 0.001);
601
602 let val = FloatValue::const_new_fractional(1, 67);
604 assert!((val.get() - 1.67).abs() < 0.001);
605
606 let val = FloatValue::const_new_fractional(2, 33);
608 assert!((val.get() - 2.33).abs() < 0.001);
609 }
610
611 #[test]
612 fn test_const_new_fractional_consistency() {
613 let const_val = FloatValue::const_new_fractional(1, 5);
616 let runtime_val = FloatValue::new(1.5);
617 assert_eq!(const_val.get(), runtime_val.get());
618
619 let const_val = FloatValue::const_new_fractional(0, 83);
620 let runtime_val = FloatValue::new(0.83);
621 assert!((const_val.get() - runtime_val.get()).abs() < 0.001);
622
623 let const_val = FloatValue::const_new_fractional(1, 523);
624 let runtime_val = FloatValue::new(1.523);
625 assert!((const_val.get() - runtime_val.get()).abs() < 0.001);
626
627 let const_val = FloatValue::const_new_fractional(2, 99);
628 let runtime_val = FloatValue::new(2.99);
629 assert!((const_val.get() - runtime_val.get()).abs() < 0.001);
630 }
631}