refactor(tvix/eval): extract float formatting into a helper
This keeps the actual TotalDisplay implementation readable, as this float formatting code suddenly made up the majority of its implementation. Change-Id: I2c0d00e4a691e0b8ffbc72680f680e16feef4bee Reviewed-on: https://cl.tvl.fyi/c/depot/+/7925 Reviewed-by: flokli <flokli@flokli.de> Autosubmit: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
This commit is contained in:
parent
d05c380504
commit
164005656d
1 changed files with 75 additions and 71 deletions
|
@ -514,6 +514,80 @@ impl Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emulates the C++-Nix style formatting of floats, which diverges
|
||||||
|
/// significantly from Rust's native float formatting.
|
||||||
|
fn total_fmt_float<F: std::fmt::Write>(num: f64, mut f: F) -> std::fmt::Result {
|
||||||
|
let mut buf = [b'0'; lexical_core::BUFFER_SIZE];
|
||||||
|
let mut s = lexical_core::write_with_options::<f64, { CXX_LITERAL }>(
|
||||||
|
num.clone(),
|
||||||
|
&mut buf,
|
||||||
|
&WRITE_FLOAT_OPTIONS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// apply some postprocessing on the buffer. If scientific
|
||||||
|
// notation is used (we see an `e`), and the next character is
|
||||||
|
// a digit, add the missing `+` sign.)
|
||||||
|
let mut new_s = Vec::with_capacity(s.len());
|
||||||
|
|
||||||
|
if s.contains(&b'e') {
|
||||||
|
for (i, c) in s.iter().enumerate() {
|
||||||
|
// encountered `e`
|
||||||
|
if c == &b'e' {
|
||||||
|
// next character is a digit (so no negative exponent)
|
||||||
|
if s.len() > i && s[i + 1].is_ascii_digit() {
|
||||||
|
// copy everything from the start up to (including) the e
|
||||||
|
new_s.extend_from_slice(&s[0..=i]);
|
||||||
|
// add the missing '+'
|
||||||
|
new_s.push(b'+');
|
||||||
|
// check for the remaining characters.
|
||||||
|
// If it's only one, we need to prepend a trailing zero
|
||||||
|
if s.len() == i + 2 {
|
||||||
|
new_s.push(b'0');
|
||||||
|
}
|
||||||
|
new_s.extend_from_slice(&s[i + 1..]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we modified the scientific notation, flip the reference
|
||||||
|
if new_s.len() != 0 {
|
||||||
|
s = &mut new_s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else, if this is not scientific notation, and there's a
|
||||||
|
// decimal point, make sure we really drop trailing zeroes.
|
||||||
|
// In some cases, lexical_core doesn't.
|
||||||
|
else if s.contains(&b'.') {
|
||||||
|
for (i, c) in s.iter().enumerate() {
|
||||||
|
// at `.``
|
||||||
|
if c == &b'.' {
|
||||||
|
// trim zeroes from the right side.
|
||||||
|
let frac = String::from_utf8_lossy(&s[i + 1..]);
|
||||||
|
let frac_no_trailing_zeroes = frac.trim_end_matches("0");
|
||||||
|
|
||||||
|
if frac.len() != frac_no_trailing_zeroes.len() {
|
||||||
|
// we managed to strip something, construct new_s
|
||||||
|
if frac_no_trailing_zeroes.is_empty() {
|
||||||
|
// if frac_no_trailing_zeroes is empty, the fractional part was all zeroes, so we can drop the decimal point as well
|
||||||
|
new_s.extend_from_slice(&s[0..=i - 1]);
|
||||||
|
} else {
|
||||||
|
// else, assemble the rest of the string
|
||||||
|
new_s.extend_from_slice(&s[0..=i]);
|
||||||
|
new_s.extend_from_slice(frac_no_trailing_zeroes.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip the reference
|
||||||
|
s = &mut new_s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", format!("{}", String::from_utf8_lossy(&s)))
|
||||||
|
}
|
||||||
|
|
||||||
impl TotalDisplay for Value {
|
impl TotalDisplay for Value {
|
||||||
fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
|
fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -531,77 +605,7 @@ impl TotalDisplay for Value {
|
||||||
// Nix prints floats with a maximum precision of 5 digits
|
// Nix prints floats with a maximum precision of 5 digits
|
||||||
// only. Except when it decides to use scientific notation
|
// only. Except when it decides to use scientific notation
|
||||||
// (with a + after the `e`, and zero-padded to 0 digits)
|
// (with a + after the `e`, and zero-padded to 0 digits)
|
||||||
Value::Float(num) => {
|
Value::Float(num) => total_fmt_float(*num, f),
|
||||||
let mut buf = [b'0'; lexical_core::BUFFER_SIZE];
|
|
||||||
let mut s = lexical_core::write_with_options::<f64, { CXX_LITERAL }>(
|
|
||||||
num.clone(),
|
|
||||||
&mut buf,
|
|
||||||
&WRITE_FLOAT_OPTIONS,
|
|
||||||
);
|
|
||||||
|
|
||||||
// apply some postprocessing on the buffer. If scientific
|
|
||||||
// notation is used (we see an `e`), and the next character is
|
|
||||||
// a digit, add the missing `+` sign.)
|
|
||||||
let mut new_s = Vec::with_capacity(s.len());
|
|
||||||
|
|
||||||
if s.contains(&b'e') {
|
|
||||||
for (i, c) in s.iter().enumerate() {
|
|
||||||
// encountered `e`
|
|
||||||
if c == &b'e' {
|
|
||||||
// next character is a digit (so no negative exponent)
|
|
||||||
if s.len() > i && s[i + 1].is_ascii_digit() {
|
|
||||||
// copy everything from the start up to (including) the e
|
|
||||||
new_s.extend_from_slice(&s[0..=i]);
|
|
||||||
// add the missing '+'
|
|
||||||
new_s.push(b'+');
|
|
||||||
// check for the remaining characters.
|
|
||||||
// If it's only one, we need to prepend a trailing zero
|
|
||||||
if s.len() == i + 2 {
|
|
||||||
new_s.push(b'0');
|
|
||||||
}
|
|
||||||
new_s.extend_from_slice(&s[i + 1..]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we modified the scientific notation, flip the reference
|
|
||||||
if new_s.len() != 0 {
|
|
||||||
s = &mut new_s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// else, if this is not scientific notation, and there's a
|
|
||||||
// decimal point, make sure we really drop trailing zeroes.
|
|
||||||
// In some cases, lexical_core doesn't.
|
|
||||||
else if s.contains(&b'.') {
|
|
||||||
for (i, c) in s.iter().enumerate() {
|
|
||||||
// at `.``
|
|
||||||
if c == &b'.' {
|
|
||||||
// trim zeroes from the right side.
|
|
||||||
let frac = String::from_utf8_lossy(&s[i + 1..]);
|
|
||||||
let frac_no_trailing_zeroes = frac.trim_end_matches("0");
|
|
||||||
|
|
||||||
if frac.len() != frac_no_trailing_zeroes.len() {
|
|
||||||
// we managed to strip something, construct new_s
|
|
||||||
if frac_no_trailing_zeroes.is_empty() {
|
|
||||||
// if frac_no_trailing_zeroes is empty, the fractional part was all zeroes, so we can drop the decimal point as well
|
|
||||||
new_s.extend_from_slice(&s[0..=i - 1]);
|
|
||||||
} else {
|
|
||||||
// else, assemble the rest of the string
|
|
||||||
new_s.extend_from_slice(&s[0..=i]);
|
|
||||||
new_s.extend_from_slice(frac_no_trailing_zeroes.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// flip the reference
|
|
||||||
s = &mut new_s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{}", format!("{}", String::from_utf8_lossy(&s)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal types
|
// internal types
|
||||||
Value::AttrNotFound => f.write_str("internal[not found]"),
|
Value::AttrNotFound => f.write_str("internal[not found]"),
|
||||||
|
|
Loading…
Reference in a new issue