use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct S {
v: String,
}
#[test]
fn quoting_matrix() {
let cases: &[(&str, &str)] = &[
("bar", "v=bar"),
("42", "v=42"), ("with=equals", "v=with=equals"), ("hello world", r#"v="hello world""#),
("", r#"v="""#), (r#"a"b"#, r#"v="a\"b""#), (r#"a\b"#, r#"v="a\\b""#), (" spaces ", r#"v=" spaces ""#), ("tab\tok", "v=tab\tok"), ];
for (raw, expected) in cases {
let line = serde_kv::to_string(&S { v: (*raw).into() }).unwrap();
assert_eq!(line, *expected, "serialize {raw:?}");
let back: S = serde_kv::from_str(&line).unwrap();
assert_eq!(back.v, *raw, "round-trip {raw:?}");
}
}
#[test]
fn value_interpretation() {
#[derive(Deserialize)]
struct AsStr { v: String }
#[derive(Deserialize)]
struct AsI64 { v: i64 }
#[derive(Deserialize)]
struct AsF64 { v: f64 }
#[derive(Deserialize)]
struct AsBool { v: bool }
assert_eq!(serde_kv::from_str::<AsStr>("v=42").unwrap().v, "42");
assert_eq!(serde_kv::from_str::<AsI64>("v=-42").unwrap().v, -42);
assert_eq!(serde_kv::from_str::<AsF64>("v=2.5").unwrap().v, 2.5);
assert_eq!(serde_kv::from_str::<AsF64>("v=1e3").unwrap().v, 1000.0);
assert!(serde_kv::from_str::<AsBool>("v=true").unwrap().v);
assert_eq!(serde_kv::from_str::<AsI64>(r#"v="-42""#).unwrap().v, -42);
}
#[test]
fn whitespace_handling() {
#[derive(Deserialize, PartialEq, Debug)]
struct AB { a: u32, b: u32 }
let cases: &[&str] = &[
"a=1 b=2",
"a=1 b=2", " a=1 b=2", "a=1 b=2 ", " a=1 b=2 ",
];
for input in cases {
assert_eq!(
serde_kv::from_str::<AB>(input).unwrap(),
AB { a: 1, b: 2 },
"input {input:?}"
);
}
}
#[test]
fn empty_input() {
let map: BTreeMap<String, String> = serde_kv::from_str("").unwrap();
assert!(map.is_empty());
assert!(serde_kv::from_str::<BTreeMap<String, String>>(" ").unwrap().is_empty());
#[derive(Deserialize, PartialEq, Debug)]
struct AllOpt { a: Option<u32> }
assert_eq!(serde_kv::from_str::<AllOpt>("").unwrap(), AllOpt { a: None });
}
#[test]
fn numeric_boundaries() {
use serde_kv::Error;
#[derive(Deserialize, Debug)]
struct U8 { #[allow(dead_code)] v: u8 }
#[derive(Deserialize, Debug)]
struct I32 { #[allow(dead_code)] v: i32 }
let fails: &[&str] = &[
"v=256", "v=-1", "v=3.5", "v=abc", "v=", ];
for input in fails {
let err = serde_kv::from_str::<U8>(input).unwrap_err();
assert!(matches!(err, Error::ParseInt(_)), "{input:?} -> {err:?}");
}
assert_eq!(serde_kv::from_str::<I32>("v=2147483647").unwrap().v, i32::MAX);
assert!(matches!(
serde_kv::from_str::<I32>("v=2147483648").unwrap_err(),
Error::ParseInt(_)
));
}
#[test]
fn unicode() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct U {
emoji: String,
c: char,
}
let line = r#"emoji="héllo 🌍" c=🦀"#;
let got: U = serde_kv::from_str(line).unwrap();
assert_eq!(got, U { emoji: "héllo 🌍".into(), c: '🦀' });
assert_eq!(serde_kv::to_string(&got).unwrap(), line);
}
#[test]
fn duplicate_keys() {
#[derive(Deserialize, Debug)]
struct One { #[allow(dead_code)] a: u32 }
assert!(serde_kv::from_str::<One>("a=1 a=2").is_err());
let map: BTreeMap<String, String> = serde_kv::from_str("a=1 a=2").unwrap();
assert_eq!(map["a"], "2");
}