use super::FromToml;
use crate::Item;
use crate::arena::Arena;
use crate::error::Error;
use crate::span::Spanned;
fn parse_val<'a, T: FromToml<'a>>(input: &'a str, arena: &'a Arena) -> Result<T, Error> {
let mut doc = crate::parser::parse(input, arena).unwrap();
let result = {
let mut helper = doc.table_helper();
helper.required::<T>("v")
};
match result {
Ok(val) => Ok(val),
Err(_) => Err(doc.ctx.errors.remove(0)),
}
}
#[test]
fn deser_strings() {
let arena = Arena::new();
let val: String = parse_val(r#"v = "hello""#, &arena).unwrap();
assert_eq!(val, "hello");
let val: &str = parse_val(r#"v = "borrowed""#, &arena).unwrap();
assert_eq!(val, "borrowed");
let val: std::borrow::Cow<'_, str> = parse_val(r#"v = "cow""#, &arena).unwrap();
assert_eq!(&*val, "cow");
}
#[test]
fn deser_booleans() {
let arena = Arena::new();
let val: bool = parse_val("v = true", &arena).unwrap();
assert!(val);
let val: bool = parse_val("v = false", &arena).unwrap();
assert!(!val);
let err = parse_val::<bool>(r#"v = "not a bool""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
}
#[test]
fn deser_integers() {
let arena = Arena::new();
let val: i8 = parse_val("v = 42", &arena).unwrap();
assert_eq!(val, 42);
let val: i16 = parse_val("v = 1000", &arena).unwrap();
assert_eq!(val, 1000);
let val: i32 = parse_val("v = 100000", &arena).unwrap();
assert_eq!(val, 100000);
let val: i64 = parse_val("v = 9999999999", &arena).unwrap();
assert_eq!(val, 9999999999);
let val: isize = parse_val("v = -42", &arena).unwrap();
assert_eq!(val, -42);
let val: u8 = parse_val("v = 255", &arena).unwrap();
assert_eq!(val, 255);
let val: u16 = parse_val("v = 65535", &arena).unwrap();
assert_eq!(val, 65535);
let val: u32 = parse_val("v = 100000", &arena).unwrap();
assert_eq!(val, 100000);
let val: u64 = parse_val("v = 9999999999", &arena).unwrap();
assert_eq!(val, 9999999999);
let val: usize = parse_val("v = 42", &arena).unwrap();
assert_eq!(val, 42);
let err = parse_val::<i8>("v = 200", &arena).unwrap_err();
assert!(matches!(
err.kind(),
crate::ErrorKind::OutOfRange { ty: &"i8", .. }
));
let err = parse_val::<u8>("v = 256", &arena).unwrap_err();
assert!(matches!(
err.kind(),
crate::ErrorKind::OutOfRange { ty: &"u8", .. }
));
let err = parse_val::<u64>("v = -1", &arena).unwrap_err();
assert!(matches!(
err.kind(),
crate::ErrorKind::OutOfRange { ty: &"u64", .. }
));
let err = parse_val::<usize>("v = -1", &arena).unwrap_err();
assert!(matches!(
err.kind(),
crate::ErrorKind::OutOfRange { ty: &"usize", .. }
));
let err = parse_val::<i32>(r#"v = "not an int""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
}
#[test]
fn deser_floats() {
let arena = Arena::new();
let val: f32 = parse_val("v = 3.15", &arena).unwrap();
assert!((val - 3.15_f32).abs() < 0.001);
let val: f64 = parse_val("v = 3.15", &arena).unwrap();
assert!((val - 3.15).abs() < f64::EPSILON);
let err = parse_val::<f64>(r#"v = "not a float""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let err = parse_val::<f32>(r#"v = "not a float""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
}
#[test]
fn deser_vecs() {
let arena = Arena::new();
let val: Vec<i64> = parse_val("v = [1, 2, 3]", &arena).unwrap();
assert_eq!(val, vec![1, 2, 3]);
let val: Vec<String> = parse_val(r#"v = ["a", "b"]"#, &arena).unwrap();
assert_eq!(val, vec!["a", "b"]);
let val: Vec<i64> = parse_val("v = []", &arena).unwrap();
assert!(val.is_empty());
let err = parse_val::<Vec<i64>>(r#"v = "not an array""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
}
#[test]
fn deser_spanned() {
let arena = Arena::new();
let input = "v = 42";
let mut doc = crate::parser::parse(input, &arena).unwrap();
let val: Spanned<i64> = {
let mut helper = doc.table_helper();
helper.required("v").unwrap()
};
assert_eq!(val.value, 42);
assert_eq!(&input[val.span.start as usize..val.span.end as usize], "42");
}
#[test]
fn into_remaining() {
fn check(key_count: usize, use_every_nth: usize) {
let mut toml = String::new();
for i in 0..key_count {
if !toml.is_empty() {
toml.push('\n');
}
toml.push_str(&format!("k{i} = {i}"));
}
let arena = Arena::new();
let mut doc = crate::parser::parse(&toml, &arena).unwrap();
let mut helper = doc.table_helper();
let mut expected_remaining = Vec::new();
for i in 0..key_count {
let name = format!("k{i}");
if use_every_nth > 0 && i % use_every_nth == 0 {
let _: Option<i64> = helper.optional(&name);
} else {
expected_remaining.push(name);
}
}
let keys: Vec<_> = helper.into_remaining().map(|(k, _)| k.name).collect();
assert_eq!(
keys, expected_remaining,
"key_count={key_count} use_every_nth={use_every_nth}"
);
}
check(0, 0);
check(3, 0);
check(3, 2);
check(3, 1);
check(64, 0);
check(64, 2);
check(64, 1);
check(65, 0);
check(65, 3);
if !cfg!(miri) {
check(65, 1);
check(150, 0);
check(150, 5);
check(150, 1);
}
}
#[test]
fn table_helper_workflows() {
let arena = Arena::new();
let mut doc = crate::parser::parse("a = 1\nb = 2", &arena).unwrap();
{
let mut helper = doc.table_helper();
let _: i64 = helper.required("a").unwrap();
let _: i64 = helper.required("b").unwrap();
assert_eq!(helper.remaining_count(), 0);
helper.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1\nb = 2\nc = 3", &arena).unwrap();
{
let mut helper = doc.table_helper();
let _: i64 = helper.required("a").unwrap();
assert_eq!(helper.remaining_count(), 2);
assert!(helper.require_empty().is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::UnexpectedKey { .. }
));
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut helper = doc.table_helper();
assert!(helper.required::<i64>("nonexistent").is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::MissingField("nonexistent")
));
let mut doc = crate::parser::parse(r#"a = "string""#, &arena).unwrap();
{
let mut helper = doc.table_helper();
assert!(helper.optional::<i64>("nonexistent").is_none());
assert!(helper.optional::<i64>("a").is_none());
}
assert_eq!(doc.ctx.errors.len(), 1);
let mut lines = Vec::new();
for i in 0..8 {
lines.push(format!("k{i} = {i}"));
}
let input = lines.join("\n");
let mut doc = crate::parser::parse(&input, &arena).unwrap();
{
let mut helper = doc.table_helper();
let v: i64 = helper.required("k0").unwrap();
assert_eq!(v, 0);
let v: i64 = helper.required("k7").unwrap();
assert_eq!(v, 7);
assert!(helper.required::<i64>("nonexistent").is_err());
assert_eq!(helper.remaining_count(), 6);
}
}
#[test]
fn deser_boxed_and_array_types() {
let arena = Arena::new();
let val: Box<str> = parse_val(r#"v = "boxed""#, &arena).unwrap();
assert_eq!(&*val, "boxed");
let err = parse_val::<Box<str>>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let val: Box<i64> = parse_val("v = 42", &arena).unwrap();
assert_eq!(*val, 42);
let err = parse_val::<Box<i64>>(r#"v = "nope""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let val: Box<[i64]> = parse_val("v = [1, 2, 3]", &arena).unwrap();
assert_eq!(&*val, &[1, 2, 3]);
let err = parse_val::<Box<[i64]>>(r#"v = "nope""#, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let err = parse_val::<String>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let val: [i64; 3] = parse_val("v = [1, 2, 3]", &arena).unwrap();
assert_eq!(val, [1, 2, 3]);
let err = parse_val::<[i64; 2]>("v = [1, 2, 3]", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Custom(_)));
let err = parse_val::<&str>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let err = parse_val::<std::borrow::Cow<'_, str>>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let mut doc = crate::parser::parse(r#"v = [1, "bad", 3, "worse"]"#, &arena).unwrap();
let result = {
let mut helper = doc.table_helper();
helper.required::<Vec<i64>>("v")
};
assert!(result.is_err());
assert_eq!(doc.ctx.errors.len(), 2);
}
#[test]
fn require_custom_string_and_context_errors() {
let arena = Arena::new();
let mut doc = crate::parser::parse("ip = \"127.0.0.1\"\nport = 8080", &arena).unwrap();
{
let helper = doc.table_helper();
let (_, ip_item) = helper.get_entry("ip").unwrap();
assert_eq!(
ip_item
.require_custom_string(helper.ctx, &"an IPv4 address")
.unwrap(),
"127.0.0.1"
);
let (_, port_item) = helper.get_entry("port").unwrap();
assert!(
port_item
.require_custom_string(helper.ctx, &"an IPv4 address")
.is_err()
);
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::Wanted { .. }
));
let mut doc = crate::parser::parse("[sub]\na = 1\nval = 42", &arena).unwrap();
{
let helper = doc.table_helper();
let (_, sub_item) = helper.get_entry("sub").unwrap();
let mut th = sub_item.table_helper(helper.ctx).unwrap();
let v: i64 = th.required("a").unwrap();
assert_eq!(v, 1);
}
let mut doc = crate::parser::parse("val = 42", &arena).unwrap();
{
let helper = doc.table_helper();
let (_, val_item) = helper.get_entry("val").unwrap();
assert!(val_item.table_helper(helper.ctx).is_err());
}
let span = crate::Span::new(0, 5);
let mut ctx = super::Context {
arena: &arena,
index: Default::default(),
errors: Vec::new(),
source: "",
};
let _ = ctx.report_error_at("something went wrong", span);
let _ = ctx.push_error(Error::new(crate::ErrorKind::InvalidInteger(""), span));
assert_eq!(ctx.errors.len(), 2);
assert!(matches!(ctx.errors[0].kind(), crate::ErrorKind::Custom(_)));
assert!(matches!(
ctx.errors[1].kind(),
crate::ErrorKind::InvalidInteger(_)
));
}
#[test]
fn root_methods() {
let arena = Arena::new();
let doc = crate::parser::parse("a = 1\nb = 2", &arena).unwrap();
assert_eq!(doc.table().len(), 2);
assert_eq!(doc["a"].as_i64(), Some(1));
assert!(doc.errors().is_empty());
assert!(!doc.has_errors());
let debug = format!("{:?}", doc);
assert!(debug.contains("a"));
let doc = crate::parser::parse("x = 42", &arena).unwrap();
let item = doc.into_item();
assert_eq!(item.as_table().unwrap().len(), 1);
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
let err = doc.to::<i64>().unwrap_err();
assert!(!err.errors.is_empty());
}
#[test]
fn required_item_and_optional_item() {
let arena = Arena::new();
let mut doc = crate::parser::parse(
r#"
s = "hello"
i = 42
f = 3.15
b = true
a = [1, 2]
[t]
x = 1
"#,
&arena,
)
.unwrap();
{
let mut h = doc.table_helper();
let item = h.required_item("s").unwrap();
assert_eq!(item.as_str(), Some("hello"));
let item = h.required_item("i").unwrap();
assert_eq!(item.as_i64(), Some(42));
let item = h.required_item("f").unwrap();
assert!((item.as_f64().unwrap() - 3.15).abs() < f64::EPSILON);
let item = h.required_item("b").unwrap();
assert_eq!(item.as_bool(), Some(true));
let item = h.required_item("a").unwrap();
assert_eq!(item.as_array().unwrap().len(), 2);
let item = h.required_item("t").unwrap();
assert!(item.as_table().is_some());
assert_eq!(h.remaining_count(), 0);
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.required_item("missing").is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::MissingField("missing")
));
let mut doc = crate::parser::parse("x = 99\ny = true", &arena).unwrap();
{
let mut h = doc.table_helper();
let item = h.optional_item("x");
assert!(item.is_some());
assert_eq!(item.unwrap().as_i64(), Some(99));
let item = h.optional_item("y");
assert_eq!(item.unwrap().as_bool(), Some(true));
assert!(h.optional_item("absent").is_none());
assert_eq!(h.remaining_count(), 0);
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.optional_item("nope").is_none());
let _ = h.optional_item("a");
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1\nb = 2\nc = 3", &arena).unwrap();
{
let mut h = doc.table_helper();
h.required_item("a").unwrap();
h.optional_item("b");
h.required_item("c").unwrap();
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1\nb = 2\nc = 3", &arena).unwrap();
{
let mut h = doc.table_helper();
h.required_item("a").unwrap();
assert_eq!(h.remaining_count(), 2);
assert!(h.require_empty().is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::UnexpectedKey { .. }
));
let mut lines = Vec::new();
for i in 0..10 {
lines.push(format!("k{i} = {i}"));
}
let input = lines.join("\n");
let mut doc = crate::parser::parse(&input, &arena).unwrap();
{
let mut h = doc.table_helper();
let item = h.required_item("k0").unwrap();
assert_eq!(item.as_i64(), Some(0));
let item = h.required_item("k9").unwrap();
assert_eq!(item.as_i64(), Some(9));
let item = h.optional_item("k5").unwrap();
assert_eq!(item.as_i64(), Some(5));
assert!(h.optional_item("nonexistent").is_none());
assert!(h.required_item("also_missing").is_err());
}
let mut doc = crate::parser::parse("only = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
h.optional_item("only");
h.optional_item("only");
assert_eq!(h.remaining_count(), 0);
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
}
#[test]
fn required_entry_and_optional_entry() {
let arena = Arena::new();
let input = "name = \"alice\"\nage = 30";
let mut doc = crate::parser::parse(input, &arena).unwrap();
{
let mut h = doc.table_helper();
let (key, item) = h.required_entry("name").unwrap();
assert_eq!(key.name, "name");
assert_eq!(item.as_str(), Some("alice"));
let key_text = &input[key.span.start as usize..key.span.end as usize];
assert_eq!(key_text, "name");
let (key, item) = h.required_entry("age").unwrap();
assert_eq!(key.name, "age");
assert_eq!(item.as_i64(), Some(30));
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("x = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.required_entry("missing").is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::MissingField("missing")
));
let input = "color = \"red\"";
let mut doc = crate::parser::parse(input, &arena).unwrap();
{
let mut h = doc.table_helper();
let entry = h.optional_entry("color");
assert!(entry.is_some());
let (key, item) = entry.unwrap();
assert_eq!(key.name, "color");
assert_eq!(item.as_str(), Some("red"));
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.optional_entry("nope").is_none());
h.optional_entry("a");
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1\nb = 2\nc = 3", &arena).unwrap();
{
let mut h = doc.table_helper();
h.optional_entry("a");
h.required_entry("c").unwrap();
assert_eq!(h.remaining_count(), 1);
assert!(h.require_empty().is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::UnexpectedKey { .. }
));
let mut lines = Vec::new();
for i in 0..8 {
lines.push(format!("field{i} = \"{i}\""));
}
let input = lines.join("\n");
let mut doc = crate::parser::parse(&input, &arena).unwrap();
{
let mut h = doc.table_helper();
let (key, item) = h.required_entry("field0").unwrap();
assert_eq!(key.name, "field0");
assert_eq!(item.as_str(), Some("0"));
let (key, item) = h.required_entry("field7").unwrap();
assert_eq!(key.name, "field7");
assert_eq!(item.as_str(), Some("7"));
assert!(h.optional_entry("nonexistent").is_none());
}
let input = r#""quoted-key" = 42"#;
let mut doc = crate::parser::parse(input, &arena).unwrap();
{
let mut h = doc.table_helper();
let (key, item) = h.required_entry("quoted-key").unwrap();
assert_eq!(key.name, "quoted-key");
assert_eq!(item.as_i64(), Some(42));
}
}
#[test]
fn required_mapped_and_optional_mapped() {
use std::net::Ipv4Addr;
fn parse_positive_int(item: &crate::item::Item<'_>) -> Result<u32, Error> {
let val = item
.as_i64()
.ok_or_else(|| item.expected(&"a positive integer"))?;
if val > 0 && val <= u32::MAX as i64 {
Ok(val as u32)
} else {
Err(item.expected(&"a positive integer"))
}
}
fn parse_uppercase(item: &crate::item::Item<'_>) -> Result<String, Error> {
let s = item.as_str().ok_or_else(|| item.expected(&"a string"))?;
Ok(s.to_uppercase())
}
let arena = Arena::new();
let mut doc =
crate::parser::parse("ip = \"192.168.1.1\"\ncount = 5\nname = \"hello\"", &arena).unwrap();
{
let mut h = doc.table_helper();
let ip: Ipv4Addr = h.required_mapped("ip", Item::parse::<Ipv4Addr>).unwrap();
assert_eq!(ip, Ipv4Addr::new(192, 168, 1, 1));
let count: u32 = h.required_mapped("count", parse_positive_int).unwrap();
assert_eq!(count, 5);
let name: String = h.required_mapped("name", parse_uppercase).unwrap();
assert_eq!(name, "HELLO");
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.required_mapped("missing", parse_positive_int).is_err());
}
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::MissingField("missing")
));
let mut doc = crate::parser::parse("ip = \"not-an-ip\"", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.required_mapped("ip", Item::parse::<Ipv4Addr>).is_err());
}
assert_eq!(doc.ctx.errors.len(), 1);
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::Custom(_)
));
let mut doc = crate::parser::parse("count = -5", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.required_mapped("count", parse_positive_int).is_err());
}
assert_eq!(doc.ctx.errors.len(), 1);
let mut doc = crate::parser::parse("ip = \"10.0.0.1\"\nport = 8080", &arena).unwrap();
{
let mut h = doc.table_helper();
let ip = h.optional_mapped("ip", Item::parse::<Ipv4Addr>);
assert_eq!(ip, Some(Ipv4Addr::new(10, 0, 0, 1)));
let port = h.optional_mapped("port", parse_positive_int);
assert_eq!(port, Some(8080));
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("a = 1", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(
h.optional_mapped("absent", Item::parse::<Ipv4Addr>)
.is_none()
);
h.optional_mapped("a", parse_positive_int);
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut doc = crate::parser::parse("ip = \"bad\"", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.optional_mapped("ip", Item::parse::<Ipv4Addr>).is_none());
h.require_empty().unwrap();
}
assert_eq!(doc.ctx.errors.len(), 1);
assert!(matches!(
doc.ctx.errors[0].kind(),
crate::ErrorKind::Custom(_)
));
let mut doc = crate::parser::parse("name = 42", &arena).unwrap();
{
let mut h = doc.table_helper();
assert!(h.optional_mapped("name", parse_uppercase).is_none());
h.require_empty().unwrap();
}
assert_eq!(doc.ctx.errors.len(), 1);
let mut doc = crate::parser::parse(
"ip = \"1.2.3.4\"\nname = \"test\"\ncount = 7\nextra = true",
&arena,
)
.unwrap();
{
let mut h = doc.table_helper();
let _: Ipv4Addr = h.required_mapped("ip", Item::parse::<Ipv4Addr>).unwrap();
let _: String = h.required("name").unwrap();
let _ = h.optional_mapped("count", parse_positive_int);
let _: bool = h.optional("extra").unwrap();
assert_eq!(h.remaining_count(), 0);
h.require_empty().unwrap();
}
assert!(doc.ctx.errors.is_empty());
let mut lines = Vec::new();
for i in 0..8 {
lines.push(format!("n{i} = \"{i}\""));
}
let input = lines.join("\n");
let mut doc = crate::parser::parse(&input, &arena).unwrap();
{
let mut h = doc.table_helper();
let v: String = h.required_mapped("n0", parse_uppercase).unwrap();
assert_eq!(v, "0");
let v = h.optional_mapped("n7", parse_uppercase);
assert_eq!(v.as_deref(), Some("7"));
assert!(h.required_mapped("nonexistent", parse_uppercase).is_err());
assert!(h.optional_mapped("also_missing", parse_uppercase).is_none());
}
}
#[test]
fn deser_hashmap_and_btreemap() {
let arena = Arena::new();
let input = r#"
[v]
alpha = 1
beta = 2
gamma = 3
"#;
let val: std::collections::BTreeMap<String, i64> = parse_val(input, &arena).unwrap();
assert_eq!(val.len(), 3);
assert_eq!(val["alpha"], 1);
assert_eq!(val["beta"], 2);
assert_eq!(val["gamma"], 3);
let input = r#"
[v]
key1 = "val1"
key2 = "val2"
"#;
let val: std::collections::HashMap<String, String> = parse_val(input, &arena).unwrap();
assert_eq!(val.len(), 2);
assert_eq!(val["key1"], "val1");
assert_eq!(val["key2"], "val2");
let err = parse_val::<std::collections::BTreeMap<String, i64>>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let err =
parse_val::<std::collections::HashMap<String, i64>>("v = \"nope\"", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let val: std::path::PathBuf = parse_val(r#"v = "/usr/bin/test""#, &arena).unwrap();
assert_eq!(val, std::path::PathBuf::from("/usr/bin/test"));
let err = parse_val::<std::path::PathBuf>("v = 42", &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let input = r#"
[v]
good = 1
bad = "not a number"
"#;
let err = parse_val::<std::collections::HashMap<String, i64>>(input, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
let input = r#"
[v]
ok = 10
nope = "string"
"#;
let err = parse_val::<std::collections::BTreeMap<String, i64>>(input, &arena).unwrap_err();
assert!(matches!(err.kind(), crate::ErrorKind::Wanted { .. }));
}
#[test]
fn from_str_and_to_string_roundtrip() {
use std::collections::BTreeMap;
let val: BTreeMap<String, String> = crate::from_str("x = \"hello\"\ny = \"world\"").unwrap();
assert_eq!(val["x"], "hello");
assert_eq!(val["y"], "world");
let mut map = BTreeMap::new();
map.insert("name".to_string(), "test".to_string());
map.insert("value".to_string(), "42".to_string());
let toml_str = crate::to_string(&map).unwrap();
assert!(toml_str.contains("name = \"test\""), "got: {toml_str}");
assert!(toml_str.contains("value = \"42\""), "got: {toml_str}");
let restored: BTreeMap<String, String> = crate::from_str(&toml_str).unwrap();
assert_eq!(restored["name"], "test");
assert_eq!(restored["value"], "42");
let source = "name = \"test\"\nvalue = \"42\"\n";
let arena = crate::Arena::new();
let doc = crate::parse(source, &arena).unwrap();
let output = crate::Formatting::preserved_from(&doc)
.format(&map)
.unwrap();
assert!(output.contains("name = \"test\""), "got: {output}");
let err = crate::to_string(&42i64).unwrap_err();
assert!(
err.message.contains("Top-level item must be a table"),
"got: {}",
err.message
);
let err = crate::Formatting::preserved_from(&doc)
.format(&42i64)
.unwrap_err();
assert!(
err.message.contains("Top-level item must be a table"),
"got: {}",
err.message
);
}
#[test]
fn to_toml_wrapper_types() {
use crate::Arena;
use crate::ser::ToToml;
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
let arena = Arena::new();
let s: &str = "hello";
let item = s.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("hello"));
let num: i64 = 42;
let item = (&num).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(42));
let boxed = Box::new(99i64);
let item = boxed.to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(99));
let rc = Rc::new("rc-str".to_string());
let item = rc.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("rc-str"));
let arc = Arc::new(7i64);
let item = arc.to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(7));
let cow: std::borrow::Cow<'_, str> = std::borrow::Cow::Borrowed("cow-val");
let item = cow.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("cow-val"));
let ch = 'A';
let item = ch.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("A"));
let f: f32 = 1.5;
let item = f.to_toml(&arena).unwrap();
assert!((item.as_f64().unwrap() - 1.5).abs() < f64::EPSILON);
let f: f64 = 2.5;
let item = f.to_toml(&arena).unwrap();
assert!((item.as_f64().unwrap() - 2.5).abs() < f64::EPSILON);
let b = true;
let item = b.to_toml(&arena).unwrap();
assert_eq!(item.as_bool(), Some(true));
let path = PathBuf::from("/usr/bin");
let item = path.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("/usr/bin"));
let path = std::path::Path::new("/etc/config");
let item = path.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("/etc/config"));
let vec = vec![1i64, 2, 3];
let item = vec.to_toml(&arena).unwrap();
assert_eq!(item.as_array().unwrap().len(), 3);
let arr = [10i64, 20, 30];
let item = arr.to_toml(&arena).unwrap();
assert_eq!(item.as_array().unwrap().len(), 3);
let mut set = BTreeSet::new();
set.insert("a".to_string());
set.insert("b".to_string());
let item = set.to_toml(&arena).unwrap();
assert_eq!(item.as_array().unwrap().len(), 2);
let mut hset = std::collections::HashSet::new();
hset.insert("x".to_string());
let item = hset.to_toml(&arena).unwrap();
assert_eq!(item.as_array().unwrap().len(), 1);
let some_val: Option<i64> = Some(5);
let item = some_val.to_optional_toml(&arena).unwrap();
assert!(item.is_some());
assert_eq!(item.unwrap().as_i64(), Some(5));
let none_val: Option<i64> = None;
let item = none_val.to_optional_toml(&arena).unwrap();
assert!(item.is_none());
let mut btm = BTreeMap::new();
btm.insert("k".to_string(), "v".to_string());
let item = btm.to_toml(&arena).unwrap();
assert!(item.as_table().is_some());
assert_eq!(
item.as_table().unwrap().get("k").unwrap().as_str(),
Some("v")
);
let mut hm = std::collections::HashMap::new();
hm.insert("key".to_string(), 42i64);
let item = hm.to_toml(&arena).unwrap();
assert!(item.as_table().is_some());
let item = (42u8).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(42));
let item = (-5i8).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(-5));
let item = (1000u16).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(1000));
let item = (-200i16).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(-200));
let item = (100000u32).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(100000));
let item = (-50000i32).to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(-50000));
let tbl = btm.to_toml(&arena).unwrap();
let tbl_ref = tbl.as_table().unwrap();
let item = tbl_ref.to_toml(&arena).unwrap();
assert!(item.as_table().is_some());
let arr_item = vec.to_toml(&arena).unwrap();
let arr_ref = arr_item.as_array().unwrap();
let item = arr_ref.to_toml(&arena).unwrap();
assert_eq!(item.as_array().unwrap().len(), 3);
let original = Item::from(42i64);
let item = original.to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(42));
let mut num2: i64 = 77;
let num2_ref = &mut num2;
let item = num2_ref.to_toml(&arena).unwrap();
assert_eq!(item.as_i64(), Some(77));
let cow_owned: std::borrow::Cow<'_, str> = std::borrow::Cow::Owned("owned-val".to_string());
let item = cow_owned.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("owned-val"));
let ch_multi = '\u{1F600}';
let item = ch_multi.to_toml(&arena).unwrap();
assert_eq!(item.as_str(), Some("\u{1F600}"));
}
#[test]
fn toml_path_on_unexpected_key() {
use crate::error::ErrorKind;
let arena = Arena::new();
let input = r#"
[server]
host = "localhost"
port = 8080
unknown_field = true
"#;
let mut doc = crate::parser::parse(input, &arena).unwrap();
let result = {
let (ctx, table) = doc.split();
let server_entry = table.get("server").unwrap();
let server_table = server_entry.as_table().unwrap();
let mut th = super::TableHelper::new(ctx, server_table);
let _: String = th.required("host").unwrap();
let _: i64 = th.required("port").unwrap();
let r = th.require_empty();
super::compute_paths(table, &mut ctx.errors);
r
};
assert!(result.is_err());
assert_eq!(doc.ctx.errors.len(), 1);
assert!(matches!(
doc.ctx.errors[0].kind(),
ErrorKind::UnexpectedKey { .. }
));
assert!(doc.ctx.errors[0].path().is_some());
assert_eq!(
format!("{}", doc.ctx.errors[0]),
"unexpected key at `server.unknown_field`"
);
let input = r#"
[[items]]
name = "a"
bogus = 1
"#;
let mut doc = crate::parser::parse(input, &arena).unwrap();
let result = {
let (ctx, table) = doc.split();
let items_entry = table.get("items").unwrap();
let items_array = items_entry.as_array().unwrap();
let elem = &items_array.as_slice()[0];
let elem_table = elem.as_table().unwrap();
let mut th = super::TableHelper::new(ctx, elem_table);
let _: String = th.required("name").unwrap();
let r = th.require_empty();
super::compute_paths(table, &mut ctx.errors);
r
};
assert!(result.is_err());
assert_eq!(doc.ctx.errors.len(), 1);
assert!(doc.ctx.errors[0].path().is_some());
let display = format!("{}", doc.ctx.errors[0]);
assert_eq!(display, "unexpected key at `items[0].bogus`");
let mut doc = crate::parser::parse("a = 1\nb = 2", &arena).unwrap();
{
let mut helper = doc.table_helper();
let _: i64 = helper.required("a").unwrap();
assert!(helper.require_empty().is_err());
}
assert!(doc.ctx.errors[0].path().is_none());
}
#[test]
fn deser_tuple_1() {
let arena = Arena::new();
let val: (i64,) = parse_val("v = [42]", &arena).unwrap();
assert_eq!(val, (42,));
}
#[test]
fn deser_tuple_2() {
let arena = Arena::new();
let val: (String, i64) = parse_val(r#"v = ["hello", 7]"#, &arena).unwrap();
assert_eq!(val, ("hello".to_string(), 7));
}
#[test]
fn deser_tuple_3() {
let arena = Arena::new();
let val: (bool, i64, String) = parse_val(r#"v = [true, 99, "ok"]"#, &arena).unwrap();
assert_eq!(val, (true, 99, "ok".to_string()));
}
#[test]
fn deser_tuple_wrong_size() {
let arena = Arena::new();
let err = parse_val::<(i64, i64)>("v = [1, 2, 3]", &arena).unwrap_err();
let msg = format!("{err}");
assert!(msg.contains("size of 2"), "got: {msg}");
}
#[test]
fn deser_tuple_wrong_type() {
let arena = Arena::new();
assert!(parse_val::<(i64,)>(r#"v = "not an array""#, &arena).is_err());
}