use crate::std::string::{String, ToString};
pub trait AsBalance {
fn convert_balance_pretty(value: Self, decimals: u8, unit: &str) -> Currency;
}
macro_rules! impl_balance {
($($uint_type: ty), *) => {
$(
impl AsBalance for $uint_type {
fn convert_balance_pretty(value: $uint_type, decimals: u8, unit: &str) -> Currency {
convert_balance_string(&value.to_string(), decimals, unit)
}
}
)*
}
}
impl_balance!(u8, u16, u32, u64, u128);
struct CutNumber {
before_point: String,
after_point: Option<String>,
mag: i8,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Currency {
pub number: String,
pub units: String,
}
const MAG_HIGHEST_POS: u8 = 6;
const MAG_LOWEST_NEG: u8 = 4;
fn assist(balance: &str, zeroes_after_point_before_value: u8) -> (String, Option<String>, i8) {
let out = format!(
"{}{}",
"0".repeat(zeroes_after_point_before_value as usize),
balance
);
(String::from("0"), Some(out), MAG_HIGHEST_POS as i8)
}
fn convert_balance_string(balance: &str, decimals: u8, unit: &str) -> Currency {
let order = (balance.len() as u8) - 1;
let transformed_number = match order {
0 => {
if balance == "0" {
let (before_point, after_point, mag) = {
if decimals <= MAG_HIGHEST_POS * 3 {
match decimals % 3 {
0 => (balance.to_string(), None, (decimals / 3) as i8),
1 => (
balance.to_string(),
Some(String::from("0")),
(decimals / 3) as i8,
),
2 => (
balance.to_string(),
Some(String::from("00")),
(decimals / 3) as i8,
),
_ => unreachable!(),
}
} else {
assist(balance, decimals - MAG_HIGHEST_POS * 3 - 1)
}
};
CutNumber {
before_point,
after_point,
mag,
}
} else {
let (before_point, after_point, mag) = {
if decimals <= MAG_HIGHEST_POS * 3 {
match decimals % 3 {
0 => (balance.to_string(), None, (decimals / 3) as i8),
1 => (format!("{balance}00"), None, (decimals / 3) as i8 + 1),
2 => (format!("{balance}0"), None, (decimals / 3) as i8 + 1),
_ => unreachable!(),
}
} else {
assist(balance, decimals - MAG_HIGHEST_POS * 3 - 1)
}
};
CutNumber {
before_point,
after_point,
mag,
}
}
}
1 => {
let (before_point, after_point, mag) = {
if order <= decimals {
if (decimals - order) <= MAG_HIGHEST_POS * 3 {
match (decimals + 2) % 3 {
0 => (
balance[..1].to_string(),
Some(balance[1..].to_string()),
(decimals / 3) as i8,
),
1 => (format!("{balance}0"), None, (decimals / 3) as i8 + 1),
2 => (balance.to_string(), None, (decimals / 3) as i8),
_ => unreachable!(),
}
} else {
assist(balance, decimals - order - MAG_HIGHEST_POS * 3 - 1)
}
} else {
(balance.to_string(), None, 0)
}
};
CutNumber {
before_point,
after_point,
mag,
}
}
2 => {
let (before_point, after_point, mag) = {
if order <= decimals {
if (decimals - order) <= MAG_HIGHEST_POS * 3 {
match (decimals + 1) % 3 {
0 => (
balance[..1].to_string(),
Some(balance[1..].to_string()),
(decimals / 3) as i8,
),
1 => (balance.to_string(), None, (decimals / 3) as i8),
2 => (
balance[..2].to_string(),
Some(balance[2..].to_string()),
(decimals / 3) as i8,
),
_ => unreachable!(),
}
} else {
assist(balance, decimals - order - MAG_HIGHEST_POS * 3 - 1)
}
} else if decimals == 0 {
(balance.to_string(), None, 0)
} else {
(balance[..2].to_string(), Some(balance[2..].to_string()), 0)
}
};
CutNumber {
before_point,
after_point,
mag,
}
}
_ => {
if order <= decimals {
let (before_point, after_point, mag) = {
if (decimals - order) <= MAG_HIGHEST_POS * 3 {
let (length, mag) = match (decimals - order) % 3 {
0 => (order as usize, ((decimals - order) / 3) as i8),
1 => ((order - 2) as usize, ((decimals - order) / 3) as i8 + 1),
2 => ((order - 1) as usize, ((decimals - order) / 3) as i8 + 1),
_ => unreachable!(),
};
let before_point = balance[..balance.len() - length].to_string();
let after_point = Some(balance[balance.len() - length..].to_string());
(before_point, after_point, mag)
} else {
assist(balance, decimals - order - MAG_HIGHEST_POS * 3 - 1)
}
};
CutNumber {
before_point,
after_point,
mag,
}
} else {
let (length, mag) = {
if (order - decimals) <= (MAG_LOWEST_NEG * 3) {
(
(order - (order - decimals) % 3) as usize,
-(((order - decimals) as i8) / 3),
)
} else {
(
(MAG_LOWEST_NEG * 3 + decimals) as usize,
-(MAG_LOWEST_NEG as i8),
)
}
};
let before_point = balance[..balance.len() - length].to_string();
let after_point = Some(balance[balance.len() - length..].to_string());
CutNumber {
before_point,
after_point,
mag,
}
}
}
};
let unit_prefix = match transformed_number.mag {
-4 => "T",
-3 => "G",
-2 => "M",
-1 => "k",
0 => "",
1 => "m",
2 => "u",
3 => "n",
4 => "p",
5 => "f",
6 => "a",
_ => unreachable!(),
};
let number = match transformed_number.after_point {
Some(x) => format!("{}.{}", transformed_number.before_point, x),
None => transformed_number.before_point.to_string(),
};
Currency {
number,
units: format!("{unit_prefix}{unit}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test01() {
let try_me = <u128>::convert_balance_pretty(0, 0, "X");
assert_eq!(try_me.number, "0");
assert_eq!(try_me.units, "X");
}
#[test]
fn test02() {
let try_me = <u128>::convert_balance_pretty(0, 1, "X");
assert_eq!(try_me.number, "0.0");
assert_eq!(try_me.units, "X");
}
#[test]
fn test03() {
let try_me = <u128>::convert_balance_pretty(0, 2, "X");
assert_eq!(try_me.number, "0.00");
assert_eq!(try_me.units, "X");
}
#[test]
fn test04() {
let try_me = <u128>::convert_balance_pretty(0, 3, "X");
assert_eq!(try_me.number, "0");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test05() {
let try_me = <u128>::convert_balance_pretty(0, 4, "X");
assert_eq!(try_me.number, "0.0");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test06() {
let try_me = <u128>::convert_balance_pretty(0, 20, "X");
assert_eq!(try_me.number, "0.00");
assert_eq!(try_me.units, "aX");
}
#[test]
fn test07() {
let try_me = <u128>::convert_balance_pretty(0, 24, "X");
assert_eq!(try_me.number, "0.000000");
assert_eq!(try_me.units, "aX");
}
#[test]
fn test08() {
let try_me = <u128>::convert_balance_pretty(0xffffffffffffffffffffffffffffffff, 0, "X");
assert_eq!(try_me.number, "340282366920938463463374607.431768211455");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test09() {
let try_me = <u128>::convert_balance_pretty(1, 0, "X");
assert_eq!(try_me.number, "1");
assert_eq!(try_me.units, "X");
}
#[test]
fn test10() {
let try_me = <u128>::convert_balance_pretty(1, 1, "X");
assert_eq!(try_me.number, "100");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test11() {
let try_me = <u128>::convert_balance_pretty(1, 2, "X");
assert_eq!(try_me.number, "10");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test12() {
let try_me = <u128>::convert_balance_pretty(1, 3, "X");
assert_eq!(try_me.number, "1");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test13() {
let try_me = <u128>::convert_balance_pretty(1, 4, "X");
assert_eq!(try_me.number, "100");
assert_eq!(try_me.units, "uX");
}
#[test]
fn test14() {
let try_me = <u128>::convert_balance_pretty(12, 0, "X");
assert_eq!(try_me.number, "12");
assert_eq!(try_me.units, "X");
}
#[test]
fn test15() {
let try_me = <u128>::convert_balance_pretty(12, 1, "X");
assert_eq!(try_me.number, "1.2");
assert_eq!(try_me.units, "X");
}
#[test]
fn test16() {
let try_me = <u128>::convert_balance_pretty(12, 2, "X");
assert_eq!(try_me.number, "120");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test17() {
let try_me = <u128>::convert_balance_pretty(12, 3, "X");
assert_eq!(try_me.number, "12");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test18() {
let try_me = <u128>::convert_balance_pretty(12, 4, "X");
assert_eq!(try_me.number, "1.2");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test19() {
let try_me = <u128>::convert_balance_pretty(123, 0, "X");
assert_eq!(try_me.number, "123");
assert_eq!(try_me.units, "X");
}
#[test]
fn test20() {
let try_me = <u128>::convert_balance_pretty(123, 1, "X");
assert_eq!(try_me.number, "12.3");
assert_eq!(try_me.units, "X");
}
#[test]
fn test21() {
let try_me = <u128>::convert_balance_pretty(123, 2, "X");
assert_eq!(try_me.number, "1.23");
assert_eq!(try_me.units, "X");
}
#[test]
fn test22() {
let try_me = <u128>::convert_balance_pretty(123, 3, "X");
assert_eq!(try_me.number, "123");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test23() {
let try_me = <u128>::convert_balance_pretty(123, 4, "X");
assert_eq!(try_me.number, "12.3");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test24() {
let try_me = <u128>::convert_balance_pretty(1, 40, "X");
assert_eq!(try_me.number, "0.0000000000000000000001");
assert_eq!(try_me.units, "aX");
}
#[test]
fn test25() {
let try_me = <u128>::convert_balance_pretty(12345, 21, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "aX");
}
#[test]
fn test26() {
let try_me = <u128>::convert_balance_pretty(12345, 18, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "fX");
}
#[test]
fn test27() {
let try_me = <u128>::convert_balance_pretty(12345, 15, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "pX");
}
#[test]
fn test28() {
let try_me = <u128>::convert_balance_pretty(12345, 12, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "nX");
}
#[test]
fn test29() {
let try_me = <u128>::convert_balance_pretty(12345, 10, "X");
assert_eq!(try_me.number, "1.2345");
assert_eq!(try_me.units, "uX");
}
#[test]
fn test30() {
let try_me = <u128>::convert_balance_pretty(12345, 9, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "uX");
}
#[test]
fn test31() {
let try_me = <u128>::convert_balance_pretty(12345, 6, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "mX");
}
#[test]
fn test32() {
let try_me = <u128>::convert_balance_pretty(12345, 3, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "X");
}
#[test]
fn test33() {
let try_me = <u128>::convert_balance_pretty(12345, 0, "X");
assert_eq!(try_me.number, "12.345");
assert_eq!(try_me.units, "kX");
}
#[test]
fn test34() {
let try_me = <u128>::convert_balance_pretty(123450000, 0, "X");
assert_eq!(try_me.number, "123.450000");
assert_eq!(try_me.units, "MX");
}
#[test]
fn test35() {
let try_me = <u128>::convert_balance_pretty(1234500000, 0, "X");
assert_eq!(try_me.number, "1.234500000");
assert_eq!(try_me.units, "GX");
assert_eq!(try_me.number == "1.234500000", try_me.units == "GX");
}
#[test]
fn test36() {
let try_me = <u128>::convert_balance_pretty(1234500000000, 0, "X");
assert_eq!(try_me.number, "1.234500000000");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test37() {
let try_me = <u128>::convert_balance_pretty(10000000000000001, 0, "X");
assert_eq!(try_me.number, "10000.000000000001");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test38() {
let try_me = <u128>::convert_balance_pretty(1234, 24, "X");
assert_eq!(try_me.number, "0.001234");
assert_eq!(try_me.units, "aX");
}
#[test]
fn test39() {
let try_me = <u128>::convert_balance_pretty(0xffffffffffffffffffffffffffffffff, 15, "X");
assert_eq!(try_me.number, "340282366920.938463463374607431768211455");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test40() {
let try_me = <u128>::convert_balance_pretty(0xffffffffffffffffffffffffffffffff, 18, "X");
assert_eq!(try_me.number, "340282366.920938463463374607431768211455");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test41() {
let try_me = <u128>::convert_balance_pretty(0xffffffffffffffffffffffffffffffff, 9, "X");
assert_eq!(try_me.number, "340282366920938463.463374607431768211455");
assert_eq!(try_me.units, "TX");
}
#[test]
fn test42() {
let try_me = <u128>::convert_balance_pretty(0xffffffffffffffffffffffffffffffff, 27, "X");
assert_eq!(try_me.number, "340.282366920938463463374607431768211455");
assert_eq!(try_me.units, "GX");
}
}