use super::*;
type KeyCase<'a> = (&'a str, &'a str, &'a str);
#[test]
fn should_emit_a_bare_key_only_when_the_wire_name_is_a_legal_identifier() {
let cases: [KeyCase<'_>; 8] = [
(
"a snake_case core field name is already legal",
"max_chars",
"max_chars",
),
("a camelCase wire name is already legal", "maxChars", "maxChars"),
("a leading underscore is legal", "_internal", "_internal"),
("a leading dollar is legal", "$ref", "$ref"),
("a dollar inside the name is legal", "a$b", "a$b"),
("digits after the first character are legal", "sha256", "sha256"),
("a single letter is legal", "x", "x"),
(
"an all-caps SCREAMING wire name is legal",
"CONTENT_TYPE",
"CONTENT_TYPE",
),
];
for (case, wire_name, expected) in cases {
assert_eq!(ts_property_key(wire_name), expected, "{case}");
}
}
#[test]
fn should_quote_a_wire_name_that_is_not_a_legal_identifier() {
let cases: [KeyCase<'_>; 9] = [
("kebab-case from #[serde(rename)]", "content-type", "\"content-type\""),
(
"kebab-case from #[serde(rename_all = \"kebab-case\")]",
"max-chars",
"\"max-chars\"",
),
(
"a space cannot appear in an identifier",
"content type",
"\"content type\"",
),
("a leading digit cannot start an identifier", "2fa", "\"2fa\""),
("a bare digit run cannot start an identifier", "0", "\"0\""),
("a dot would read as member access", "a.b", "\"a.b\""),
("an at-sign is not an identifier character", "@type", "\"@type\""),
("a colon is not an identifier character", "xml:lang", "\"xml:lang\""),
("the empty wire name is not an identifier", "", "\"\""),
];
for (case, wire_name, expected) in cases {
assert_eq!(ts_property_key(wire_name), expected, "{case}");
}
}
#[test]
fn should_quote_a_non_ascii_wire_name_without_mangling_it() {
assert_eq!(ts_property_key("café"), "\"café\"");
assert_eq!(ts_property_key("naïve_field"), "\"naïve_field\"");
}
#[test]
fn should_escape_characters_that_would_break_the_quoted_key() {
let cases: [KeyCase<'_>; 8] = [
(
"a double quote would close the key early",
r#"he said "hi""#,
r#""he said \"hi\"""#,
),
(
"a backslash must not escape the next character",
r"back\slash",
r#""back\\slash""#,
),
(
"a backslash before a quote must not be swallowed",
r#"trailing\"#,
r#""trailing\\""#,
),
(
"a newline cannot appear raw in a string literal",
"line\nbreak",
r#""line\nbreak""#,
),
("a carriage return cannot appear raw", "line\rbreak", r#""line\rbreak""#),
("a tab is escaped for legibility", "col\tumn", r#""col\tumn""#),
(
"U+2028 terminates a line even inside a string literal",
"a\u{2028}b",
r#""a\u2028b""#,
),
(
"an unnamed control character falls back to a \\u escape",
"a\u{1}b",
r#""a\u0001b""#,
),
];
for (case, wire_name, expected) in cases {
assert_eq!(ts_property_key(wire_name), expected, "{case}");
}
}
#[test]
fn should_quote_a_reserved_word_used_as_a_wire_name() {
for reserved in ["default", "class", "new", "function", "in", "typeof", "null", "true"] {
assert_eq!(
ts_property_key(reserved),
format!("\"{reserved}\""),
"reserved word {reserved} must be quoted"
);
}
}
#[test]
fn should_leave_the_discriminator_keys_bare() {
assert_eq!(ts_property_key("type"), "type");
assert_eq!(ts_property_key("kind"), "kind");
}