pub mod expansion;
pub mod parser;
pub mod scanner;
pub mod validation;
use serde_json::Value;
use crate::types::{
DecodeOptions,
ToonResult,
};
pub fn decode<T: serde::de::DeserializeOwned>(
input: &str,
options: &DecodeOptions,
) -> ToonResult<T> {
let mut parser = parser::Parser::new(input, options.clone())?;
let value = parser.parse()?;
use crate::types::PathExpansionMode;
let final_value = if options.expand_paths != PathExpansionMode::Off {
let json_value = crate::types::JsonValue::from(value);
let expanded =
expansion::expand_paths_recursive(json_value, options.expand_paths, options.strict)?;
Value::from(expanded)
} else {
value
};
serde_json::from_value(final_value)
.map_err(|e| crate::types::ToonError::DeserializationError(e.to_string()))
}
pub fn decode_strict<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
decode(input, &DecodeOptions::new().with_strict(true))
}
pub fn decode_strict_with_options<T: serde::de::DeserializeOwned>(
input: &str,
options: &DecodeOptions,
) -> ToonResult<T> {
let opts = options.clone().with_strict(true);
decode(input, &opts)
}
pub fn decode_no_coerce<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
decode(input, &DecodeOptions::new().with_coerce_types(false))
}
pub fn decode_no_coerce_with_options<T: serde::de::DeserializeOwned>(
input: &str,
options: &DecodeOptions,
) -> ToonResult<T> {
let opts = options.clone().with_coerce_types(false);
decode(input, &opts)
}
pub fn decode_default<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
decode(input, &DecodeOptions::default())
}
#[cfg(test)]
mod tests {
use core::f64;
use serde_json::json;
use super::*;
#[test]
fn test_decode_null() {
assert_eq!(decode_default::<Value>("null").unwrap(), json!(null));
}
#[test]
fn test_decode_bool() {
assert_eq!(decode_default::<Value>("true").unwrap(), json!(true));
assert_eq!(decode_default::<Value>("false").unwrap(), json!(false));
}
#[test]
fn test_decode_number() {
assert_eq!(decode_default::<Value>("42").unwrap(), json!(42));
assert_eq!(
decode_default::<Value>("3.141592653589793").unwrap(),
json!(f64::consts::PI)
);
assert_eq!(decode_default::<Value>("-5").unwrap(), json!(-5));
}
#[test]
fn test_decode_string() {
assert_eq!(decode_default::<Value>("hello").unwrap(), json!("hello"));
assert_eq!(
decode_default::<Value>("\"hello world\"").unwrap(),
json!("hello world")
);
}
#[test]
fn test_decode_simple_object() {
let input = "name: Alice\nage: 30";
let result: Value = decode_default(input).unwrap();
assert_eq!(result["name"], json!("Alice"));
assert_eq!(result["age"], json!(30));
}
#[test]
fn test_decode_primitive_array() {
let input = "tags[3]: reading,gaming,coding";
let result: Value = decode_default(input).unwrap();
assert_eq!(result["tags"], json!(["reading", "gaming", "coding"]));
}
#[test]
fn test_decode_tabular_array() {
let input = "users[2]{id,name,role}:\n 1,Alice,admin\n 2,Bob,user";
let result: Value = decode_default(input).unwrap();
assert_eq!(
result["users"],
json!([
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "user"}
])
);
}
#[test]
fn test_decode_empty_array() {
let input = "items[0]:";
let result: Value = decode_default(input).unwrap();
assert_eq!(result["items"], json!([]));
}
#[test]
fn test_decode_quoted_strings() {
let input = "tags[3]: \"true\",\"42\",\"-3.14\"";
let result: Value = decode_default(input).unwrap();
assert_eq!(result["tags"], json!(["true", "42", "-3.14"]));
}
}