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) }; } fn parse_decimal(s: &str) -> Result { 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> { 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 }) }