use serde_json::{Map, Value};
use superposition_types::{Cac, Condition, Overrides};
use toml::Value as TomlValue;
use crate::{format::FormatError as TomlError, ConfigFormat, TomlFormat};
pub fn toml_to_json(value: TomlValue) -> Value {
match value {
TomlValue::String(s) => Value::String(s),
TomlValue::Integer(i) => Value::Number(i.into()),
TomlValue::Float(f) => serde_json::Number::from_f64(f)
.map(Value::Number)
.unwrap_or(Value::Null),
TomlValue::Boolean(b) => Value::Bool(b),
TomlValue::Datetime(dt) => Value::String(dt.to_string()),
TomlValue::Array(arr) => {
Value::Array(arr.into_iter().map(toml_to_json).collect())
}
TomlValue::Table(table) => {
let map: Map<String, Value> = table
.into_iter()
.map(|(k, v)| (k, toml_to_json(v)))
.collect();
Value::Object(map)
}
}
}
pub fn needs_quoting(key: &str) -> bool {
key.chars()
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-')
}
pub fn format_key(key: &str) -> String {
if needs_quoting(key) {
format!("\"{}\"", key.replace('"', r#"\"#))
} else {
key.to_string()
}
}
pub fn format_toml_value(value: &TomlValue) -> String {
match value {
TomlValue::String(s) => format!("\"{}\"", s.replace('"', r#"\"#)),
TomlValue::Integer(i) => i.to_string(),
TomlValue::Float(f) => f.to_string(),
TomlValue::Boolean(b) => b.to_string(),
TomlValue::Datetime(dt) => format!("\"{}\"", dt),
TomlValue::Array(arr) => {
let items: Vec<String> = arr.iter().map(format_toml_value).collect();
format!("[{}]", items.join(", "))
}
TomlValue::Table(table) => {
let mut keys: Vec<&String> = table.keys().collect();
keys.sort();
let entries: Vec<String> = keys
.into_iter()
.map(|k| format!("{} = {}", format_key(k), format_toml_value(&table[k])))
.collect();
format!("{{ {} }}", entries.join(", "))
}
}
}
pub fn try_condition_from_toml(ctx: toml::Table) -> Result<Condition, TomlError> {
let json = toml_to_json(TomlValue::Table(ctx));
let map = match json {
Value::Object(map) => map,
_ => return Err(TomlFormat::conversion_error("Context must be an object")),
};
Cac::<Condition>::try_from(map)
.map(|cac| cac.into_inner())
.map_err(|e| TomlFormat::conversion_error(format!("Invalid condition: {}", e)))
}
pub fn try_overrides_from_toml(overrides: toml::Table) -> Result<Overrides, TomlError> {
let json = toml_to_json(TomlValue::Table(overrides));
let map = match json {
Value::Object(map) => map,
_ => return Err(TomlFormat::conversion_error("Overrides must be an object")),
};
Cac::<Overrides>::try_from(map)
.map(|cac| cac.into_inner())
.map_err(|e| TomlFormat::conversion_error(format!("Invalid overrides: {}", e)))
}