1use crate::corety::AzString;
7
8#[derive(Debug, Copy, Clone, Eq, PartialEq)]
10pub struct InvalidValueErr<'a>(pub &'a str);
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[repr(C)]
15pub struct InvalidValueErrOwned {
16 pub value: AzString,
17}
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
24#[repr(C)]
25pub enum ParseFloatError {
26 Empty,
28 Invalid,
30}
31
32impl ParseFloatError {
33 fn from_std(e: &core::num::ParseFloatError) -> Self {
35 let empty_err = "".parse::<f32>().unwrap_err();
38 if *e == empty_err {
39 Self::Empty
40 } else {
41 Self::Invalid
42 }
43 }
44
45 #[must_use] pub fn to_std(&self) -> core::num::ParseFloatError {
47 match self {
48 Self::Empty => "".parse::<f32>().unwrap_err(),
49 Self::Invalid => "x".parse::<f32>().unwrap_err(),
50 }
51 }
52}
53
54impl core::fmt::Display for ParseFloatError {
55 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 match self {
57 Self::Empty => write!(f, "cannot parse float from empty string"),
58 Self::Invalid => write!(f, "invalid float literal"),
59 }
60 }
61}
62
63impl From<core::num::ParseFloatError> for ParseFloatError {
64 fn from(e: core::num::ParseFloatError) -> Self {
65 Self::from_std(&e)
66 }
67}
68
69#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
74#[repr(C)]
75pub enum ParseIntError {
76 Empty,
78 InvalidDigit,
80 PosOverflow,
82 NegOverflow,
84 Zero,
86}
87
88impl ParseIntError {
89 const fn from_std(e: &core::num::ParseIntError) -> Self {
91 use core::num::IntErrorKind;
92 match e.kind() {
93 IntErrorKind::Empty => Self::Empty,
94 IntErrorKind::PosOverflow => Self::PosOverflow,
95 IntErrorKind::NegOverflow => Self::NegOverflow,
96 IntErrorKind::Zero => Self::Zero,
97 _ => Self::InvalidDigit, }
99 }
100
101 #[must_use] pub fn to_std(&self) -> core::num::ParseIntError {
103 match self {
104 Self::Empty => "".parse::<i32>().unwrap_err(),
105 Self::InvalidDigit => "x".parse::<i32>().unwrap_err(),
106 Self::PosOverflow => "99999999999999999999".parse::<i32>().unwrap_err(),
107 Self::NegOverflow => "-99999999999999999999".parse::<i32>().unwrap_err(),
108 Self::Zero => {
109 "x".parse::<i32>().unwrap_err()
112 }
113 }
114 }
115}
116
117impl core::fmt::Display for ParseIntError {
118 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119 match self {
120 Self::Empty => write!(f, "cannot parse integer from empty string"),
121 Self::InvalidDigit => write!(f, "invalid digit found in string"),
122 Self::PosOverflow => write!(f, "number too large to fit in target type"),
123 Self::NegOverflow => write!(f, "number too small to fit in target type"),
124 Self::Zero => write!(f, "number would be zero for non-zero type"),
125 }
126 }
127}
128
129impl From<core::num::ParseIntError> for ParseIntError {
130 fn from(e: core::num::ParseIntError) -> Self {
131 Self::from_std(&e)
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
138#[repr(C)]
139pub struct ParseFloatErrorWithInput {
140 pub error: ParseFloatError,
141 pub input: AzString,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
146#[repr(C)]
147pub struct WrongComponentCountError {
148 pub expected: usize,
149 pub got: usize,
150 pub input: AzString,
151}
152
153impl InvalidValueErr<'_> {
154 #[must_use] pub fn to_contained(&self) -> InvalidValueErrOwned {
155 InvalidValueErrOwned { value: self.0.to_string().into() }
156 }
157}
158
159impl InvalidValueErrOwned {
160 #[must_use] pub fn to_shared(&self) -> InvalidValueErr<'_> {
161 InvalidValueErr(self.value.as_str())
162 }
163}
164
165#[cfg(test)]
166#[allow(clippy::too_many_lines)]
167mod autotest_generated {
168 use core::num::IntErrorKind;
169 use std::{
170 collections::hash_map::DefaultHasher,
171 hash::{Hash, Hasher},
172 };
173
174 use super::*;
175
176 fn float_kind<T>(s: &str) -> ParseFloatError
183 where
184 T: core::str::FromStr<Err = core::num::ParseFloatError>,
185 {
186 match s.parse::<T>() {
187 Ok(_) => panic!("expected {s:?} to FAIL to parse as a float"),
188 Err(e) => ParseFloatError::from_std(&e),
189 }
190 }
191
192 fn int_kind<T>(s: &str) -> ParseIntError
193 where
194 T: core::str::FromStr<Err = core::num::ParseIntError>,
195 {
196 match s.parse::<T>() {
197 Ok(_) => panic!("expected {s:?} to FAIL to parse as an integer"),
198 Err(e) => ParseIntError::from_std(&e),
199 }
200 }
201
202 fn std_float_err(s: &str) -> core::num::ParseFloatError {
203 s.parse::<f32>().expect_err("input should not parse")
204 }
205
206 fn std_int_err(s: &str) -> core::num::ParseIntError {
207 s.parse::<i32>().expect_err("input should not parse")
208 }
209
210 fn hash_of<T: Hash>(v: &T) -> u64 {
211 let mut h = DefaultHasher::new();
212 v.hash(&mut h);
213 h.finish()
214 }
215
216 const ALL_FLOAT: [ParseFloatError; 2] = [ParseFloatError::Empty, ParseFloatError::Invalid];
217
218 const ALL_INT: [ParseIntError; 5] = [
219 ParseIntError::Empty,
220 ParseIntError::InvalidDigit,
221 ParseIntError::PosOverflow,
222 ParseIntError::NegOverflow,
223 ParseIntError::Zero,
224 ];
225
226 #[test]
231 fn float_from_std_empty_string_maps_to_empty() {
232 assert_eq!(float_kind::<f32>(""), ParseFloatError::Empty);
233 assert_eq!(float_kind::<f64>(""), ParseFloatError::Empty);
237 }
238
239 #[test]
240 fn float_from_std_blank_input_is_invalid_not_empty() {
241 for s in [
244 " ",
245 " ",
246 "\t",
247 "\n",
248 "\r\n",
249 "\u{a0}", "\u{feff}", "\u{200b}", "\u{0}", ] {
254 assert_eq!(
255 float_kind::<f32>(s),
256 ParseFloatError::Invalid,
257 "blank-ish input {s:?} must be Invalid, not Empty"
258 );
259 }
260 }
261
262 #[test]
263 fn float_from_std_malformed_inputs_are_invalid() {
264 for s in [
265 "x", ".", "-", "+", "e", "e5", "5e", "1.2.3", "0x1f", "1,5", "--1", "++1", "1 ", " 1",
266 "1_0", "NaNx", "infinit", "1/2", "abc", "1e", "1e+", "-.",
267 ] {
268 assert_eq!(
269 float_kind::<f32>(s),
270 ParseFloatError::Invalid,
271 "malformed input {s:?} should be Invalid"
272 );
273 }
274 }
275
276 #[test]
277 fn float_from_std_non_ascii_digits_are_invalid() {
278 for s in [
279 "١٢٣", "123", "½", "😀",
283 "٣.٥",
284 "1\u{301}", "Ⅻ", ] {
287 assert_eq!(
288 float_kind::<f32>(s),
289 ParseFloatError::Invalid,
290 "unicode input {s:?} should be Invalid"
291 );
292 }
293 }
294
295 #[test]
296 fn float_from_std_huge_malformed_input_does_not_panic_or_hang() {
297 let mut huge = "9".repeat(100_000);
298 huge.push('x');
299 assert_eq!(float_kind::<f32>(&huge), ParseFloatError::Invalid);
300
301 let mut zeros = "0".repeat(100_000);
303 zeros.push_str("..");
304 assert_eq!(float_kind::<f32>(&zeros), ParseFloatError::Invalid);
305 }
306
307 #[test]
308 fn float_from_impl_agrees_with_from_std() {
309 for s in ["", " ", "x", "1.2.3", "😀"] {
310 let a: ParseFloatError = std_float_err(s).into();
311 let b = ParseFloatError::from_std(&std_float_err(s));
312 assert_eq!(a, b, "From<> and from_std disagree for {s:?}");
313 }
314 }
315
316 #[test]
321 fn float_magnitude_overflow_saturates_to_infinity_instead_of_erroring() {
322 assert!(
325 "1e400".parse::<f32>().expect("saturates, does not error").is_infinite(),
326 "huge positive exponent should saturate to +inf"
327 );
328 assert!("-1e400".parse::<f32>().expect("saturates").is_sign_negative());
329 assert_eq!("1e-400".parse::<f32>().expect("underflows to zero"), 0.0);
330
331 let huge = "9".repeat(100_000);
332 assert!(huge.parse::<f32>().expect("saturates").is_infinite());
333 }
334
335 #[test]
336 fn float_nan_and_inf_literals_parse_and_never_error() {
337 assert!("nan".parse::<f32>().expect("nan is valid").is_nan());
338 assert!("NaN".parse::<f32>().expect("NaN is valid").is_nan());
339 assert!("inf".parse::<f32>().expect("inf is valid").is_infinite());
340 assert!("infinity".parse::<f32>().expect("infinity is valid").is_infinite());
341 assert!("-inf".parse::<f32>().expect("-inf is valid").is_sign_negative());
342 assert!("-0".parse::<f32>().expect("-0 is valid").is_sign_negative());
343 }
344
345 #[test]
350 fn float_to_std_returns_the_matching_std_error() {
351 assert_eq!(ParseFloatError::Empty.to_std(), std_float_err(""));
352 assert_eq!(ParseFloatError::Invalid.to_std(), std_float_err("x"));
353 }
354
355 #[test]
356 fn float_to_std_variants_stay_distinct() {
357 assert_ne!(ParseFloatError::Empty.to_std(), ParseFloatError::Invalid.to_std());
359 }
360
361 #[test]
362 fn float_to_std_is_deterministic() {
363 for v in ALL_FLOAT {
364 assert_eq!(v.to_std(), v.to_std(), "to_std() must be stable for {v:?}");
365 }
366 }
367
368 #[test]
369 fn float_round_trip_encode_decode_is_identity() {
370 for v in ALL_FLOAT {
371 assert_eq!(ParseFloatError::from_std(&v.to_std()), v, "round-trip lost {v:?}");
372 assert_eq!(ParseFloatError::from(v.to_std()), v);
373 }
374 }
375
376 #[test]
381 fn int_from_std_empty_string_maps_to_empty_for_every_width() {
382 assert_eq!(int_kind::<i8>(""), ParseIntError::Empty);
383 assert_eq!(int_kind::<u8>(""), ParseIntError::Empty);
384 assert_eq!(int_kind::<i32>(""), ParseIntError::Empty);
385 assert_eq!(int_kind::<u128>(""), ParseIntError::Empty);
386 assert_eq!(int_kind::<usize>(""), ParseIntError::Empty);
387 assert_eq!(int_kind::<isize>(""), ParseIntError::Empty);
388 }
389
390 #[test]
391 fn int_from_std_malformed_inputs_are_invalid_digit() {
392 for s in [
393 "x", " ", " ", "\t", "+", "-", "+-1", "--1", "1 ", " 1", "1_000", "0x10", "1.0",
394 "1e3", "abc", "\u{0}", "1\u{0}", "٣", "123", "😀", "½", ",", "1,000",
395 ] {
396 assert_eq!(
397 int_kind::<i32>(s),
398 ParseIntError::InvalidDigit,
399 "malformed input {s:?} should be InvalidDigit"
400 );
401 }
402 }
403
404 #[test]
405 fn int_from_std_negative_into_unsigned_is_invalid_digit_not_neg_overflow() {
406 assert_eq!(int_kind::<u32>("-1"), ParseIntError::InvalidDigit);
409 assert_eq!(int_kind::<u8>("-0"), ParseIntError::InvalidDigit);
410 assert_eq!(int_kind::<u128>("-99999999999999999999999999"), ParseIntError::InvalidDigit);
411 }
412
413 #[test]
414 fn int_from_std_positive_overflow_boundaries() {
415 assert_eq!(i32::MAX.to_string().parse::<i32>(), Ok(i32::MAX));
417 assert_eq!(int_kind::<i32>("2147483648"), ParseIntError::PosOverflow);
418 assert_eq!(u8::MAX.to_string().parse::<u8>(), Ok(u8::MAX));
419 assert_eq!(int_kind::<u8>("256"), ParseIntError::PosOverflow);
420 assert_eq!(i8::MAX.to_string().parse::<i8>(), Ok(i8::MAX));
421 assert_eq!(int_kind::<i8>("128"), ParseIntError::PosOverflow);
422 assert_eq!(
423 int_kind::<u128>("340282366920938463463374607431768211456"),
424 ParseIntError::PosOverflow
425 );
426 }
427
428 #[test]
429 fn int_from_std_negative_overflow_boundaries() {
430 assert_eq!(i32::MIN.to_string().parse::<i32>(), Ok(i32::MIN));
431 assert_eq!(int_kind::<i32>("-2147483649"), ParseIntError::NegOverflow);
432 assert_eq!(i8::MIN.to_string().parse::<i8>(), Ok(i8::MIN));
433 assert_eq!(int_kind::<i8>("-129"), ParseIntError::NegOverflow);
434 assert_eq!(int_kind::<i128>("-99999999999999999999999999999999999999999"), ParseIntError::NegOverflow);
435 }
436
437 #[test]
438 fn int_from_std_huge_digit_runs_overflow_without_panic() {
439 let huge = "9".repeat(10_000);
440 assert_eq!(int_kind::<i32>(&huge), ParseIntError::PosOverflow);
441 assert_eq!(int_kind::<u128>(&huge), ParseIntError::PosOverflow);
442
443 let huge_neg = format!("-{huge}");
444 assert_eq!(int_kind::<i64>(&huge_neg), ParseIntError::NegOverflow);
445 }
446
447 #[test]
448 fn int_leading_zeros_do_not_produce_a_false_overflow() {
449 let padded = format!("{}5", "0".repeat(10_000));
452 assert_eq!(padded.parse::<i32>(), Ok(5));
453 assert_eq!("0000000000000000000000000000005".parse::<i32>(), Ok(5));
454 }
455
456 #[test]
457 fn int_from_std_zero_variant_is_reachable_via_nonzero_types() {
458 assert_eq!(int_kind::<core::num::NonZeroU8>("0"), ParseIntError::Zero);
461 assert_eq!(int_kind::<core::num::NonZeroI32>("0"), ParseIntError::Zero);
462 assert_eq!(int_kind::<core::num::NonZeroUsize>("0"), ParseIntError::Zero);
463 assert_eq!(int_kind::<core::num::NonZeroU8>(""), ParseIntError::Empty);
465 assert_eq!(int_kind::<core::num::NonZeroU8>("x"), ParseIntError::InvalidDigit);
466 assert_eq!(int_kind::<core::num::NonZeroU8>("256"), ParseIntError::PosOverflow);
467 }
468
469 #[test]
470 fn int_from_impl_agrees_with_from_std() {
471 for s in ["", "x", "99999999999999999999", "-99999999999999999999", "😀"] {
472 let a: ParseIntError = std_int_err(s).into();
473 let b = ParseIntError::from_std(&std_int_err(s));
474 assert_eq!(a, b, "From<> and from_std disagree for {s:?}");
475 }
476 }
477
478 #[test]
483 fn int_to_std_maps_each_variant_onto_the_expected_std_kind() {
484 assert!(matches!(ParseIntError::Empty.to_std().kind(), IntErrorKind::Empty));
485 assert!(matches!(ParseIntError::InvalidDigit.to_std().kind(), IntErrorKind::InvalidDigit));
486 assert!(matches!(ParseIntError::PosOverflow.to_std().kind(), IntErrorKind::PosOverflow));
487 assert!(matches!(ParseIntError::NegOverflow.to_std().kind(), IntErrorKind::NegOverflow));
488 assert!(matches!(ParseIntError::Zero.to_std().kind(), IntErrorKind::InvalidDigit));
490 }
491
492 #[test]
493 fn int_to_std_is_deterministic() {
494 for v in ALL_INT {
495 assert_eq!(v.to_std(), v.to_std(), "to_std() must be stable for {v:?}");
496 }
497 }
498
499 #[test]
500 fn int_round_trip_encode_decode_is_identity_except_for_zero() {
501 for v in [
502 ParseIntError::Empty,
503 ParseIntError::InvalidDigit,
504 ParseIntError::PosOverflow,
505 ParseIntError::NegOverflow,
506 ] {
507 assert_eq!(ParseIntError::from_std(&v.to_std()), v, "round-trip lost {v:?}");
508 assert_eq!(ParseIntError::from(v.to_std()), v);
509 }
510
511 assert_eq!(
515 ParseIntError::from_std(&ParseIntError::Zero.to_std()),
516 ParseIntError::InvalidDigit,
517 "Zero round-trip is documented as lossy"
518 );
519 }
520
521 #[test]
522 fn int_to_std_variants_stay_distinct_where_they_must() {
523 let empty = ParseIntError::Empty.to_std();
524 let invalid = ParseIntError::InvalidDigit.to_std();
525 let pos = ParseIntError::PosOverflow.to_std();
526 let neg = ParseIntError::NegOverflow.to_std();
527 assert_ne!(empty, invalid);
528 assert_ne!(invalid, pos);
529 assert_ne!(pos, neg);
530 assert_ne!(empty, neg);
531 assert_eq!(ParseIntError::Zero.to_std(), invalid);
533 }
534
535 #[test]
540 fn display_output_is_non_empty_and_unique_per_variant() {
541 let float_msgs: Vec<String> = ALL_FLOAT.iter().map(ToString::to_string).collect();
542 for m in &float_msgs {
543 assert!(!m.is_empty(), "float Display must not be empty");
544 }
545 assert_ne!(float_msgs[0], float_msgs[1], "float variants must be distinguishable");
546
547 let int_msgs: Vec<String> = ALL_INT.iter().map(ToString::to_string).collect();
548 for m in &int_msgs {
549 assert!(!m.is_empty(), "int Display must not be empty");
550 }
551 for i in 0..int_msgs.len() {
552 for j in (i + 1)..int_msgs.len() {
553 assert_ne!(int_msgs[i], int_msgs[j], "int variants {i}/{j} share a message");
554 }
555 }
556 }
557
558 #[test]
559 fn display_mirrors_the_std_error_messages() {
560 assert_eq!(ParseFloatError::Empty.to_string(), std_float_err("").to_string());
563 assert_eq!(ParseFloatError::Invalid.to_string(), std_float_err("x").to_string());
564
565 assert_eq!(ParseIntError::Empty.to_string(), std_int_err("").to_string());
566 assert_eq!(ParseIntError::InvalidDigit.to_string(), std_int_err("x").to_string());
567 assert_eq!(
568 ParseIntError::PosOverflow.to_string(),
569 std_int_err("99999999999999999999").to_string()
570 );
571 assert_eq!(
572 ParseIntError::NegOverflow.to_string(),
573 std_int_err("-99999999999999999999").to_string()
574 );
575 let std_zero = "0"
578 .parse::<core::num::NonZeroU8>()
579 .expect_err("parsing 0 as NonZeroU8 must fail");
580 assert!(matches!(std_zero.kind(), IntErrorKind::Zero));
581 assert_eq!(ParseIntError::Zero.to_string(), std_zero.to_string());
582 }
583
584 #[test]
585 fn display_with_formatter_flags_does_not_panic() {
586 for v in ALL_INT {
587 let msg = v.to_string();
588 let padded = format!("{v:>60}");
589 assert!(!padded.is_empty());
590 assert!(padded.contains(&msg), "padding must not corrupt the message");
591 assert!(!format!("{v:.3}").is_empty());
593 assert!(!format!("{v:*^10}").is_empty());
594 assert!(!format!("{v:#?}").is_empty());
595 }
596 for v in ALL_FLOAT {
597 assert!(!format!("{v:>60}").is_empty());
598 assert!(!format!("{v:.1}").is_empty());
599 assert!(!format!("{v:?}").is_empty());
600 }
601 }
602
603 #[test]
604 fn debug_output_names_the_variant() {
605 assert_eq!(format!("{:?}", ParseFloatError::Empty), "Empty");
606 assert_eq!(format!("{:?}", ParseIntError::PosOverflow), "PosOverflow");
607 assert_eq!(format!("{:?}", ParseIntError::Zero), "Zero");
608 }
609
610 #[test]
615 fn error_enums_have_consistent_eq_hash_and_ord() {
616 for (i, a) in ALL_INT.iter().enumerate() {
617 assert_eq!(hash_of(a), hash_of(&ALL_INT[i]), "equal values must hash equal");
618 for (j, b) in ALL_INT.iter().enumerate() {
619 assert_eq!(a == b, i == j, "only identical variants may compare equal");
620 assert_eq!(a.cmp(b), i.cmp(&j), "Ord must follow declaration order");
621 }
622 }
623 assert!(ParseFloatError::Empty < ParseFloatError::Invalid);
624 assert_eq!(hash_of(&ParseFloatError::Empty), hash_of(&ParseFloatError::Empty));
625 assert_ne!(ParseFloatError::Empty, ParseFloatError::Invalid);
626 }
627
628 #[test]
629 fn error_enums_are_copy_and_survive_a_sort() {
630 let mut v = [
631 ParseIntError::Zero,
632 ParseIntError::Empty,
633 ParseIntError::NegOverflow,
634 ParseIntError::InvalidDigit,
635 ParseIntError::PosOverflow,
636 ];
637 v.sort_unstable();
638 assert_eq!(v, ALL_INT);
639
640 let a = ParseIntError::Zero;
641 let b = a; assert_eq!(a, b);
643 }
644
645 #[test]
650 fn invalid_value_err_round_trips_through_owned() {
651 for s in [
652 "",
653 "a",
654 "border-radius",
655 " ",
656 "\n\t",
657 "börder-radiüs 😀",
658 "٣.٥",
659 "\u{feff}leading-bom",
660 "trailing-nul\u{0}",
661 "a\u{0}b",
662 ] {
663 let shared = InvalidValueErr(s);
664 let owned = shared.to_contained();
665 assert_eq!(owned.value.as_str(), s, "to_contained lost {s:?}");
666 assert_eq!(owned.to_shared(), shared, "round-trip changed {s:?}");
667 assert_eq!(owned.to_shared().0, s);
668 }
669 }
670
671 #[test]
672 fn invalid_value_err_empty_string_is_not_confused_with_default() {
673 let owned = InvalidValueErr("").to_contained();
674 assert_eq!(owned.value, AzString::default());
675 assert!(owned.value.as_str().is_empty());
676 assert_eq!(owned.to_shared(), InvalidValueErr(""));
677 assert_eq!(owned, InvalidValueErrOwned { value: AzString::default() });
678 }
679
680 #[test]
681 fn invalid_value_err_preserves_interior_nul_bytes() {
682 let s = "a\u{0}b";
685 let owned = InvalidValueErr(s).to_contained();
686 assert_eq!(owned.value.as_bytes(), b"a\0b");
687 assert_eq!(owned.value.as_str().len(), 3);
688 assert_eq!(owned.to_shared().0.len(), 3);
689 }
690
691 #[test]
692 fn to_contained_deep_copies_and_outlives_its_source() {
693 let owned = {
694 let src = String::from("temporary-buffer");
695 let copied = InvalidValueErr(src.as_str()).to_contained();
696 assert!(
697 !core::ptr::eq(copied.value.as_str().as_ptr(), src.as_str().as_ptr()),
698 "to_contained must copy, not alias the borrowed input"
699 );
700 drop(src);
701 copied
702 };
703 assert_eq!(owned.value.as_str(), "temporary-buffer");
704 }
705
706 #[test]
707 fn to_shared_borrows_the_owned_buffer_without_copying() {
708 let owned = InvalidValueErr("shared-buffer").to_contained();
709 let shared = owned.to_shared();
710 assert!(
711 core::ptr::eq(shared.0.as_ptr(), owned.value.as_str().as_ptr()),
712 "to_shared must borrow the existing buffer"
713 );
714 assert_eq!(owned.to_shared(), owned.to_shared());
716 }
717
718 #[test]
719 fn invalid_value_err_handles_a_huge_payload() {
720 let big = "ü".repeat(100_000); let owned = InvalidValueErr(big.as_str()).to_contained();
722 assert_eq!(owned.value.as_str().len(), big.len());
723 assert_eq!(owned.value.as_bytes().len(), 200_000);
724 assert_eq!(owned.to_shared().0, big.as_str());
725 assert_eq!(owned.clone(), owned);
726 }
727
728 #[test]
729 fn invalid_value_err_owned_equality_is_by_content() {
730 let a = InvalidValueErr("x").to_contained();
731 let b = InvalidValueErrOwned { value: AzString::from("x") };
732 let c = InvalidValueErrOwned { value: AzString::from("y") };
733 assert_eq!(a, b);
734 assert_ne!(a, c);
735 assert_eq!(a.clone(), a);
736 assert_eq!(a.to_shared(), b.to_shared());
737 }
738
739 #[test]
744 fn parse_float_error_with_input_keeps_error_and_input_together() {
745 let input = "1.2.3";
746 let err = ParseFloatErrorWithInput {
747 error: ParseFloatError::from(std_float_err(input)),
748 input: AzString::from(input),
749 };
750 assert_eq!(err.error, ParseFloatError::Invalid);
751 assert_eq!(err.input.as_str(), input);
752 assert_eq!(err.clone(), err);
753
754 let empty = ParseFloatErrorWithInput {
755 error: ParseFloatError::from(std_float_err("")),
756 input: AzString::default(),
757 };
758 assert_eq!(empty.error, ParseFloatError::Empty);
759 assert!(empty.input.as_str().is_empty());
760 assert_ne!(empty, err);
761 assert!(!format!("{err:?}").is_empty());
762 }
763
764 #[test]
765 fn wrong_component_count_error_survives_usize_extremes() {
766 let e = WrongComponentCountError {
767 expected: usize::MAX,
768 got: 0,
769 input: AzString::from("rgba(1)"),
770 };
771 assert_eq!(e.expected, usize::MAX);
772 assert_eq!(e.got, 0);
773 assert_eq!(e.input.as_str(), "rgba(1)");
774 assert_eq!(e.clone(), e);
775 assert!(!format!("{e:?}").is_empty());
776
777 let same_but_got_max = WrongComponentCountError {
778 expected: usize::MAX,
779 got: usize::MAX,
780 input: AzString::from("rgba(1)"),
781 };
782 assert_ne!(e, same_but_got_max, "`got` participates in equality");
783
784 let zeroed = WrongComponentCountError {
786 expected: 0,
787 got: 0,
788 input: AzString::default(),
789 };
790 assert_eq!(zeroed.expected, zeroed.got);
791 assert!(zeroed.input.as_str().is_empty());
792 }
793}