55 lines
1.4 KiB
Rust
55 lines
1.4 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use dashu::rational::RBig;
|
|
|
|
use crate::{PrecDec, prefixes, qty::Qty, unit};
|
|
|
|
macro_rules! _t {
|
|
($e:expr) => {
|
|
$e.map_err(|e| Box::new(e) as Box<dyn Debug>)
|
|
};
|
|
}
|
|
|
|
fn parse_decimal<const B: u32>(s: &str) -> Result<RBig, dashu::base::ParseError> {
|
|
if let Some((int_part, frac_part)) = s.split_once('.') {
|
|
let denominator = B.pow(frac_part.len() as u32);
|
|
let numerator = RBig::from_str_radix(&format!("{}{}", int_part, frac_part), B)?;
|
|
Ok(numerator / RBig::from(denominator))
|
|
} else {
|
|
Ok(RBig::from_str_radix(s, B)?)
|
|
}
|
|
}
|
|
|
|
pub fn qty(q: &str) -> Result<Qty, Box<dyn Debug>> {
|
|
let split_idx = q
|
|
.char_indices()
|
|
.rev()
|
|
.find(|&(_, c)| c.is_ascii_digit())
|
|
.map(|(i, _)| i + 1) // found digit and digits are 1 byte so no worries byt ugly
|
|
.unwrap_or(0);
|
|
|
|
let (amt, unt) = q.split_at(split_idx);
|
|
let mut amt: PrecDec = if amt.is_empty() {
|
|
PrecDec::from(1)
|
|
} else {
|
|
_t!(parse_decimal::<10>(amt))?
|
|
};
|
|
|
|
let unt = if let Some((_, rem, exp)) = prefixes::extract_prefix_and_rem(unt.trim()) {
|
|
let e = PrecDec::from(10isize.pow(exp.unsigned_abs().into()));
|
|
if exp < 0 {
|
|
amt /= e;
|
|
} else {
|
|
amt *= e;
|
|
}
|
|
|
|
rem
|
|
} else {
|
|
unt
|
|
};
|
|
|
|
let unt = _t!(unit::Unit::try_from(unt.trim()))?;
|
|
|
|
Ok(Qty { n: amt, u: unt })
|
|
}
|