Created
October 19, 2022 10:22
-
-
Save ascjones/d85500620a5c5db4ef59657034876fc3 to your computer and use it in GitHub Desktop.
Denominated Balances
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::str::FromStr; | |
// Fast access for 10^n where n is 0-19 | |
pub const POWERS_10: [u128; 20] = [ | |
1, | |
10, | |
100, | |
1000, | |
10000, | |
100000, | |
1000000, | |
10000000, | |
100000000, | |
1000000000, | |
10000000000, | |
100000000000, | |
1000000000000, | |
10000000000000, | |
100000000000000, | |
1000000000000000, | |
10000000000000000, | |
100000000000000000, | |
1000000000000000000, | |
10000000000000000000, | |
]; | |
#[derive(Clone, Debug, Eq, PartialEq)] | |
pub struct Decimal { | |
value: u128, | |
scale: i8, | |
} | |
impl FromStr for Decimal { | |
type Err = String; | |
fn from_str(input: &str) -> Result<Self, Self::Err> { | |
let input = input.replace("_", ""); | |
let (digits, scale) = match input.split('.').collect::<Vec<_>>()[..] { | |
[h] => Ok((input, 0)), | |
[h, t] => { | |
let mut digits = String::from(h); | |
digits.push_str(t); | |
Ok((digits, t.len())) | |
}, | |
_ => Err(format!("Invalid input {}", input)) | |
}?; | |
let value = u128::from_str(&digits).expect("value u128"); | |
Ok(Decimal { value, scale: scale as i8 }) | |
} | |
} | |
impl Decimal { | |
pub fn new(value: u128, scale: i8) -> Self { | |
Self { value, scale } | |
} | |
pub fn scale(&self, new_scale: i8) -> Self { | |
if self.value == 0 { | |
return Self { value: 0, scale: new_scale } | |
} | |
if new_scale > self.scale { | |
let scale_diff = new_scale - self.scale; | |
let value = &self.value * POWERS_10[scale_diff as usize]; | |
Self::new(value, new_scale) | |
} else if new_scale < self.scale { | |
let scale_diff = self.scale - new_scale; | |
let value = &self.value / POWERS_10[scale_diff as usize]; | |
Self::new(value, new_scale) | |
} else { | |
self.clone() | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn one() { | |
let d = Decimal::from_str("1").unwrap(); | |
assert_eq!(Decimal::new(1, 0), d) | |
} | |
#[test] | |
fn scale() { | |
let one = Decimal::from_str("1").unwrap(); | |
let zero_point_one = one.scale(-1); | |
assert_eq!(Decimal::new(1, 1), zero_point_one) | |
} | |
} | |
#[derive(Debug, Clone)] | |
pub struct Balance { | |
value: u128, | |
unit: Option<UnitPrefix>, | |
symbol: Option<String>, | |
} | |
// impl FromStr for Balance { | |
// type Err = String; | |
// fn from_str(input: &str) -> Result<Self, Self::Err> { | |
// let input = input.replace('_', ""); | |
// match input.split(char::is_alphabetic).collect::<Vec<_>>()[..] { | |
// [decimal, suffix] => { | |
// let (prefix, symbol) = suffix.split_at(1); | |
// let unit = UnitPrefix::from_str(prefix)?; | |
// Ok(Self { | |
// value: Decimal::from_str(decimal)?, | |
// unit: Some(unit), | |
// symbol: Some(symbol.to_owned()), | |
// }) | |
// }, | |
// [decimal] => { | |
// Ok(Self { | |
// value: Decimal::from_str(decimal)?, | |
// unit: None, | |
// symbol: None | |
// }) | |
// } | |
// } | |
// } | |
// } | |
#[derive(Debug, Clone)] | |
pub enum UnitPrefix { | |
Giga, | |
Mega, | |
Kilo, | |
One, | |
Milli, | |
Micro, | |
Nano, | |
} | |
// impl FromStr for UnitPrefix { | |
// type Err = String; | |
// fn from_str(s: &str) -> Result<Self, Self::Err> { | |
// match s { | |
// "G" => Ok(Self::Giga), | |
// "M" => Ok(Self::Mega), | |
// "k" => Ok(Self::Kilo), | |
// "" => Ok(Self::One), | |
// "m" => Ok(Self::Milli), | |
// "μ" => Ok(Self::Micro), | |
// "n" => Ok(Self::Nano), | |
// _ => Err(format!("Invalid unit '{}'", s)) | |
// } | |
// } | |
// } | |
// impl ToString for UnitPrefix { | |
// fn to_string(&self) -> String { | |
// match self { | |
// UnitPrefix::Giga => "G", | |
// UnitPrefix::Mega => "M", | |
// UnitPrefix::Kilo => "k", | |
// UnitPrefix::One => "", | |
// UnitPrefix::Milli => "m", | |
// UnitPrefix::Micro => "μ", | |
// UnitPrefix::Nano => "n", | |
// }.to_owned() | |
// } | |
// } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment