use std::fmt;
use crate::sql_value::SqlValue;
fn format_f64(n: f64) -> String {
if n.is_nan() {
return "NaN".to_string();
}
if n.is_infinite() {
return if n > 0.0 { "Inf".to_string() } else { "-Inf".to_string() };
}
let n = if n == 0.0 { 0.0 } else { n };
let abs_n = n.abs();
if abs_n >= 1e15 || (abs_n < 1e-4 && abs_n != 0.0) {
let s = format!("{:.14e}", n);
return format_scientific_sqlite(&s);
}
format_with_significant_digits(n, 15)
}
fn format_with_significant_digits(n: f64, sig_digits: usize) -> String {
if n == 0.0 {
return "0.0".to_string();
}
let log10_abs = n.abs().log10();
let integer_digits = if log10_abs >= 0.0 { log10_abs.floor() as i32 + 1 } else { 0 };
let decimal_places = (sig_digits as i32 - integer_digits).max(0) as usize;
let formatted = format!("{:.prec$}", n, prec = decimal_places);
if formatted.contains('.') {
let trimmed = formatted.trim_end_matches('0');
if trimmed.ends_with('.') {
format!("{}0", trimmed)
} else {
trimmed.to_string()
}
} else {
format!("{}.0", formatted)
}
}
fn format_scientific_sqlite(s: &str) -> String {
if let Some(e_pos) = s.find('e') {
let (mantissa, exp_part) = s.split_at(e_pos);
let exp_str = &exp_part[1..];
let mantissa: std::borrow::Cow<'_, str> = if mantissa.contains('.') {
let trimmed = mantissa.trim_end_matches('0');
if trimmed.ends_with('.') {
std::borrow::Cow::Owned(format!("{}0", trimmed))
} else {
std::borrow::Cow::Borrowed(trimmed)
}
} else {
std::borrow::Cow::Owned(format!("{}.0", mantissa))
};
let (sign, exp_digits) = if let Some(stripped) = exp_str.strip_prefix('-') {
("-", stripped)
} else if let Some(stripped) = exp_str.strip_prefix('+') {
("+", stripped)
} else {
("+", exp_str)
};
let exp_num: i32 = exp_digits.parse().unwrap_or(0);
format!("{}e{}{:02}", mantissa, sign, exp_num.abs())
} else {
s.to_string()
}
}
fn format_f32(n: f32) -> String {
if n.is_nan() {
return "NaN".to_string();
}
if n.is_infinite() {
return if n > 0.0 { "Inf".to_string() } else { "-Inf".to_string() };
}
let n = if n == 0.0 { 0.0 } else { n };
let abs_n = n.abs();
if abs_n >= 1e15 || (abs_n < 1e-4 && abs_n != 0.0) {
let s = format!("{:.14e}", n);
return format_scientific_sqlite(&s);
}
let mut buffer = ryu::Buffer::new();
let s = buffer.format(n);
s.to_string()
}
impl fmt::Display for SqlValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SqlValue::Integer(i) => write!(f, "{}", i),
SqlValue::Smallint(i) => write!(f, "{}", i),
SqlValue::Bigint(i) => write!(f, "{}", i),
SqlValue::Unsigned(u) => write!(f, "{}", u),
SqlValue::Numeric(n) => write!(f, "{}", format_f64(*n)),
SqlValue::Float(n) => write!(f, "{}", format_f32(*n)),
SqlValue::Real(n) => write!(f, "{}", format_f64(*n)),
SqlValue::Double(n) => write!(f, "{}", format_f64(*n)),
SqlValue::Character(s) => write!(f, "{}", s),
SqlValue::Varchar(s) => write!(f, "{}", s),
SqlValue::Boolean(true) => write!(f, "TRUE"),
SqlValue::Boolean(false) => write!(f, "FALSE"),
SqlValue::Date(s) => write!(f, "{}", s),
SqlValue::Time(s) => write!(f, "{}", s),
SqlValue::Timestamp(s) => write!(f, "{}", s),
SqlValue::Interval(s) => write!(f, "{}", s),
SqlValue::Vector(v) => {
let formatted: Vec<String> = v.iter().map(|x| x.to_string()).collect();
write!(f, "[{}]", formatted.join(", "))
}
SqlValue::Blob(b) => {
if let Ok(s) = std::str::from_utf8(b) {
write!(f, "{}", s)
} else {
for byte in b {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}
SqlValue::Null => write!(f, "NULL"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_f64_helper() {
assert_eq!(format_f64(1.1), "1.1");
assert_eq!(format_f64(2.2), "2.2");
assert_eq!(format_f64(1.0), "1.0");
assert_eq!(format_f64(2.0), "2.0");
assert_eq!(format_f64(0.0), "0.0");
assert_eq!(format_f64(123.456), "123.456");
assert_eq!(format_f64(0.5), "0.5");
assert_eq!(format_f64(100.0), "100.0");
assert_eq!(format_f64(-4373.0), "-4373.0");
assert_eq!(format_f64(-4373.123), "-4373.123");
}
#[test]
fn test_format_f32_helper() {
assert_eq!(format_f32(1.1f32), "1.1");
assert_eq!(format_f32(2.2f32), "2.2");
assert_eq!(format_f32(1.0f32), "1.0");
assert_eq!(format_f32(0.0f32), "0.0");
assert_eq!(format_f32(123.456f32), "123.456");
assert_eq!(format_f32(0.5f32), "0.5");
assert_eq!(format_f32(100.0f32), "100.0");
assert_eq!(format_f32(-4373.0f32), "-4373.0");
}
#[test]
fn test_format_f64_scientific() {
assert_eq!(format_f64(1e15), "1.0e+15");
assert_eq!(format_f64(1e16), "1.0e+16");
assert_eq!(format_f64(0.00001), "1.0e-05");
assert_eq!(format_f64(1e-10), "1.0e-10");
assert_eq!(format_f64(1e20), "1.0e+20"); assert_eq!(format_f64(1e-20), "1.0e-20"); assert_eq!(format_f64(1.5e20), "1.5e+20"); assert_eq!(format_f64(9.99e30), "9.99e+30"); assert_eq!(format_f64(1e100), "1.0e+100"); assert_eq!(format_f64(1e308), "1.0e+308"); assert_eq!(format_f64(-1e20), "-1.0e+20"); assert_eq!(format_f64(1.23456789012346e15), "1.23456789012346e+15"); }
#[test]
fn test_format_f64_scientific_boundary() {
assert_eq!(format_f64(1e14), "100000000000000.0"); assert_eq!(format_f64(1e15), "1.0e+15"); assert_eq!(format_f64(0.0001), "0.0001"); assert_eq!(format_f64(0.00001), "1.0e-05"); assert_eq!(format_f64(0.0), "0.0");
}
#[test]
fn test_format_scientific_sqlite() {
assert_eq!(format_scientific_sqlite("1e15"), "1.0e+15");
assert_eq!(format_scientific_sqlite("1e5"), "1.0e+05");
assert_eq!(format_scientific_sqlite("1.00000000000000e20"), "1.0e+20");
assert_eq!(format_scientific_sqlite("1.5e-5"), "1.5e-05");
assert_eq!(format_scientific_sqlite("1.5e-15"), "1.5e-15");
assert_eq!(format_scientific_sqlite("9.99000000000000e30"), "9.99e+30");
assert_eq!(format_scientific_sqlite("9.22337203685478e18"), "9.22337203685478e+18");
assert_eq!(format_scientific_sqlite("1.00000000000000e100"), "1.0e+100");
}
#[test]
fn test_numeric_display_whole_numbers() {
assert_eq!(format!("{}", SqlValue::Numeric(32.0)), "32.0");
assert_eq!(format!("{}", SqlValue::Numeric(-4373.0)), "-4373.0");
assert_eq!(format!("{}", SqlValue::Numeric(0.0)), "0.0");
assert_eq!(format!("{}", SqlValue::Numeric(164.0)), "164.0");
}
#[test]
fn test_numeric_display_fractional() {
assert_eq!(format!("{}", SqlValue::Numeric(32.5)), "32.5");
assert_eq!(format!("{}", SqlValue::Numeric(-4373.123)), "-4373.123");
assert_eq!(format!("{}", SqlValue::Numeric(0.5)), "0.5");
assert_eq!(format!("{}", SqlValue::Numeric(1.1)), "1.1");
}
#[test]
fn test_numeric_display_special_values() {
assert_eq!(format!("{}", SqlValue::Numeric(f64::NAN)), "NaN");
assert_eq!(format!("{}", SqlValue::Numeric(f64::INFINITY)), "Inf");
assert_eq!(format!("{}", SqlValue::Numeric(f64::NEG_INFINITY)), "-Inf");
}
#[test]
fn test_float_display_whole_numbers() {
assert_eq!(format!("{}", SqlValue::Float(32.0)), "32.0");
assert_eq!(format!("{}", SqlValue::Float(-4373.0)), "-4373.0");
assert_eq!(format!("{}", SqlValue::Float(0.0)), "0.0");
assert_eq!(format!("{}", SqlValue::Float(127.75)), "127.75");
}
#[test]
fn test_real_display_fractional() {
assert_eq!(format!("{}", SqlValue::Real(32.5)), "32.5");
assert_eq!(format!("{}", SqlValue::Real(0.5)), "0.5");
assert_eq!(format!("{}", SqlValue::Real(1.1)), "1.1");
assert_eq!(format!("{}", SqlValue::Real(2.2)), "2.2");
}
#[test]
fn test_double_display_special_values() {
assert_eq!(format!("{}", SqlValue::Double(f64::NAN)), "NaN");
assert_eq!(format!("{}", SqlValue::Double(f64::INFINITY)), "Inf");
assert_eq!(format!("{}", SqlValue::Double(f64::NEG_INFINITY)), "-Inf");
assert_eq!(format!("{}", SqlValue::Double(123.45)), "123.45");
}
#[test]
fn test_format_f64_whole_numbers() {
assert_eq!(format_f64(45.0), "45.0");
assert_eq!(format_f64(100.0), "100.0");
assert_eq!(format_f64(0.0), "0.0");
assert_eq!(format_f64(45.5), "45.5");
assert_eq!(format_f64(123.456), "123.456");
}
#[test]
fn test_format_negative_zero() {
assert_eq!(format_f64(-0.0), "0.0");
assert_eq!(format_f32(-0.0f32), "0.0");
assert_eq!(format_f64(0.0 * -1.0), "0.0");
}
#[test]
fn test_blob_display_utf8() {
assert_eq!(format!("{}", SqlValue::Blob(vec![0x61, 0x62, 0x63])), "abc");
assert_eq!(format!("{}", SqlValue::Blob(vec![0x68, 0x61, 0x72, 0x65])), "hare");
assert_eq!(format!("{}", SqlValue::Blob(vec![0x68, 0x65, 0x6c, 0x6c, 0x6f])), "hello");
}
#[test]
fn test_blob_display_invalid_utf8() {
assert_eq!(format!("{}", SqlValue::Blob(vec![0xFF, 0xFE])), "FFFE");
assert_eq!(format!("{}", SqlValue::Blob(vec![0x80, 0x81, 0x82])), "808182");
}
#[test]
fn test_blob_display_empty() {
assert_eq!(format!("{}", SqlValue::Blob(vec![])), "");
}
#[test]
fn test_integer_display_no_scientific_notation() {
assert_eq!(format!("{}", SqlValue::Integer(i64::MAX)), "9223372036854775807");
assert_eq!(format!("{}", SqlValue::Bigint(i64::MAX)), "9223372036854775807");
assert_eq!(format!("{}", SqlValue::Integer(i64::MIN)), "-9223372036854775808");
assert_eq!(format!("{}", SqlValue::Integer(1_000_000_000_000_000)), "1000000000000000");
assert_eq!(format!("{}", SqlValue::Integer(999_999_999_999_999)), "999999999999999");
assert_eq!(format!("{}", SqlValue::Integer(10_000_000_000_000_000)), "10000000000000000");
}
#[test]
fn test_small_integer_display() {
assert_eq!(format!("{}", SqlValue::Integer(0)), "0");
assert_eq!(format!("{}", SqlValue::Integer(42)), "42");
assert_eq!(format!("{}", SqlValue::Integer(-100)), "-100");
assert_eq!(format!("{}", SqlValue::Integer(1_000_000)), "1000000");
assert_eq!(format!("{}", SqlValue::Bigint(123456789)), "123456789");
}
#[test]
fn test_unsigned_integer_display() {
assert_eq!(format!("{}", SqlValue::Unsigned(u64::MAX)), "18446744073709551615");
assert_eq!(format!("{}", SqlValue::Unsigned(1_000_000_000_000_000)), "1000000000000000");
assert_eq!(format!("{}", SqlValue::Unsigned(999_999_999_999_999)), "999999999999999");
}
}