1use std::{
3 error::Error,
4 fmt::{Display, Formatter},
5};
6
7const ALPHABET: &str = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
8
9const ERROR_INVALID_CHAR: &str = "UID contains an invalid character";
10const ERROR_TOO_BIG: &str = "UID is too big to fit into a u64";
11const ERROR_EMPTY: &str = "UID is empty or a value that mapped to zero";
12
13#[derive(Debug, Copy, Clone)]
15pub enum Base58Error {
16 InvalidCharacter,
18 UidTooBig,
19 UidEmpty,
20}
21
22impl Display for Base58Error {
23 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
24 match *self {
25 Base58Error::InvalidCharacter => write!(f, "{}", ERROR_INVALID_CHAR),
26 Base58Error::UidTooBig => write!(f, "{}", ERROR_TOO_BIG),
27 Base58Error::UidEmpty => write!(f, "{}", ERROR_EMPTY),
28 }
29 }
30}
31
32impl Error for Base58Error {
33 fn description(&self) -> &str {
34 match *self {
35 Base58Error::InvalidCharacter => ERROR_INVALID_CHAR,
36 Base58Error::UidTooBig => ERROR_TOO_BIG,
37 Base58Error::UidEmpty => ERROR_EMPTY,
38 }
39 }
40}
41
42pub trait Base58 {
44 fn base58_to_u32(&self) -> Result<u32, Base58Error>;
46}
47
48impl Base58 for str {
49 fn base58_to_u32(&self) -> Result<u32, Base58Error> {
50 let mut result_u64: u64 = 0;
51 let radix: u64 = ALPHABET.len() as u64;
52 let mut digit: u32 = 0;
53 let filtered = self.as_bytes().iter().skip_while(|c| **c == '1' as u8).collect::<Vec<&u8>>();
55 for (_idx, &&character) in filtered.iter().enumerate().rev() {
56 match ALPHABET.as_bytes().iter().enumerate().find(|(_i, c)| **c == character).map(|(i, _c)| i) {
57 None => return Err(Base58Error::InvalidCharacter),
58 Some(i) => {
59 if digit > 0 && radix.pow(digit - 1) > (u64::max_value() / radix) {
60 return Err(Base58Error::UidTooBig); }
62 let opt = radix.pow(digit).checked_mul(i as u64);
63 if opt.is_none() {
64 return Err(Base58Error::UidTooBig); }
66 if u64::max_value() - opt.unwrap() < result_u64 {
67 return Err(Base58Error::UidTooBig); }
69 result_u64 += opt.unwrap();
70 }
71 }
72 digit += 1;
73 }
74
75 let result = if result_u64 > u32::max_value().into() {
76 let value1 = result_u64 & 0xFF_FF_FF_FF;
77 let value2 = (result_u64 >> 32) & 0xFF_FF_FF_FF;
78 ((value1 & 0x00_00_0F_FF)
79 | (value1 & 0x0F_00_00_00) >> 12
80 | (value2 & 0x00_00_00_3F) << 16
81 | (value2 & 0x00_0F_00_00) << 6
82 | (value2 & 0x3F_00_00_00) << 2) as u32
83 } else {
84 result_u64 as u32
85 };
86 if result == 0 {
87 Err(Base58Error::UidEmpty)
88 } else {
89 Ok(result)
90 }
91 }
92}
93
94impl Base58 for String {
95 fn base58_to_u32(&self) -> Result<u32, Base58Error> { self.as_str().base58_to_u32() }
96}