1use alloy_primitives::{utils::ParseUnits, Sign, I256, U256};
2use eyre::Result;
3use std::{
4 convert::Infallible,
5 fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex},
6 num::IntErrorKind,
7 str::FromStr,
8};
9
10#[repr(u32)]
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
18pub enum Base {
19 Binary = 2,
20 Octal = 8,
21 #[default]
22 Decimal = 10,
23 Hexadecimal = 16,
24}
25
26impl Display for Base {
27 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
28 Display::fmt(&(*self as u32), f)
29 }
30}
31
32impl FromStr for Base {
33 type Err = eyre::Report;
34
35 fn from_str(s: &str) -> Result<Self, Self::Err> {
36 match s.to_lowercase().as_str() {
37 "2" | "b" | "bin" | "binary" => Ok(Self::Binary),
38 "8" | "o" | "oct" | "octal" => Ok(Self::Octal),
39 "10" | "d" | "dec" | "decimal" => Ok(Self::Decimal),
40 "16" | "h" | "hex" | "hexadecimal" => Ok(Self::Hexadecimal),
41 s => Err(eyre::eyre!(
42 "\
43Invalid base \"{s}\". Possible values:
44 2, b, bin, binary
45 8, o, oct, octal
4610, d, dec, decimal
4716, h, hex, hexadecimal"
48 )),
49 }
50 }
51}
52
53impl TryFrom<String> for Base {
54 type Error = eyre::Report;
55
56 fn try_from(s: String) -> Result<Self, Self::Error> {
57 Self::from_str(&s)
58 }
59}
60
61impl TryFrom<u32> for Base {
62 type Error = eyre::Report;
63
64 fn try_from(n: u32) -> Result<Self, Self::Error> {
65 match n {
66 2 => Ok(Self::Binary),
67 8 => Ok(Self::Octal),
68 10 => Ok(Self::Decimal),
69 16 => Ok(Self::Hexadecimal),
70 n => Err(eyre::eyre!("Invalid base \"{}\". Possible values: 2, 8, 10, 16", n)),
71 }
72 }
73}
74
75impl TryFrom<I256> for Base {
76 type Error = eyre::Report;
77
78 fn try_from(n: I256) -> Result<Self, Self::Error> {
79 Self::try_from(n.low_u32())
80 }
81}
82
83impl TryFrom<U256> for Base {
84 type Error = eyre::Report;
85
86 fn try_from(n: U256) -> Result<Self, Self::Error> {
87 Self::try_from(n.saturating_to::<u32>())
88 }
89}
90
91impl From<Base> for u32 {
92 fn from(b: Base) -> Self {
93 b as Self
94 }
95}
96
97impl From<Base> for String {
98 fn from(b: Base) -> Self {
99 b.to_string()
100 }
101}
102
103impl Base {
104 pub fn unwrap_or_detect(base: Option<&str>, s: impl AsRef<str>) -> Result<Self> {
105 match base {
106 Some(base) => base.parse(),
107 None => Self::detect(s),
108 }
109 }
110
111 pub fn detect(s: impl AsRef<str>) -> Result<Self> {
113 let s = s.as_ref();
114 match s {
115 _ if s.starts_with(['+', '-']) => Self::detect(&s[1..]),
117 _ if s.starts_with("0b") => match u64::from_str_radix(&s[2..], 2) {
124 Ok(_) => Ok(Self::Binary),
125 Err(e) => match e.kind() {
126 IntErrorKind::PosOverflow => Ok(Self::Binary),
127 _ => Err(eyre::eyre!("could not parse binary value: {}", e)),
128 },
129 },
130 _ if s.starts_with("0o") => match u64::from_str_radix(&s[2..], 8) {
131 Ok(_) => Ok(Self::Octal),
132 Err(e) => match e.kind() {
133 IntErrorKind::PosOverflow => Ok(Self::Octal),
134 _ => Err(eyre::eyre!("could not parse octal value: {e}")),
135 },
136 },
137 _ if s.starts_with("0x") => match u64::from_str_radix(&s[2..], 16) {
138 Ok(_) => Ok(Self::Hexadecimal),
139 Err(e) => match e.kind() {
140 IntErrorKind::PosOverflow => Ok(Self::Hexadecimal),
141 _ => Err(eyre::eyre!("could not parse hexadecimal value: {e}")),
142 },
143 },
144 _ => match U256::from_str_radix(s, 10) {
146 Ok(_) => Ok(Self::Decimal),
148 Err(_) => match U256::from_str_radix(s, 16) {
149 Ok(_) => Ok(Self::Hexadecimal),
150 Err(e) => Err(eyre::eyre!(
151 "could not autodetect base as neither decimal or hexadecimal: {e}"
152 )),
153 },
154 },
155 }
156 }
157
158 pub const fn prefix(&self) -> &str {
160 match self {
161 Self::Binary => "0b",
162 Self::Octal => "0o",
163 Self::Decimal => "",
164 Self::Hexadecimal => "0x",
165 }
166 }
167}
168
169#[derive(Clone, Copy)]
199pub struct NumberWithBase {
200 number: U256,
202 is_nonnegative: bool,
204 base: Base,
206}
207
208impl std::ops::Deref for NumberWithBase {
209 type Target = U256;
210
211 fn deref(&self) -> &Self::Target {
212 &self.number
213 }
214}
215
216impl Debug for NumberWithBase {
218 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
219 let prefix = self.base.prefix();
220 if self.number.is_zero() {
221 f.pad_integral(true, prefix, "0")
222 } else {
223 let is_nonnegative = match self.base {
225 Base::Decimal => self.is_nonnegative,
226 _ => true,
227 };
228 f.pad_integral(is_nonnegative, prefix, &self.format())
229 }
230 }
231}
232
233impl Binary for NumberWithBase {
234 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
235 Debug::fmt(&self.with_base(Base::Binary), f)
236 }
237}
238
239impl Octal for NumberWithBase {
240 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
241 Debug::fmt(&self.with_base(Base::Octal), f)
242 }
243}
244
245impl Display for NumberWithBase {
246 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
247 Debug::fmt(&self.with_base(Base::Decimal), f)
248 }
249}
250
251impl LowerHex for NumberWithBase {
252 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
253 Debug::fmt(&self.with_base(Base::Hexadecimal), f)
254 }
255}
256
257impl UpperHex for NumberWithBase {
258 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
259 let n = format!("{self:x}").to_uppercase();
260 f.pad_integral(true, Base::Hexadecimal.prefix(), &n)
261 }
262}
263
264impl FromStr for NumberWithBase {
265 type Err = eyre::Report;
266
267 fn from_str(s: &str) -> Result<Self, Self::Err> {
268 Self::parse_int(s, None)
269 }
270}
271
272impl From<I256> for NumberWithBase {
273 fn from(number: I256) -> Self {
274 Self::new(number.into_raw(), !number.is_negative(), Base::default())
276 }
277}
278
279impl From<ParseUnits> for NumberWithBase {
280 fn from(value: ParseUnits) -> Self {
281 match value {
282 ParseUnits::U256(val) => val.into(),
283 ParseUnits::I256(val) => val.into(),
284 }
285 }
286}
287
288impl From<U256> for NumberWithBase {
289 fn from(number: U256) -> Self {
290 Self::new(number, true, Base::default())
291 }
292}
293
294impl From<NumberWithBase> for I256 {
295 fn from(n: NumberWithBase) -> Self {
296 Self::from_raw(n.number)
297 }
298}
299
300impl From<NumberWithBase> for U256 {
301 fn from(n: NumberWithBase) -> Self {
302 n.number
303 }
304}
305
306impl From<NumberWithBase> for String {
307 fn from(n: NumberWithBase) -> Self {
311 n.format()
312 }
313}
314
315impl NumberWithBase {
316 pub fn new(number: impl Into<U256>, is_nonnegative: bool, base: Base) -> Self {
317 Self { number: number.into(), is_nonnegative, base }
318 }
319
320 pub fn with_base(&self, base: Base) -> Self {
322 Self { number: self.number, is_nonnegative: self.is_nonnegative, base }
323 }
324
325 pub fn parse_int(s: &str, base: Option<&str>) -> Result<Self> {
328 let base = Base::unwrap_or_detect(base, s)?;
329 let (number, is_nonnegative) = Self::_parse_int(s, base)?;
330 Ok(Self { number, is_nonnegative, base })
331 }
332
333 pub fn parse_uint(s: &str, base: Option<&str>) -> Result<Self> {
336 let base = Base::unwrap_or_detect(base, s)?;
337 let number = Self::_parse_uint(s, base)?;
338 Ok(Self { number, is_nonnegative: true, base })
339 }
340
341 pub fn number(&self) -> U256 {
344 self.number
345 }
346
347 pub fn is_nonnegative(&self) -> bool {
349 self.is_nonnegative
350 }
351
352 pub fn base(&self) -> Base {
354 self.base
355 }
356
357 pub const fn prefix(&self) -> &str {
359 self.base.prefix()
360 }
361
362 pub fn set_base(&mut self, base: Base) -> &mut Self {
364 self.base = base;
365 self
366 }
367
368 pub fn format(&self) -> String {
374 let s = match self.base {
375 Base::Binary => format!("{:b}", self.number),
376 Base::Octal => format!("{:o}", self.number),
377 Base::Decimal => {
378 if self.is_nonnegative {
379 self.number.to_string()
380 } else {
381 let s = I256::from_raw(self.number).to_string();
382 s.strip_prefix('-').unwrap_or(&s).to_string()
383 }
384 }
385 Base::Hexadecimal => format!("{:x}", self.number),
386 };
387 if s.starts_with('0') {
388 s.trim_start_matches('0').to_string()
389 } else {
390 s
391 }
392 }
393
394 fn _parse_int(s: &str, base: Base) -> Result<(U256, bool)> {
395 let (s, sign) = get_sign(s);
396 let mut n = Self::_parse_uint(s, base)?;
397
398 let is_neg = matches!(sign, Sign::Negative);
399 if is_neg {
400 n = (!n).overflowing_add(U256::from(1)).0;
401 }
402
403 Ok((n, !is_neg))
404 }
405
406 fn _parse_uint(s: &str, base: Base) -> Result<U256> {
407 let s = match s.get(0..2) {
408 Some("0x" | "0X" | "0o" | "0O" | "0b" | "0B") => &s[2..],
409 _ => s,
410 };
411 U256::from_str_radix(s, base as u64).map_err(Into::into)
412 }
413}
414
415pub trait ToBase {
419 type Err;
420
421 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err>;
441}
442
443impl ToBase for NumberWithBase {
444 type Err = Infallible;
445
446 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
447 let n = self.with_base(base);
448 if add_prefix {
449 Ok(format!("{n:#?}"))
450 } else {
451 Ok(format!("{n:?}"))
452 }
453 }
454}
455
456impl ToBase for I256 {
457 type Err = Infallible;
458
459 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
460 let n = NumberWithBase::from(*self).with_base(base);
461 if add_prefix {
462 Ok(format!("{n:#?}"))
463 } else {
464 Ok(format!("{n:?}"))
465 }
466 }
467}
468
469impl ToBase for U256 {
470 type Err = Infallible;
471
472 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
473 let n = NumberWithBase::from(*self).with_base(base);
474 if add_prefix {
475 Ok(format!("{n:#?}"))
476 } else {
477 Ok(format!("{n:?}"))
478 }
479 }
480}
481
482impl ToBase for String {
483 type Err = eyre::Report;
484
485 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
486 str::to_base(self, base, add_prefix)
487 }
488}
489
490impl ToBase for str {
491 type Err = eyre::Report;
492
493 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
494 let n = NumberWithBase::from_str(self)?.with_base(base);
495 if add_prefix {
496 Ok(format!("{n:#?}"))
497 } else {
498 Ok(format!("{n:?}"))
499 }
500 }
501}
502
503fn get_sign(s: &str) -> (&str, Sign) {
504 match s.as_bytes().first() {
505 Some(b'+') => (&s[1..], Sign::Positive),
506 Some(b'-') => (&s[1..], Sign::Negative),
507 _ => (s, Sign::Positive),
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514 use Base::*;
515
516 const POS_NUM: [i128; 44] = [
517 1,
518 2,
519 3,
520 5,
521 7,
522 8,
523 10,
524 11,
525 13,
526 16,
527 17,
528 19,
529 23,
530 29,
531 31,
532 32,
533 37,
534 41,
535 43,
536 47,
537 53,
538 59,
539 61,
540 64,
541 67,
542 71,
543 73,
544 79,
545 83,
546 89,
547 97,
548 100,
549 128,
550 200,
551 333,
552 500,
553 666,
554 1000,
555 6666,
556 10000,
557 i16::MAX as i128,
558 i32::MAX as i128,
559 i64::MAX as i128,
560 i128::MAX,
561 ];
562
563 const NEG_NUM: [i128; 44] = [
564 -1,
565 -2,
566 -3,
567 -5,
568 -7,
569 -8,
570 -10,
571 -11,
572 -13,
573 -16,
574 -17,
575 -19,
576 -23,
577 -29,
578 -31,
579 -32,
580 -37,
581 -41,
582 -43,
583 -47,
584 -53,
585 -59,
586 -61,
587 -64,
588 -67,
589 -71,
590 -73,
591 -79,
592 -83,
593 -89,
594 -97,
595 -100,
596 -128,
597 -200,
598 -333,
599 -500,
600 -666,
601 -1000,
602 -6666,
603 -10000,
604 i16::MIN as i128,
605 i32::MIN as i128,
606 i64::MIN as i128,
607 i128::MIN,
608 ];
609
610 #[test]
611 fn test_defaults() {
612 let def: Base = Default::default();
613 assert!(matches!(def, Decimal));
614
615 let n: NumberWithBase = U256::ZERO.into();
616 assert!(matches!(n.base, Decimal));
617 let n: NumberWithBase = I256::ZERO.into();
618 assert!(matches!(n.base, Decimal));
619 }
620
621 #[test]
622 fn can_parse_base() {
623 assert_eq!("2".parse::<Base>().unwrap(), Binary);
624 assert_eq!("b".parse::<Base>().unwrap(), Binary);
625 assert_eq!("bin".parse::<Base>().unwrap(), Binary);
626 assert_eq!("binary".parse::<Base>().unwrap(), Binary);
627
628 assert_eq!("8".parse::<Base>().unwrap(), Octal);
629 assert_eq!("o".parse::<Base>().unwrap(), Octal);
630 assert_eq!("oct".parse::<Base>().unwrap(), Octal);
631 assert_eq!("octal".parse::<Base>().unwrap(), Octal);
632
633 assert_eq!("10".parse::<Base>().unwrap(), Decimal);
634 assert_eq!("d".parse::<Base>().unwrap(), Decimal);
635 assert_eq!("dec".parse::<Base>().unwrap(), Decimal);
636 assert_eq!("decimal".parse::<Base>().unwrap(), Decimal);
637
638 assert_eq!("16".parse::<Base>().unwrap(), Hexadecimal);
639 assert_eq!("h".parse::<Base>().unwrap(), Hexadecimal);
640 assert_eq!("hex".parse::<Base>().unwrap(), Hexadecimal);
641 assert_eq!("hexadecimal".parse::<Base>().unwrap(), Hexadecimal);
642 }
643
644 #[test]
645 fn can_detect_base() {
646 assert_eq!(Base::detect("0b100").unwrap(), Binary);
647 assert_eq!(Base::detect("0o100").unwrap(), Octal);
648 assert_eq!(Base::detect("100").unwrap(), Decimal);
649 assert_eq!(Base::detect("0x100").unwrap(), Hexadecimal);
650
651 assert_eq!(Base::detect("0123456789abcdef").unwrap(), Hexadecimal);
652
653 let _ = Base::detect("0b234abc").unwrap_err();
654 let _ = Base::detect("0o89cba").unwrap_err();
655 let _ = Base::detect("0123456789abcdefg").unwrap_err();
656 let _ = Base::detect("0x123abclpmk").unwrap_err();
657 let _ = Base::detect("hello world").unwrap_err();
658 }
659
660 #[test]
661 fn test_format_pos() {
662 let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect();
663 let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect();
664 let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect();
665 let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect();
666 let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect();
667
668 for (i, n) in POS_NUM.into_iter().enumerate() {
669 let mut num: NumberWithBase = I256::try_from(n).unwrap().into();
670
671 assert_eq!(num.set_base(Binary).format(), expected_2[i]);
672 assert_eq!(num.set_base(Octal).format(), expected_8[i]);
673 assert_eq!(num.set_base(Decimal).format(), expected_10[i]);
674 assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]);
675 assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]);
676 }
677 }
678
679 #[test]
681 fn test_format_neg() {
682 let expected_2: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:1>256b}")).collect();
685 let expected_10: Vec<_> =
688 NEG_NUM.iter().map(|n| format!("{n:}").trim_matches('-').to_string()).collect();
689 let expected_l16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:f>64x}")).collect();
690 let expected_u16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:F>64X}")).collect();
691
692 for (i, n) in NEG_NUM.into_iter().enumerate() {
693 let mut num: NumberWithBase = I256::try_from(n).unwrap().into();
694
695 assert_eq!(num.set_base(Binary).format(), expected_2[i]);
696 assert_eq!(num.set_base(Decimal).format(), expected_10[i]);
698 assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]);
699 assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]);
700 }
701 }
702
703 #[test]
704 fn test_fmt_macro() {
705 let nums: Vec<_> =
706 POS_NUM.into_iter().map(|n| NumberWithBase::from(I256::try_from(n).unwrap())).collect();
707
708 let actual_2: Vec<_> = nums.iter().map(|n| format!("{n:b}")).collect();
709 let actual_2_alt: Vec<_> = nums.iter().map(|n| format!("{n:#b}")).collect();
710 let actual_8: Vec<_> = nums.iter().map(|n| format!("{n:o}")).collect();
711 let actual_8_alt: Vec<_> = nums.iter().map(|n| format!("{n:#o}")).collect();
712 let actual_10: Vec<_> = nums.iter().map(|n| format!("{n:}")).collect();
713 let actual_10_alt: Vec<_> = nums.iter().map(|n| format!("{n:#}")).collect();
714 let actual_l16: Vec<_> = nums.iter().map(|n| format!("{n:x}")).collect();
715 let actual_l16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#x}")).collect();
716 let actual_u16: Vec<_> = nums.iter().map(|n| format!("{n:X}")).collect();
717 let actual_u16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#X}")).collect();
718
719 let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect();
720 let expected_2_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#b}")).collect();
721 let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect();
722 let expected_8_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#o}")).collect();
723 let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect();
724 let expected_10_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#}")).collect();
725 let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect();
726 let expected_l16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#x}")).collect();
727 let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect();
728 let expected_u16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#X}")).collect();
729
730 for (i, _) in POS_NUM.iter().enumerate() {
731 assert_eq!(actual_2[i], expected_2[i]);
732 assert_eq!(actual_2_alt[i], expected_2_alt[i]);
733
734 assert_eq!(actual_8[i], expected_8[i]);
735 assert_eq!(actual_8_alt[i], expected_8_alt[i]);
736
737 assert_eq!(actual_10[i], expected_10[i]);
738 assert_eq!(actual_10_alt[i], expected_10_alt[i]);
739
740 assert_eq!(actual_l16[i], expected_l16[i]);
741 assert_eq!(actual_l16_alt[i], expected_l16_alt[i]);
742
743 assert_eq!(actual_u16[i], expected_u16[i]);
744 assert_eq!(actual_u16_alt[i], expected_u16_alt[i]);
745 }
746 }
747}