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