use crate::{Number, Value};
pub fn stringify(value: &Value) -> Option<String> {
match value {
Value::Bool(b) => Some(if *b { "true" } else { "false" }.to_string()),
Value::String(s) | Value::Datetime(s) => Some(s.clone()),
Value::Number(n) => Some(number(*n)),
Value::Null | Value::Array(_) | Value::Object(_) => None,
}
}
fn number(n: Number) -> String {
match n {
Number::I64(i) => i.to_string(),
Number::U64(u) => u.to_string(),
Number::F64(f) => float(f),
}
}
fn float(f: f64) -> String {
if f.is_finite() {
format!("{f:?}")
} else {
crate::value::nonfinite_spelling(f).to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Map;
#[test]
fn scalars_render() {
assert_eq!(stringify(&Value::Bool(true)).unwrap(), "true");
assert_eq!(stringify(&Value::Bool(false)).unwrap(), "false");
assert_eq!(stringify(&Value::String("hi".into())).unwrap(), "hi");
assert_eq!(
stringify(&Value::Datetime("1979-05-27T07:32:00Z".into())).unwrap(),
"1979-05-27T07:32:00Z"
);
}
#[test]
fn integral_floats_keep_their_point() {
assert_eq!(number(Number::F64(1.0)), "1.0");
assert_eq!(number(Number::F64(-0.0)), "-0.0");
assert_eq!(number(Number::F64(1.5)), "1.5");
assert_eq!(number(Number::I64(1)), "1");
assert_eq!(number(Number::U64(u64::MAX)), "18446744073709551615");
}
#[test]
fn extreme_magnitudes_go_exponential() {
assert_eq!(number(Number::F64(1e300)), "1e300");
assert_eq!(number(Number::F64(1e-7)), "1e-7");
}
#[test]
fn non_finite_floats_take_their_toml_spellings() {
assert_eq!(number(Number::F64(f64::INFINITY)), "inf");
assert_eq!(number(Number::F64(f64::NEG_INFINITY)), "-inf");
assert_eq!(number(Number::F64(f64::NAN)), "nan");
assert_eq!(number(Number::F64(-f64::NAN)), "nan");
}
#[test]
fn containers_and_null_have_no_spelling() {
assert_eq!(stringify(&Value::Null), None);
assert_eq!(stringify(&Value::Array(vec![])), None);
assert_eq!(stringify(&Value::Object(Map::new())), None);
}
}