#[macro_export]
macro_rules! format_value {
($name:ident, $fmt_str:literal) => {
format_args! {
concat!($fmt_str, " {}{}"),
$name.mantissa,
$name.prefix,
match $name.base {
$crate::base::Base::B1000 => "",
$crate::base::Base::B1024 => if $name.prefix == $crate::prefix::Prefix::Unit {""} else {"i"},
},
}
};
($name:ident, $fmt_str:literal, groupings: $separator:expr) => {
format_args! {
"{} {}{}",
$crate::format::separated_float(&format!($fmt_str, $name.mantissa), $separator),
$name.prefix,
match $name.base {
$crate::base::Base::B1000 => "",
$crate::base::Base::B1024 => if $name.prefix == $crate::prefix::Prefix::Unit {""} else {"i"},
},
}
};
($name:ident, $fmt_str:literal, groupings: $separator:expr, no_unit) => {
format_args! {
"{}{}{}{}",
$crate::format::separated_float(&format!($fmt_str, $name.mantissa), $separator),
match $name.prefix {
$crate::prefix::Prefix::Unit => "",
_=> " "
},
$name.prefix,
match $name.base {
$crate::base::Base::B1000 => "",
$crate::base::Base::B1024 => if $name.prefix == $crate::prefix::Prefix::Unit {""} else {"i"},
},
}
};
}
pub fn separated_float(input: &str, separator: char) -> String {
let idx = match input.find('.') {
Some(i) => i,
None => input.len(),
};
let int_part = &input[..idx];
let frac_part = &input[idx..];
let int_part_separated = separate_thousands_backward(int_part, separator);
let frac_part_separated = separate_thousands_forward(frac_part, separator);
int_part_separated + &frac_part_separated
}
fn separate_thousands_backward(input: &str, separator: char) -> String {
let mut output = String::with_capacity(input.len() + input.len() / 4);
let mut pos = 0;
for ch in input.chars().rev() {
if ch.is_ascii_digit() {
if pos > 1 && pos % 3 == 0 {
output.push(separator);
}
pos += 1;
}
output.push(ch);
}
output.chars().rev().collect()
}
fn separate_thousands_forward(input: &str, separator: char) -> String {
let mut output = String::with_capacity(input.len() + input.len() / 4);
let mut pos = 0;
for ch in input.chars() {
if ch.is_ascii_digit() {
if pos > 1 && pos % 3 == 0 {
output.push(separator);
}
pos += 1;
}
output.push(ch);
}
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Value;
#[test]
fn format_value_without_groupings() {
let x = 3.4e-12f32;
let v: Value = x.into();
let unit = "F";
let actual = format!("result is {}{u}", format_value!(v, "{:>8.2}"), u = unit);
let expected = "result is 3.40 pF";
assert_eq!(actual, expected);
let actual = format!("result is {}{u}", format_value!(v, "{:<8.3}"), u = unit);
let expected = "result is 3.400 pF";
assert_eq!(actual, expected);
}
#[test]
fn format_value_with_groupings() {
let x = 1234.5678;
let v: Value = x.into();
let unit = "m";
let actual = format!(
"result is {}{u}",
format_value!(v, "{:.7}", groupings: '_'),
u = unit
);
let expected = "result is 1.234_567_8 km";
assert_eq!(actual, expected);
use crate::base::Base;
use crate::prefix::Constraint;
let v = Value::new_with(x, Base::B1000, Constraint::UnitAndBelow);
let unit = "s";
let actual = format!(
"result is {}{u}",
format_value!(v, "{:.5}", groupings: '_'),
u = unit
);
let expected = "result is 1_234.567_80 s";
assert_eq!(actual, expected);
}
#[test]
fn separate_float() {
let actual: String = separated_float("123456.123456", '_');
let expected = "123_456.123_456";
assert_eq!(actual, expected);
let actual: String = separated_float("123456789.123456789", '_');
let expected = "123_456_789.123_456_789";
assert_eq!(actual, expected);
let actual: String = separated_float("1234567.1234567", '_');
let expected = "1_234_567.123_456_7";
assert_eq!(actual, expected);
let actual: String = separated_float("--1234567.1234567++", '_');
let expected = "--1_234_567.123_456_7++";
assert_eq!(actual, expected);
}
#[test]
fn int_part_with_separate_thousands_backward() {
let actual = separate_thousands_backward("123456", '_');
let expected = "123_456";
assert_eq!(actual, expected);
let actual = separate_thousands_backward(" 123456..", '_');
let expected = " 123_456..";
assert_eq!(actual, expected);
}
#[test]
fn frac_part_with_separate_thousands_forward() {
let actual = separate_thousands_forward(".123456789", '_');
let expected = ".123_456_789";
assert_eq!(actual, expected);
let actual = separate_thousands_forward(".1234567--", '_');
let expected = ".123_456_7--";
assert_eq!(actual, expected);
}
#[test]
fn format_zero_value() {
let x = 0.0f32;
let v: Value = x.into();
let unit = "F";
let actual = format!("result is {}{u}", format_value!(v, "{:>8.2}"), u = unit);
let expected = "result is 0.00 F";
assert_eq!(actual, expected);
let actual = format!("result is {}{u}", format_value!(v, "{:<8.3}"), u = unit);
let expected = "result is 0.000 F";
assert_eq!(actual, expected);
}
}