Skip to content

Instantly share code, notes, and snippets.

@ascjones
Created October 19, 2022 10:22
Show Gist options
  • Save ascjones/d85500620a5c5db4ef59657034876fc3 to your computer and use it in GitHub Desktop.
Save ascjones/d85500620a5c5db4ef59657034876fc3 to your computer and use it in GitHub Desktop.
Denominated Balances
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