structom 0.2.5

efficient data format for all needs
Documentation
use chrono::{DateTime, TimeDelta};
use num_bigint::BigInt;

use crate::{Key, StringifyOptions, Value};

fn stringify(value: impl Into<Value>) -> String {
	crate::stringify(&value.into(), &Default::default())
}
fn stringify_pretty(value: impl Into<Value>) -> String {
	crate::stringify(&value.into(), &StringifyOptions { ident: "  ", ..Default::default() })
}
fn stringify_compact(value: impl Into<Value>) -> String {
	let options = StringifyOptions { ident: "  ", compat_items: true, ..Default::default() };
	crate::stringify(&value.into(), &options)
}
#[test]
fn core() {
	assert_eq!(stringify(true), "true");
	assert_eq!(stringify(false), "false");
	assert_eq!(stringify(123), "123");
	assert_eq!(stringify(-123), "-123");
	assert_eq!(stringify(1.5), "1.5");
	assert_eq!(stringify(f64::NAN), "nan");
	assert_eq!(stringify(f64::INFINITY), "inf");
	assert_eq!(stringify(f64::NEG_INFINITY), "-inf");
	assert_eq!(stringify(BigInt::from(12345678901234567890u64)), "12345678901234567890bint");
	assert_eq!(stringify("abc"), "\"abc\"");
	assert_eq!(stringify("abc \n\t\r\" 123"), "\"abc \n\t\r\\\" 123\"");
}

#[test]
fn rich() {
	let inst = DateTime::parse_from_rfc3339("2026-02-02T20:04:02Z").unwrap().to_utc();
	assert_eq!(stringify(inst), "inst \"2026-02-02T20:04:02Z\"");
	let inst = DateTime::parse_from_rfc3339("2026-02-02T20:04:02.123456789Z").unwrap().to_utc();
	assert_eq!(stringify(inst), "instN \"2026-02-02T20:04:02.123456789Z\"");
	let inst = DateTime::parse_from_str("+100000-01-01 02:00:00+02:00", "%Y-%m-%d %H:%M:%S%:z");
	assert_eq!(stringify(inst.unwrap().to_utc()), "inst \"+100000-01-01T00:00:00Z\"");

	let uuid = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
	assert_eq!(stringify(uuid), "uuid \"00010203-0405-0607-0809-0a0b0c0d0e0f\"");

	let dur = TimeDelta::nanoseconds(12345678912);
	assert_eq!(stringify(dur), "dur \"12s 345ms 678us 912ns\"");
}

#[test]
fn arr() {
	assert_eq!(stringify(vec![1, 2, 3]), "[1,2,3]");
	assert_eq!(stringify(Value::Arr(vec![1.into(), true.into(), "a".into()])), "[1,true,\"a\"]");
	assert_eq!(stringify_pretty(vec![1, 2, 3]), "[\n  1,\n  2,\n  3,\n]");
	assert_eq!(stringify_compact(vec![1, 2, 3]), "[1, 2, 3]");
	let value = stringify_compact(Value::Arr(vec![1.into(), TimeDelta::nanoseconds(123).into()]));
	assert_eq!(value, "[\n  1,\n  dur \"123ns\",\n]");
	assert_eq!(stringify_compact(vec![TimeDelta::nanoseconds(123)]), "[dur \"123ns\"]");
}

fn i(key: impl Into<Key>, value: impl Into<Value>) -> (Key, Value) {
	(key.into(), value.into())
}
#[test]
fn obj() {
	assert_eq!(stringify(Value::map_from([("a", 1)])), "{a:1}");
	let value = stringify(Value::map_from([i("a", 1), i("b", true)]));
	assert!(value.contains("a:1"));
	assert!(value.contains("b:true"));
	assert_eq!(stringify(Value::map_from([("-123", 1)])), "{\"-123\":1}");
	assert_eq!(stringify(Value::map_from([(1, 1)])), "{[1]:1}");
	let value = stringify_pretty(Value::map_from([i("a", 1), i("b", true)]));
	assert!(value.contains("  a: 1,\n"));
	assert!(value.contains("  b: true,\n"));
	let value = stringify_compact(Value::map_from([i("a", 1), i("b", true)]));
	assert!(value.contains("a: 1"));
	assert!(value.contains("b: true"));
	let value =
		stringify_compact(Value::map_from([i("a", 1), i("b", TimeDelta::nanoseconds(123))]));
	assert!(value.contains("  a: 1,\n"));
	assert!(value.contains("  b: dur \"123ns\",\n"));
	let value =
		stringify_compact(Value::map_from([i("a", 1), i(TimeDelta::nanoseconds(123), "b")]));
	assert!(value.contains("  a: 1,\n"));
	assert!(value.contains("  [dur \"123ns\"]: \"b\",\n"));
	let value = stringify_compact(Value::map_from([i("b", TimeDelta::nanoseconds(123))]));
	assert_eq!(value, "{b: dur \"123ns\"}");
}

#[test]
fn enums() {
	assert_eq!(stringify(Value::UnitVar("A".to_string())), "A");
	let value = Value::map_from([i(Key::enum_variant_key().clone(), "A"), i("a", 1)]);
	assert_eq!(stringify(value.clone()), "A{a:1}");
	assert_eq!(stringify_pretty(value), "A{\n  a: 1,\n}");
}
#[test]
fn metadata() {
	let value = crate::stringify(
		&Value::map_from([
			i(Key::has_meta_key().clone(), true),
			i(Key::inner_key().clone(), 123),
			i("base", "16"),
			i("type", "int"),
		]),
		&StringifyOptions { metadata: true, ..Default::default() },
	);
	assert!(
		value == r#"@base("16") @type("int") 123"# || value == r#"@type("int") @base("16") 123"#
	);
}