use crate::prelude::*;
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(rename_all(serialize = "SCREAMING-KEBAB-CASE"), deny_unknown_fields)]
struct StructDenyUnknownFields {
#[serde(skip_deserializing)]
read_only: bool,
#[allow(dead_code)]
#[serde(skip_serializing)]
write_only: bool,
#[serde(default)]
default: bool,
#[serde(skip_serializing_if = "core::ops::Not::not")]
skip_serializing_if: bool,
#[serde(rename(serialize = "ser_renamed", deserialize = "de_renamed"))]
renamed: bool,
option: Option<bool>,
}
#[derive(JsonSchema, Deserialize, Serialize)]
struct StructAllowUnknownFields {
#[serde(flatten)]
inner: StructDenyUnknownFields,
}
#[test]
fn struct_deny_unknown_fields() {
test!(StructDenyUnknownFields)
.assert_snapshot()
.assert_allows_de_roundtrip([
json!({ "write_only": false, "skip_serializing_if": false, "de_renamed": false }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "default": true }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "option": true }),
])
.assert_rejects_de([
json!({ "skip_serializing_if": false, "de_renamed": false }),
json!({ "write_only": false, "de_renamed": false }),
json!({ "write_only": false, "skip_serializing_if": false }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "unknown": true }),
])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[test]
fn struct_allow_unknown_fields() {
test!(StructAllowUnknownFields)
.assert_snapshot()
.assert_allows_de_roundtrip([
json!({ "write_only": false, "skip_serializing_if": false, "de_renamed": false }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "default": true }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "option": true }),
json!({ "write_only": true, "skip_serializing_if": true, "de_renamed": true, "unknown": true }),
])
.assert_rejects_de([
json!({ "skip_serializing_if": false, "de_renamed": false }),
json!({ "write_only": false, "de_renamed": false }),
json!({ "write_only": false, "skip_serializing_if": false }),
])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[derive(JsonSchema, Deserialize, Serialize)]
struct TupleStruct(
String,
#[allow(dead_code)]
#[serde(skip_serializing)]
bool,
String,
#[serde(skip_deserializing)] bool,
String,
);
#[test]
fn tuple_struct() {
test!(TupleStruct)
.assert_snapshot()
.assert_allows_de_roundtrip([json!(["", true, "", ""])])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[allow(dead_code)]
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(
rename_all(serialize = "SCREAMING-KEBAB-CASE"),
rename_all_fields(serialize = "PascalCase")
)]
enum ExternalEnum {
#[serde(skip_deserializing)]
ReadOnlyUnit,
#[serde(skip_serializing)]
WriteOnlyUnit,
#[serde(skip_deserializing)]
ReadOnlyStruct { s: String },
#[serde(skip_serializing)]
WriteOnlyStruct { i: isize },
#[serde(rename(serialize = "ser_renamed_unit", deserialize = "de_renamed_unit"))]
RenamedUnit,
#[serde(rename(serialize = "ser_renamed_struct", deserialize = "de_renamed_struct"))]
RenamedStruct { b: bool },
}
#[test]
fn externally_tagged_enum() {
test!(ExternalEnum)
.assert_snapshot()
.assert_allows_ser_roundtrip([
ExternalEnum::ReadOnlyUnit,
ExternalEnum::ReadOnlyStruct { s: "test".into() },
ExternalEnum::RenamedUnit,
ExternalEnum::RenamedStruct { b: true },
])
.assert_allows_de_roundtrip([
json!("WriteOnlyUnit"),
json!({ "WriteOnlyStruct": { "i": 123 } }),
json!("de_renamed_unit"),
json!({ "de_renamed_struct": { "b": true } }),
])
.assert_rejects_de([
json!("READ-ONLY-UNIT"),
json!("ReadOnlyUnit"),
json!("ser_renamed_unit"),
])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[allow(dead_code)]
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(
tag = "tag",
rename_all(serialize = "SCREAMING-KEBAB-CASE"),
rename_all_fields(serialize = "PascalCase")
)]
enum InternalEnum {
#[serde(skip_deserializing)]
ReadOnlyUnit,
#[serde(skip_serializing)]
WriteOnlyUnit,
#[serde(skip_deserializing)]
ReadOnlyStruct { s: String },
#[serde(skip_serializing)]
WriteOnlyStruct { i: isize },
#[serde(rename(serialize = "ser_renamed_unit", deserialize = "de_renamed_unit"))]
RenamedUnit,
#[serde(rename(serialize = "ser_renamed_struct", deserialize = "de_renamed_struct"))]
RenamedStruct { b: bool },
}
#[test]
fn internally_tagged_enum() {
test!(InternalEnum)
.assert_snapshot()
.assert_allows_ser_roundtrip([
InternalEnum::ReadOnlyUnit,
InternalEnum::ReadOnlyStruct { s: "test".into() },
InternalEnum::RenamedUnit,
InternalEnum::RenamedStruct { b: true },
])
.assert_allows_de_roundtrip([
json!({ "tag": "WriteOnlyUnit" }),
json!({ "tag": "WriteOnlyStruct", "i": 123 }),
json!({ "tag": "de_renamed_unit" }),
json!({ "tag": "de_renamed_struct", "b": true }),
])
.assert_rejects_de([
json!({ "tag": "READ-ONLY-UNIT" }),
json!({ "tag": "ReadOnlyUnit" }),
json!({ "tag": "ser_renamed_unit" }),
])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[allow(dead_code)]
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(
tag = "tag",
content = "content",
rename_all(serialize = "SCREAMING-KEBAB-CASE"),
rename_all_fields(serialize = "PascalCase")
)]
enum AdjacentEnum {
#[serde(skip_deserializing)]
ReadOnlyUnit,
#[serde(skip_serializing)]
WriteOnlyUnit,
#[serde(skip_deserializing)]
ReadOnlyStruct { s: String },
#[serde(skip_serializing)]
WriteOnlyStruct { i: isize },
#[serde(rename(serialize = "ser_renamed_unit", deserialize = "de_renamed_unit"))]
RenamedUnit,
#[serde(rename(serialize = "ser_renamed_struct", deserialize = "de_renamed_struct"))]
RenamedStruct { b: bool },
}
#[test]
fn adjacently_tagged_enum() {
test!(AdjacentEnum)
.assert_snapshot()
.assert_allows_ser_roundtrip([
AdjacentEnum::ReadOnlyUnit,
AdjacentEnum::ReadOnlyStruct { s: "test".into() },
AdjacentEnum::RenamedUnit,
AdjacentEnum::RenamedStruct { b: true },
])
.assert_allows_de_roundtrip([
json!({ "tag": "WriteOnlyUnit" }),
json!({ "tag": "WriteOnlyStruct", "content": { "i": 123 } }),
json!({ "tag": "de_renamed_unit" }),
json!({ "tag": "de_renamed_struct", "content": { "b": true } }),
])
.assert_rejects_de([
json!({ "tag": "READ-ONLY-UNIT" }),
json!({ "tag": "ReadOnlyUnit" }),
json!({ "tag": "ser_renamed_unit" }),
])
.assert_matches_de_roundtrip(arbitrary_values());
}
#[allow(dead_code)]
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(
untagged,
rename_all(serialize = "SCREAMING-KEBAB-CASE"),
rename_all_fields(serialize = "PascalCase")
)]
enum UntaggedEnum {
#[serde(skip_deserializing)]
ReadOnlyUnit,
#[serde(skip_serializing)]
WriteOnlyUnit,
#[serde(skip_deserializing)]
ReadOnlyStruct { s: String },
#[serde(skip_serializing)]
WriteOnlyStruct { i: isize },
#[serde(rename(serialize = "ser_renamed_unit", deserialize = "de_renamed_unit"))]
RenamedUnit,
#[serde(rename(serialize = "ser_renamed_struct", deserialize = "de_renamed_struct"))]
RenamedStruct { b: bool },
}
#[test]
fn untagged_enum() {
test!(UntaggedEnum)
.assert_snapshot()
.assert_allows_ser_roundtrip([
UntaggedEnum::ReadOnlyUnit,
UntaggedEnum::ReadOnlyStruct { s: "test".into() },
UntaggedEnum::RenamedUnit,
UntaggedEnum::RenamedStruct { b: true },
])
.assert_allows_de_roundtrip([json!(null), json!({ "i": 123 }), json!({ "b": true })])
.assert_rejects_de([json!({ "s": "test" })])
.assert_matches_de_roundtrip(arbitrary_values());
}