use serde::{Serialize, de::DeserializeOwned};
use crate::format::{FormatOptions, Formatted, wrap_whitespace};
pub fn parse_toml<T>(
text: &str,
options: Option<FormatOptions>,
) -> Result<Formatted<T>, toml::de::Error>
where
T: DeserializeOwned,
{
let opts = FormatOptions {
preserve_indentation: false,
..options.unwrap_or_default()
};
let value = toml::from_str(text)?;
Ok(Formatted::new(text, value, &opts))
}
pub fn stringify_toml<T>(
formatted: &Formatted<T>,
_options: Option<FormatOptions>,
) -> Result<String, toml::ser::Error>
where
T: Serialize,
{
let toml_str = toml::to_string(&formatted.value)?;
Ok(wrap_whitespace(&toml_str, &formatted.format))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::strip_line_comments;
const FIXTURE: &str = r#"
[types]
boolean = true
integer = 1
float = 3.14
string = "hello"
array = [ 1, 2, 3 ]
null = "null"
date = "1979-05-27T15:32:00.000Z"
[types.object]
key = "value"
"#;
#[test]
fn parse_ok() {
#[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq)]
struct Types {
boolean: bool,
integer: i64,
float: f64,
string: String,
array: Vec<i64>,
null: String,
date: String,
object: Object,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq)]
struct Object {
key: String,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq)]
struct Root {
types: Types,
}
let formatted = parse_toml::<Root>(FIXTURE, None).unwrap();
assert_eq!(formatted.value.types.boolean, true);
assert_eq!(formatted.value.types.integer, 1);
assert!((formatted.value.types.float - 3.14).abs() < f64::EPSILON);
assert_eq!(formatted.value.types.string, "hello");
assert_eq!(formatted.value.types.array, vec![1, 2, 3]);
assert_eq!(formatted.value.types.null, "null");
assert_eq!(formatted.value.types.date, "1979-05-27T15:32:00.000Z");
assert_eq!(formatted.value.types.object.key, "value");
}
#[test]
fn stringify_roundtrip() {
#[derive(serde::Deserialize, serde::Serialize)]
struct Root {
types: std::collections::HashMap<String, toml::Value>,
}
let formatted = parse_toml::<Root>(FIXTURE, None).unwrap();
let out = stringify_toml(&formatted, None).unwrap();
let without_comments = strip_line_comments(FIXTURE, "#");
let expected = without_comments.trim();
let expected_val: toml::Value = toml::from_str(expected).unwrap();
let out_val: toml::Value = toml::from_str(out.trim()).unwrap();
assert_eq!(out_val, expected_val);
}
#[test]
fn preserves_outer_whitespace() {
let text = " \n[section]\nkey = 1\n\n";
#[derive(serde::Deserialize, serde::Serialize)]
struct Sectioned {
section: std::collections::HashMap<String, toml::Value>,
}
let formatted = parse_toml::<Sectioned>(text, None).unwrap();
let out = stringify_toml(&formatted, None).unwrap();
assert!(out.starts_with(" \n"));
assert!(out.ends_with("\n\n"));
}
}