1use core::str::FromStr;
6
7use internals::error::InputString;
8
9#[rustfmt::skip] #[doc(no_inline)]
11pub use self::error::{ParseIntError, PrefixedHexError, UnprefixedHexError};
12
13pub trait Integer:
17 FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized + sealed::Sealed
18{
19}
20
21macro_rules! impl_integer {
22 ($($type:ty),* $(,)?) => {
23 $(
24 impl Integer for $type {}
25 impl sealed::Sealed for $type {}
26 )*
27 }
28}
29
30impl_integer!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
31
32mod sealed {
33 pub trait Sealed {}
35}
36
37#[inline]
54pub fn int_from_str<T: Integer>(s: &str) -> Result<T, ParseIntError> { int(s) }
55
56#[inline]
62#[cfg(feature = "alloc")]
63pub fn int_from_string<T: Integer>(s: alloc::string::String) -> Result<T, ParseIntError> { int(s) }
64
65#[inline]
71#[cfg(feature = "alloc")]
72pub fn int_from_box<T: Integer>(s: alloc::boxed::Box<str>) -> Result<T, ParseIntError> { int(s) }
73
74fn int<T: Integer, S: AsRef<str> + Into<InputString>>(s: S) -> Result<T, ParseIntError> {
76 s.as_ref().parse().map_err(|error| {
77 ParseIntError {
78 input: s.into(),
79 bits: u8::try_from(core::mem::size_of::<T>() * 8).expect("max is 128 bits for u128"),
80 is_signed: T::try_from(-1i8).is_ok(),
84 source: error,
85 }
86 })
87}
88
89macro_rules! impl_parse_str_from_int_infallible {
114 ($to:ident, $inner:ident, $fn:ident) => {
115 impl $crate::_export::_core::str::FromStr for $to {
116 type Err = $crate::parse_int::ParseIntError;
117
118 #[inline]
119 fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
120 $crate::_export::_core::convert::TryFrom::try_from(s)
121 }
122 }
123
124 impl $crate::_export::_core::convert::TryFrom<&str> for $to {
125 type Error = $crate::parse_int::ParseIntError;
126
127 #[inline]
128 fn try_from(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Error> {
129 $crate::parse_int::int_from_str::<$inner>(s).map($to::$fn)
130 }
131 }
132
133 #[cfg(feature = "alloc")]
134 impl $crate::_export::_core::convert::TryFrom<alloc::string::String> for $to {
135 type Error = $crate::parse_int::ParseIntError;
136
137 #[inline]
138 fn try_from(
139 s: alloc::string::String,
140 ) -> $crate::_export::_core::result::Result<Self, Self::Error> {
141 $crate::parse_int::int_from_string::<$inner>(s).map($to::$fn)
142 }
143 }
144
145 #[cfg(feature = "alloc")]
146 impl $crate::_export::_core::convert::TryFrom<alloc::boxed::Box<str>> for $to {
147 type Error = $crate::parse_int::ParseIntError;
148
149 #[inline]
150 fn try_from(
151 s: alloc::boxed::Box<str>,
152 ) -> $crate::_export::_core::result::Result<Self, Self::Error> {
153 $crate::parse_int::int_from_box::<$inner>(s).map($to::$fn)
154 }
155 }
156 };
157}
158pub(crate) use impl_parse_str_from_int_infallible;
159
160macro_rules! impl_parse_str {
182 ($to:ty, $err:ty, $inner_fn:expr) => {
183 $crate::parse_int::impl_tryfrom_str!(&str, $to, $err, $inner_fn);
184 #[cfg(feature = "alloc")]
185 $crate::parse_int::impl_tryfrom_str!(alloc::string::String, $to, $err, $inner_fn; alloc::boxed::Box<str>, $to, $err, $inner_fn);
186
187 impl $crate::_export::_core::str::FromStr for $to {
188 type Err = $err;
189
190 #[inline]
191 fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
192 $inner_fn(s)
193 }
194 }
195 }
196}
197pub(crate) use impl_parse_str;
198
199macro_rules! impl_tryfrom_str {
201 ($($from:ty, $to:ty, $err:ty, $inner_fn:expr);*) => {
202 $(
203 impl $crate::_export::_core::convert::TryFrom<$from> for $to {
204 type Error = $err;
205
206 #[inline]
207 fn try_from(s: $from) -> $crate::_export::_core::result::Result<Self, Self::Error> {
208 $inner_fn(s)
209 }
210 }
211 )*
212 }
213}
214pub(crate) use impl_tryfrom_str;
215
216#[inline]
222pub fn hex_remove_prefix(s: &str) -> Result<&str, PrefixedHexError> {
223 if let Some(checked) = s.strip_prefix("0x") {
224 Ok(checked)
225 } else if let Some(checked) = s.strip_prefix("0X") {
226 Ok(checked)
227 } else {
228 Err(PrefixedHexError(error::PrefixedHexErrorInner::MissingPrefix(
229 error::MissingPrefixError::new(s),
230 )))
231 }
232}
233
234#[inline]
240pub fn hex_check_unprefixed(s: &str) -> Result<&str, UnprefixedHexError> {
241 if s.starts_with("0x") || s.starts_with("0X") {
242 return Err(UnprefixedHexError(error::UnprefixedHexErrorInner::ContainsPrefix(
243 error::ContainsPrefixError::new(s),
244 )));
245 }
246 Ok(s)
247}
248
249macro_rules! parse_hex_for {
251 (
252 $int_type:ty, $bits:literal;
253 fn $any_hex_fn:ident();
254 fn $prefix_hex_fn:ident();
255 fn $unprefix_hex_fn:ident();
256 fn $uncheck_hex_fn:ident();
257 ) => {
258 #[doc = "Parses a `"]
259 #[doc = stringify!($int_type)]
260 #[doc = "` from a hex string.\n\n"]
261 #[doc = "Input string may or may not contain a `0x` (or `0X`) prefix.\n\n"]
262 #[doc = "# Errors\n\nIf the input string is not a valid hex encoding of a `"]
263 #[doc = stringify!($int_type)]
264 #[doc = "`."]
265 #[inline]
266 pub fn $any_hex_fn(s: &str) -> Result<$int_type, ParseIntError> {
267 let unchecked = hex_remove_optional_prefix(s);
268 $uncheck_hex_fn(unchecked)
269 }
270
271 #[doc = "Parses a `"]
272 #[doc = stringify!($int_type)]
273 #[doc = "` from a prefixed hex string.\n\n"]
274 #[doc = "# Errors\n\n"]
275 #[doc = "- If the input string does not contain a `0x` (or `0X`) prefix.\n"]
276 #[doc = "- If the input string is not a valid hex encoding of a `"]
277 #[doc = stringify!($int_type)]
278 #[doc = "`."]
279 #[inline]
280 pub fn $prefix_hex_fn(s: &str) -> Result<$int_type, PrefixedHexError> {
281 let checked = hex_remove_prefix(s)?;
282 $uncheck_hex_fn(checked)
283 .map_err(error::PrefixedHexErrorInner::ParseInt)
284 .map_err(PrefixedHexError)
285 }
286
287 #[doc = "Parses a `"]
288 #[doc = stringify!($int_type)]
289 #[doc = "` from an unprefixed hex string.\n\n"]
290 #[doc = "# Errors\n\n"]
291 #[doc = "- If the input string contains a `0x` (or `0X`) prefix.\n"]
292 #[doc = "- If the input string is not a valid hex encoding of a `"]
293 #[doc = stringify!($int_type)]
294 #[doc = "`."]
295 #[inline]
296 pub fn $unprefix_hex_fn(s: &str) -> Result<$int_type, UnprefixedHexError> {
297 let checked = hex_check_unprefixed(s)?;
298 $uncheck_hex_fn(checked)
299 .map_err(error::UnprefixedHexErrorInner::ParseInt)
300 .map_err(UnprefixedHexError)
301 }
302
303 #[doc = "Parses a `"]
304 #[doc = stringify!($int_type)]
305 #[doc = "` from an unprefixed hex string without first checking for a prefix.\n\n"]
306 #[doc = "# Errors\n\n"]
307 #[doc = "- If the input string contains a `0x` (or `0X`) prefix,"]
308 #[doc = " returns `InvalidDigit` due to the `x`.\n"]
309 #[doc = "- If the input string is not a valid hex encoding of a `"]
310 #[doc = stringify!($int_type)]
311 #[doc = "`."]
312 #[inline]
313 pub fn $uncheck_hex_fn(s: &str) -> Result<$int_type, ParseIntError> {
314 <$int_type>::from_str_radix(s, 16).map_err(|error| ParseIntError {
315 input: s.into(),
316 bits: $bits,
317 is_signed: false,
318 source: error,
319 })
320 }
321 };
322}
323
324parse_hex_for!(
325 u16, 16;
326 fn hex_u16();
327 fn hex_u16_prefixed();
328 fn hex_u16_unprefixed();
329 fn hex_u16_unchecked();
330);
331parse_hex_for!(
332 u32, 32;
333 fn hex_u32();
334 fn hex_u32_prefixed();
335 fn hex_u32_unprefixed();
336 fn hex_u32_unchecked();
337);
338parse_hex_for!(
339 u64, 64;
340 fn hex_u64();
341 fn hex_u64_prefixed();
342 fn hex_u64_unprefixed();
343 fn hex_u64_unchecked();
344);
345parse_hex_for!(
346 u128, 128;
347 fn hex_u128();
348 fn hex_u128_prefixed();
349 fn hex_u128_unprefixed();
350 fn hex_u128_unchecked();
351);
352
353pub(crate) fn hex_u256_prefixed(s: &str) -> Result<crate::pow::U256, PrefixedHexError> {
354 let checked = hex_remove_prefix(s)?;
355 hex_u256_unchecked(checked)
356 .map_err(error::PrefixedHexErrorInner::ParseInt)
357 .map_err(PrefixedHexError)
358}
359
360pub(crate) fn hex_u256_unprefixed(s: &str) -> Result<crate::pow::U256, UnprefixedHexError> {
361 let checked = hex_check_unprefixed(s)?;
362 hex_u256_unchecked(checked)
363 .map_err(error::UnprefixedHexErrorInner::ParseInt)
364 .map_err(UnprefixedHexError)
365}
366
367pub(crate) fn hex_u256_unchecked(s: &str) -> Result<crate::pow::U256, ParseIntError> {
368 let (high, low) = if s.len() <= 32 {
369 let low = hex_u128_unchecked(s)?;
370 (0, low)
371 } else {
372 let high_len = s.len() - 32;
373 let high_s = &s[..high_len];
374 let low_s = &s[high_len..];
375
376 let high = hex_u128_unchecked(high_s)?;
377 let low = hex_u128_unchecked(low_s)?;
378 (high, low)
379 };
380
381 let mut bytes = [0u8; 32];
382 bytes[..16].copy_from_slice(&low.to_le_bytes());
383 bytes[16..].copy_from_slice(&high.to_le_bytes());
384 Ok(crate::pow::U256::from_le_bytes(bytes))
385}
386
387#[inline]
389pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
390 if let Some(stripped) = s.strip_prefix("0x") {
391 stripped
392 } else if let Some(stripped) = s.strip_prefix("0X") {
393 stripped
394 } else {
395 s
396 }
397}
398
399pub mod error {
401 use core::convert::Infallible;
402 use core::fmt;
403
404 use internals::error::InputString;
405 use internals::write_err;
406
407 #[derive(Debug, Clone, PartialEq, Eq)]
417 #[non_exhaustive]
418 pub struct ParseIntError {
419 pub(crate) input: InputString,
420 pub(crate) bits: u8,
422 pub(crate) is_signed: bool,
426 pub(crate) source: core::num::ParseIntError,
427 }
428
429 impl From<Infallible> for ParseIntError {
430 fn from(never: Infallible) -> Self { match never {} }
431 }
432
433 impl fmt::Display for ParseIntError {
434 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435 let signed = if self.is_signed { "signed" } else { "unsigned" };
436 write_err!(f, "{} ({}, {}-bit)", self.input.display_cannot_parse("integer"), signed, self.bits; self.source)
437 }
438 }
439
440 #[cfg(feature = "std")]
441 impl std::error::Error for ParseIntError {
442 #[inline]
443 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.source) }
444 }
445
446 impl From<ParseIntError> for core::num::ParseIntError {
447 #[inline]
448 fn from(value: ParseIntError) -> Self { value.source }
449 }
450
451 impl AsRef<core::num::ParseIntError> for ParseIntError {
452 #[inline]
453 fn as_ref(&self) -> &core::num::ParseIntError { &self.source }
454 }
455
456 #[derive(Debug, Clone, Eq, PartialEq)]
458 pub struct PrefixedHexError(pub(super) PrefixedHexErrorInner);
459
460 #[derive(Debug, Clone, Eq, PartialEq)]
462 pub(super) enum PrefixedHexErrorInner {
463 MissingPrefix(MissingPrefixError),
465 ParseInt(ParseIntError),
467 }
468
469 impl From<Infallible> for PrefixedHexError {
470 fn from(never: Infallible) -> Self { match never {} }
471 }
472
473 impl From<Infallible> for PrefixedHexErrorInner {
474 fn from(never: Infallible) -> Self { match never {} }
475 }
476
477 impl fmt::Display for PrefixedHexError {
478 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479 use PrefixedHexErrorInner as E;
480
481 match self.0 {
482 E::MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
483 E::ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
484 }
485 }
486 }
487
488 #[cfg(feature = "std")]
489 impl std::error::Error for PrefixedHexError {
490 #[inline]
491 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
492 use PrefixedHexErrorInner as E;
493
494 match self.0 {
495 E::MissingPrefix(ref e) => Some(e),
496 E::ParseInt(ref e) => Some(e),
497 }
498 }
499 }
500
501 #[derive(Debug, Clone, Eq, PartialEq)]
503 pub struct UnprefixedHexError(pub(super) UnprefixedHexErrorInner);
504
505 #[derive(Debug, Clone, Eq, PartialEq)]
506 pub(super) enum UnprefixedHexErrorInner {
507 ContainsPrefix(ContainsPrefixError),
509 ParseInt(ParseIntError),
511 }
512
513 impl From<Infallible> for UnprefixedHexError {
514 fn from(never: Infallible) -> Self { match never {} }
515 }
516
517 impl From<Infallible> for UnprefixedHexErrorInner {
518 fn from(never: Infallible) -> Self { match never {} }
519 }
520
521 impl fmt::Display for UnprefixedHexError {
522 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
523 use UnprefixedHexErrorInner as E;
524
525 match self.0 {
526 E::ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
527 E::ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
528 }
529 }
530 }
531
532 #[cfg(feature = "std")]
533 impl std::error::Error for UnprefixedHexError {
534 #[inline]
535 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
536 use UnprefixedHexErrorInner as E;
537
538 match self.0 {
539 E::ContainsPrefix(ref e) => Some(e),
540 E::ParseInt(ref e) => Some(e),
541 }
542 }
543 }
544
545 #[derive(Debug, Clone, Eq, PartialEq)]
547 pub(super) struct MissingPrefixError {
548 hex: InputString,
549 }
550
551 impl MissingPrefixError {
552 pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
554 }
555
556 impl fmt::Display for MissingPrefixError {
557 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558 write!(
559 f,
560 "{} because it is missing the '0x' prefix",
561 self.hex.display_cannot_parse("hex")
562 )
563 }
564 }
565
566 #[cfg(feature = "std")]
567 impl std::error::Error for MissingPrefixError {
568 #[inline]
569 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
570 }
571
572 #[derive(Debug, Clone, Eq, PartialEq)]
574 pub(super) struct ContainsPrefixError {
575 hex: InputString,
576 }
577
578 impl ContainsPrefixError {
579 #[inline]
581 pub(crate) fn new(hex: &str) -> Self { Self { hex: hex.into() } }
582 }
583
584 impl fmt::Display for ContainsPrefixError {
585 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586 write!(
587 f,
588 "{} because it contains the '0x' prefix",
589 self.hex.display_cannot_parse("hex")
590 )
591 }
592 }
593
594 #[cfg(feature = "std")]
595 impl std::error::Error for ContainsPrefixError {
596 #[inline]
597 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 #[cfg(feature = "alloc")]
604 use alloc::string::ToString;
605 #[cfg(feature = "std")]
606 use std::{error::Error, panic};
607
608 use super::*;
609
610 #[test]
611 fn parse_int() {
612 assert!(int_from_str::<u8>("1").is_ok());
613 let _ = int_from_str::<i8>("not a number").map_err(|e| assert!(e.is_signed));
614 let _ = int_from_str::<u8>("not a number").map_err(|e| assert!(!e.is_signed));
615 }
616
617 #[test]
618 #[cfg(feature = "std")]
619 fn parse_int_panic_when_populating_bits() {
620 #[allow(dead_code)]
622 struct TestTypeLargerThanU128(u128, u128);
623 impl_integer!(TestTypeLargerThanU128);
624 impl FromStr for TestTypeLargerThanU128 {
625 type Err = core::num::ParseIntError;
626
627 fn from_str(_: &str) -> Result<Self, Self::Err> {
628 "Always invalid for testing".parse::<u32>().map(|_| Self(0, 0))
629 }
630 }
631 impl From<i8> for TestTypeLargerThanU128 {
632 fn from(_: i8) -> Self { Self(0, 0) }
633 }
634
635 let result = panic::catch_unwind(|| int_from_str::<TestTypeLargerThanU128>("not a number"));
636 assert!(result.is_err());
637 }
638
639 #[test]
640 fn remove_prefix() {
641 let lower = "0xhello";
642 assert_eq!(hex_remove_prefix(lower).unwrap(), "hello");
643
644 let upper = "0Xhello";
645 assert_eq!(hex_remove_prefix(upper).unwrap(), "hello");
646
647 let err = "error";
648 assert!(hex_remove_prefix(err).is_err());
649 }
650
651 #[test]
652 fn check_unprefixed() {
653 let lower = "0xhello";
654 assert!(hex_check_unprefixed(lower).is_err());
655
656 let upper = "0Xhello";
657 assert!(hex_check_unprefixed(upper).is_err());
658
659 let valid = "hello";
660 assert_eq!(hex_check_unprefixed(valid).unwrap(), "hello");
661 }
662
663 #[test]
664 fn parse_u32_from_hex_prefixed() {
665 let want = 171;
666 let got = hex_u32("0xab").expect("failed to parse prefixed hex");
667 assert_eq!(got, want);
668 }
669
670 #[test]
671 fn parse_u32_from_hex_prefixed_upper() {
672 let want = 171;
673 let got = hex_u32("0XAB").expect("failed to parse prefixed hex");
674 assert_eq!(got, want);
675 }
676
677 #[test]
678 fn parse_u32_from_hex_no_prefix() {
679 let want = 171;
680 let got = hex_u32("ab").expect("failed to parse non-prefixed hex");
681 assert_eq!(got, want);
682 }
683
684 #[test]
685 fn parse_hex_u32_prefixed() {
686 let want = 171; assert_eq!(hex_u32_prefixed("0xab").unwrap(), want);
688 assert!(hex_u32_unprefixed("0xab").is_err());
689 }
690
691 #[test]
692 fn parse_hex_u32_upper_prefixed() {
693 let want = 171; assert_eq!(hex_u32_prefixed("0Xab").unwrap(), want);
695 assert!(hex_u32_unprefixed("0Xab").is_err());
696 }
697
698 #[test]
699 fn parse_hex_u32_unprefixed() {
700 let want = 171; assert_eq!(hex_u32_unprefixed("ab").unwrap(), want);
702 assert!(hex_u32_prefixed("ab").is_err());
703 }
704
705 #[test]
706 fn parse_u128_from_hex_prefixed() {
707 let want = 3_735_928_559;
708 let got = hex_u128("0xdeadbeef").expect("failed to parse prefixed hex");
709 assert_eq!(got, want);
710 }
711
712 #[test]
713 fn parse_u128_from_hex_upper_prefixed() {
714 let want = 3_735_928_559;
715 let got = hex_u128("0Xdeadbeef").expect("failed to parse prefixed hex");
716 assert_eq!(got, want);
717 }
718
719 #[test]
720 fn parse_u128_from_hex_no_prefix() {
721 let want = 3_735_928_559;
722 let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex");
723 assert_eq!(got, want);
724 }
725
726 #[test]
727 fn parse_hex_u128_prefixed() {
728 let want = 3_735_928_559;
729 assert_eq!(hex_u128_prefixed("0xdeadbeef").unwrap(), want);
730 assert!(hex_u128_unprefixed("0xdeadbeef").is_err());
731 }
732
733 #[test]
734 fn parse_hex_u128_upper_prefixed() {
735 let want = 3_735_928_559;
736 assert_eq!(hex_u128_prefixed("0Xdeadbeef").unwrap(), want);
737 assert!(hex_u128_unprefixed("0Xdeadbeef").is_err());
738 }
739
740 #[test]
741 fn parse_hex_u128_unprefixed() {
742 let want = 3_735_928_559;
743 assert_eq!(hex_u128_unprefixed("deadbeef").unwrap(), want);
744 assert!(hex_u128_prefixed("deadbeef").is_err());
745 }
746
747 #[test]
748 fn parse_u32_from_hex_unchecked_errors_on_prefix() {
749 assert!(hex_u32_unchecked("0xab").is_err());
750 assert!(hex_u32_unchecked("0Xab").is_err());
751 }
752
753 #[test]
754 fn parse_u32_from_hex_unchecked_errors_on_overflow() {
755 assert!(hex_u32_unchecked("1234abcd").is_ok());
756 assert!(hex_u32_unchecked("1234abcd1").is_err());
757 }
758
759 #[test]
760 fn parse_u128_from_hex_unchecked_errors_on_prefix() {
761 assert!(hex_u128_unchecked("0xdeadbeef").is_err());
762 assert!(hex_u128_unchecked("0Xdeadbeef").is_err());
763 }
764
765 #[test]
766 fn parse_u128_from_hex_unchecked_errors_on_overflow() {
767 assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff").is_ok());
768 assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff1").is_err());
769 }
770
771 #[test]
772 #[cfg(feature = "alloc")]
773 fn error_display_is_non_empty() {
774 let e = int_from_str::<u32>("not_a_number").unwrap_err();
776 assert!(!e.to_string().is_empty());
777 #[cfg(feature = "std")]
778 assert!(e.source().is_some());
779
780 let e = hex_u32_prefixed("abc").unwrap_err();
783 assert!(!e.to_string().is_empty());
784 #[cfg(feature = "std")]
785 assert!(e.source().is_some());
786 let PrefixedHexError(error::PrefixedHexErrorInner::MissingPrefix(e)) = e else {
787 panic!("should be a MissingPrefixError")
788 };
789 assert!(!e.to_string().is_empty());
790 #[cfg(feature = "std")]
791 assert!(e.source().is_none());
792 let e = hex_u32_prefixed("0xgabc").unwrap_err();
794 assert!(!e.to_string().is_empty());
795 #[cfg(feature = "std")]
796 assert!(e.source().is_some());
797
798 let e = hex_u32_unprefixed("0xabc").unwrap_err();
801 assert!(!e.to_string().is_empty());
802 #[cfg(feature = "std")]
803 assert!(e.source().is_some());
804 let UnprefixedHexError(error::UnprefixedHexErrorInner::ContainsPrefix(e)) = e else {
805 panic!("should be a ContainsPrefixError")
806 };
807 assert!(!e.to_string().is_empty());
808 #[cfg(feature = "std")]
809 assert!(e.source().is_none());
810 let e = hex_u32_unprefixed("gabc").unwrap_err();
812 assert!(!e.to_string().is_empty());
813 #[cfg(feature = "std")]
814 assert!(e.source().is_some());
815 }
816}