use toml::Value as Toml;
use super::policy::{Coercion, Value, collect};
pub(crate) fn extract(text: &str) -> Vec<f64> {
let Ok(parsed) = text.parse::<toml::Table>() else {
return Vec::new();
};
collect(
&Value::Map(parsed.values().map(convert).collect()),
Coercion::Typed,
)
}
fn convert(value: &Toml) -> Value {
match value {
Toml::Integer(number) => Value::Number(*number as f64),
Toml::Float(number) => Value::Number(*number),
Toml::String(text) => Value::Text(text.clone()),
Toml::Array(items) => Value::Seq(items.iter().map(convert).collect()),
Toml::Table(entries) => Value::Map(entries.values().map(convert).collect()),
Toml::Boolean(_) | Toml::Datetime(_) => Value::Other,
}
}
pub(crate) fn parse_error(text: &str) -> Option<String> {
text.parse::<toml::Table>()
.err()
.map(|error| format!("Failed to parse TOML: {error}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn integers_and_floats_are_both_numbers() {
assert_eq!(extract("a = 8080\nb = 0.0825"), [8080.0, 0.0825]);
}
#[test]
fn a_quoted_number_is_not_a_number() {
assert_eq!(extract("a = 42\nb = \"42\""), [42.0]);
}
#[test]
fn hex_and_underscored_literals_are_resolved_by_the_parser() {
assert_eq!(extract("a = 0x1A"), [26.0]);
assert_eq!(extract("a = 1_000"), [1000.0]);
}
#[test]
fn an_integer_past_the_safe_range_loses_precision_the_same_way() {
use crate::extract::render::js_number;
assert_eq!(
js_number(extract("a = 9007199254740993")[0]),
"9007199254740992"
);
}
#[test]
fn non_finite_values_are_dropped() {
assert!(extract("a = inf\nb = nan\nc = -inf").is_empty());
}
#[test]
fn a_datetime_is_not_a_number() {
assert!(extract("issued = 1979-05-27").is_empty());
}
#[test]
fn arrays_and_tables_are_followed() {
assert_eq!(
extract("limits = [1, 2]\n\n[owner]\nage = 30\n"),
[1.0, 2.0, 30.0]
);
}
#[test]
fn a_mixed_inline_array_parses_here_and_not_in_the_extension() {
assert_eq!(extract("limits = [1, 2.5]"), [1.0, 2.5]);
assert!(parse_error("limits = [1, 2.5]").is_none());
}
#[test]
fn a_broken_document_yields_nothing_and_says_why() {
assert!(extract("not = = toml").is_empty());
assert!(parse_error("not = = toml").is_some());
}
}