1use alloy_primitives::{I256, Sign, U256, utils::ParseUnits};
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') { s.trim_start_matches('0').to_string() } else { s }
388 }
389
390 fn _parse_int(s: &str, base: Base) -> Result<(U256, bool)> {
391 let (s, sign) = get_sign(s);
392 let mut n = Self::_parse_uint(s, base)?;
393
394 let is_neg = matches!(sign, Sign::Negative);
395 if is_neg {
396 n = (!n).overflowing_add(U256::from(1)).0;
397 }
398
399 Ok((n, !is_neg))
400 }
401
402 fn _parse_uint(s: &str, base: Base) -> Result<U256> {
403 let s = match s.get(0..2) {
404 Some("0x" | "0X" | "0o" | "0O" | "0b" | "0B") => &s[2..],
405 _ => s,
406 };
407 U256::from_str_radix(s, base as u64).map_err(Into::into)
408 }
409}
410
411pub trait ToBase {
415 type Err;
416
417 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err>;
437}
438
439impl ToBase for NumberWithBase {
440 type Err = Infallible;
441
442 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
443 let n = self.with_base(base);
444 if add_prefix { Ok(format!("{n:#?}")) } else { Ok(format!("{n:?}")) }
445 }
446}
447
448impl ToBase for I256 {
449 type Err = Infallible;
450
451 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
452 let n = NumberWithBase::from(*self).with_base(base);
453 if add_prefix { Ok(format!("{n:#?}")) } else { Ok(format!("{n:?}")) }
454 }
455}
456
457impl ToBase for U256 {
458 type Err = Infallible;
459
460 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
461 let n = NumberWithBase::from(*self).with_base(base);
462 if add_prefix { Ok(format!("{n:#?}")) } else { Ok(format!("{n:?}")) }
463 }
464}
465
466impl ToBase for String {
467 type Err = eyre::Report;
468
469 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
470 str::to_base(self, base, add_prefix)
471 }
472}
473
474impl ToBase for str {
475 type Err = eyre::Report;
476
477 fn to_base(&self, base: Base, add_prefix: bool) -> Result<String, Self::Err> {
478 let n = NumberWithBase::from_str(self)?.with_base(base);
479 if add_prefix { Ok(format!("{n:#?}")) } else { Ok(format!("{n:?}")) }
480 }
481}
482
483fn get_sign(s: &str) -> (&str, Sign) {
484 match s.as_bytes().first() {
485 Some(b'+') => (&s[1..], Sign::Positive),
486 Some(b'-') => (&s[1..], Sign::Negative),
487 _ => (s, Sign::Positive),
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 use Base::*;
495
496 const POS_NUM: [i128; 44] = [
497 1,
498 2,
499 3,
500 5,
501 7,
502 8,
503 10,
504 11,
505 13,
506 16,
507 17,
508 19,
509 23,
510 29,
511 31,
512 32,
513 37,
514 41,
515 43,
516 47,
517 53,
518 59,
519 61,
520 64,
521 67,
522 71,
523 73,
524 79,
525 83,
526 89,
527 97,
528 100,
529 128,
530 200,
531 333,
532 500,
533 666,
534 1000,
535 6666,
536 10000,
537 i16::MAX as i128,
538 i32::MAX as i128,
539 i64::MAX as i128,
540 i128::MAX,
541 ];
542
543 const NEG_NUM: [i128; 44] = [
544 -1,
545 -2,
546 -3,
547 -5,
548 -7,
549 -8,
550 -10,
551 -11,
552 -13,
553 -16,
554 -17,
555 -19,
556 -23,
557 -29,
558 -31,
559 -32,
560 -37,
561 -41,
562 -43,
563 -47,
564 -53,
565 -59,
566 -61,
567 -64,
568 -67,
569 -71,
570 -73,
571 -79,
572 -83,
573 -89,
574 -97,
575 -100,
576 -128,
577 -200,
578 -333,
579 -500,
580 -666,
581 -1000,
582 -6666,
583 -10000,
584 i16::MIN as i128,
585 i32::MIN as i128,
586 i64::MIN as i128,
587 i128::MIN,
588 ];
589
590 #[test]
591 fn test_defaults() {
592 let def: Base = Default::default();
593 assert!(matches!(def, Decimal));
594
595 let n: NumberWithBase = U256::ZERO.into();
596 assert!(matches!(n.base, Decimal));
597 let n: NumberWithBase = I256::ZERO.into();
598 assert!(matches!(n.base, Decimal));
599 }
600
601 #[test]
602 fn can_parse_base() {
603 assert_eq!("2".parse::<Base>().unwrap(), Binary);
604 assert_eq!("b".parse::<Base>().unwrap(), Binary);
605 assert_eq!("bin".parse::<Base>().unwrap(), Binary);
606 assert_eq!("binary".parse::<Base>().unwrap(), Binary);
607
608 assert_eq!("8".parse::<Base>().unwrap(), Octal);
609 assert_eq!("o".parse::<Base>().unwrap(), Octal);
610 assert_eq!("oct".parse::<Base>().unwrap(), Octal);
611 assert_eq!("octal".parse::<Base>().unwrap(), Octal);
612
613 assert_eq!("10".parse::<Base>().unwrap(), Decimal);
614 assert_eq!("d".parse::<Base>().unwrap(), Decimal);
615 assert_eq!("dec".parse::<Base>().unwrap(), Decimal);
616 assert_eq!("decimal".parse::<Base>().unwrap(), Decimal);
617
618 assert_eq!("16".parse::<Base>().unwrap(), Hexadecimal);
619 assert_eq!("h".parse::<Base>().unwrap(), Hexadecimal);
620 assert_eq!("hex".parse::<Base>().unwrap(), Hexadecimal);
621 assert_eq!("hexadecimal".parse::<Base>().unwrap(), Hexadecimal);
622 }
623
624 #[test]
625 fn can_detect_base() {
626 assert_eq!(Base::detect("0b100").unwrap(), Binary);
627 assert_eq!(Base::detect("0o100").unwrap(), Octal);
628 assert_eq!(Base::detect("100").unwrap(), Decimal);
629 assert_eq!(Base::detect("0x100").unwrap(), Hexadecimal);
630
631 assert_eq!(Base::detect("0123456789abcdef").unwrap(), Hexadecimal);
632
633 let _ = Base::detect("0b234abc").unwrap_err();
634 let _ = Base::detect("0o89cba").unwrap_err();
635 let _ = Base::detect("0123456789abcdefg").unwrap_err();
636 let _ = Base::detect("0x123abclpmk").unwrap_err();
637 let _ = Base::detect("hello world").unwrap_err();
638 }
639
640 #[test]
641 fn test_format_pos() {
642 let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect();
643 let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect();
644 let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect();
645 let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect();
646 let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect();
647
648 for (i, n) in POS_NUM.into_iter().enumerate() {
649 let mut num: NumberWithBase = I256::try_from(n).unwrap().into();
650
651 assert_eq!(num.set_base(Binary).format(), expected_2[i]);
652 assert_eq!(num.set_base(Octal).format(), expected_8[i]);
653 assert_eq!(num.set_base(Decimal).format(), expected_10[i]);
654 assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]);
655 assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]);
656 }
657 }
658
659 #[test]
660 fn test_format_neg() {
661 let expected_2: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:1>256b}")).collect();
664 let expected_8: Vec<_> = NEG_NUM
665 .iter()
666 .map(|n| {
667 let i = I256::try_from(*n).unwrap();
668 let mut u = NumberWithBase::from(i);
669 u.set_base(Octal);
670 u.format()
671 })
672 .collect();
673 let expected_10: Vec<_> =
675 NEG_NUM.iter().map(|n| format!("{n:}").trim_matches('-').to_string()).collect();
676 let expected_l16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:f>64x}")).collect();
677 let expected_u16: Vec<_> = NEG_NUM.iter().map(|n| format!("{n:F>64X}")).collect();
678
679 for (i, n) in NEG_NUM.into_iter().enumerate() {
680 let mut num: NumberWithBase = I256::try_from(n).unwrap().into();
681
682 assert_eq!(num.set_base(Binary).format(), expected_2[i]);
683 assert_eq!(num.set_base(Octal).format(), expected_8[i]);
684 assert_eq!(num.set_base(Decimal).format(), expected_10[i]);
685 assert_eq!(num.set_base(Hexadecimal).format(), expected_l16[i]);
686 assert_eq!(num.set_base(Hexadecimal).format().to_uppercase(), expected_u16[i]);
687 }
688 }
689
690 #[test]
691 fn test_fmt_macro() {
692 let nums: Vec<_> =
693 POS_NUM.into_iter().map(|n| NumberWithBase::from(I256::try_from(n).unwrap())).collect();
694
695 let actual_2: Vec<_> = nums.iter().map(|n| format!("{n:b}")).collect();
696 let actual_2_alt: Vec<_> = nums.iter().map(|n| format!("{n:#b}")).collect();
697 let actual_8: Vec<_> = nums.iter().map(|n| format!("{n:o}")).collect();
698 let actual_8_alt: Vec<_> = nums.iter().map(|n| format!("{n:#o}")).collect();
699 let actual_10: Vec<_> = nums.iter().map(|n| format!("{n:}")).collect();
700 let actual_10_alt: Vec<_> = nums.iter().map(|n| format!("{n:#}")).collect();
701 let actual_l16: Vec<_> = nums.iter().map(|n| format!("{n:x}")).collect();
702 let actual_l16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#x}")).collect();
703 let actual_u16: Vec<_> = nums.iter().map(|n| format!("{n:X}")).collect();
704 let actual_u16_alt: Vec<_> = nums.iter().map(|n| format!("{n:#X}")).collect();
705
706 let expected_2: Vec<_> = POS_NUM.iter().map(|n| format!("{n:b}")).collect();
707 let expected_2_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#b}")).collect();
708 let expected_8: Vec<_> = POS_NUM.iter().map(|n| format!("{n:o}")).collect();
709 let expected_8_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#o}")).collect();
710 let expected_10: Vec<_> = POS_NUM.iter().map(|n| format!("{n:}")).collect();
711 let expected_10_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#}")).collect();
712 let expected_l16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:x}")).collect();
713 let expected_l16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#x}")).collect();
714 let expected_u16: Vec<_> = POS_NUM.iter().map(|n| format!("{n:X}")).collect();
715 let expected_u16_alt: Vec<_> = POS_NUM.iter().map(|n| format!("{n:#X}")).collect();
716
717 for (i, _) in POS_NUM.iter().enumerate() {
718 assert_eq!(actual_2[i], expected_2[i]);
719 assert_eq!(actual_2_alt[i], expected_2_alt[i]);
720
721 assert_eq!(actual_8[i], expected_8[i]);
722 assert_eq!(actual_8_alt[i], expected_8_alt[i]);
723
724 assert_eq!(actual_10[i], expected_10[i]);
725 assert_eq!(actual_10_alt[i], expected_10_alt[i]);
726
727 assert_eq!(actual_l16[i], expected_l16[i]);
728 assert_eq!(actual_l16_alt[i], expected_l16_alt[i]);
729
730 assert_eq!(actual_u16[i], expected_u16[i]);
731 assert_eq!(actual_u16_alt[i], expected_u16_alt[i]);
732 }
733 }
734}