summaryrefslogtreecommitdiffstats
path: root/third_party/rust/serde_with/tests/serde_as
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/serde_with/tests/serde_as')
-rw-r--r--third_party/rust/serde_with/tests/serde_as/collections.rs44
-rw-r--r--third_party/rust/serde_with/tests/serde_as/default_on.rs130
-rw-r--r--third_party/rust/serde_with/tests/serde_as/enum_map.rs459
-rw-r--r--third_party/rust/serde_with/tests/serde_as/frominto.rs187
-rw-r--r--third_party/rust/serde_with/tests/serde_as/lib.rs1129
-rw-r--r--third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs272
-rw-r--r--third_party/rust/serde_with/tests/serde_as/pickfirst.rs134
-rw-r--r--third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs191
-rw-r--r--third_party/rust/serde_with/tests/serde_as/serde_conv.rs52
-rw-r--r--third_party/rust/serde_with/tests/serde_as/time.rs521
10 files changed, 3119 insertions, 0 deletions
diff --git a/third_party/rust/serde_with/tests/serde_as/collections.rs b/third_party/rust/serde_with/tests/serde_as/collections.rs
new file mode 100644
index 0000000000..dad84c4978
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/collections.rs
@@ -0,0 +1,44 @@
+use super::*;
+use fnv::{FnvHashMap, FnvHashSet};
+
+/// Test that HashSets are also supported with non-default hashers.
+#[test]
+fn test_fnv_hashset() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "FnvHashSet<DisplayFromStr>")] FnvHashSet<u32>);
+
+ // Normal
+ is_equal(
+ S([1, 2, 3, 4, 5].iter().cloned().collect()),
+ expect![[r#"
+ [
+ "5",
+ "4",
+ "1",
+ "3",
+ "2"
+ ]"#]],
+ );
+ is_equal(S(FnvHashSet::default()), expect![[r#"[]"#]]);
+}
+
+/// Test that HashSets are also supported with non-default hashers.
+#[test]
+fn test_fnv_hashmap() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "FnvHashMap<DisplayFromStr, DisplayFromStr>")] FnvHashMap<u8, u32>);
+
+ // Normal
+ is_equal(
+ S([(1, 1), (3, 3), (111, 111)].iter().cloned().collect()),
+ expect![[r#"
+ {
+ "1": "1",
+ "3": "3",
+ "111": "111"
+ }"#]],
+ );
+ is_equal(S(FnvHashMap::default()), expect![[r#"{}"#]]);
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/default_on.rs b/third_party/rust/serde_with/tests/serde_as/default_on.rs
new file mode 100644
index 0000000000..ea3678b166
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/default_on.rs
@@ -0,0 +1,130 @@
+use super::*;
+use serde_with::{DefaultOnError, DefaultOnNull};
+
+#[test]
+fn test_default_on_error() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "DefaultOnError<DisplayFromStr>")] u32);
+
+ // Normal
+ is_equal(S(123), expect![[r#""123""#]]);
+ is_equal(S(0), expect![[r#""0""#]]);
+ // Error cases
+ check_deserialization(S(0), r#""""#);
+ check_deserialization(S(0), r#""12+3""#);
+ check_deserialization(S(0), r#""abc""#);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(#[serde_as(as = "DefaultOnError<Vec<DisplayFromStr>>")] Vec<u32>);
+
+ // Normal
+ is_equal(
+ S2(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#]],
+ );
+ is_equal(S2(vec![]), expect![[r#"[]"#]]);
+ // Error cases
+ check_deserialization(S2(vec![]), r#"2"#);
+ check_deserialization(S2(vec![]), r#""not_a_list""#);
+ check_deserialization(S2(vec![]), r#"{}"#);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Struct2 {
+ #[serde_as(as = "DefaultOnError<Vec<DisplayFromStr>>")]
+ value: Vec<u32>,
+ }
+ check_deserialization(Struct2 { value: vec![] }, r#"{"value":}"#);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S3(#[serde_as(as = "Vec<DefaultOnError<DisplayFromStr>>")] Vec<u32>);
+
+ // Normal
+ is_equal(
+ S3(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#]],
+ );
+ is_equal(S3(vec![]), expect![[r#"[]"#]]);
+ // Error cases
+ check_deserialization(S3(vec![0, 3, 0]), r#"[2,"3",4]"#);
+ check_deserialization(S3(vec![0, 0]), r#"["AA",5]"#);
+}
+
+#[test]
+fn test_default_on_null() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "DefaultOnNull<DisplayFromStr>")] u32);
+
+ // Normal
+ is_equal(S(123), expect![[r#""123""#]]);
+ is_equal(S(0), expect![[r#""0""#]]);
+ // Null case
+ check_deserialization(S(0), r#"null"#);
+ // Error cases
+ check_error_deserialization::<S>(
+ r#""12+3""#,
+ expect![[r#"invalid digit found in string at line 1 column 6"#]],
+ );
+ check_error_deserialization::<S>(
+ r#""abc""#,
+ expect![[r#"invalid digit found in string at line 1 column 5"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(#[serde_as(as = "Vec<DefaultOnNull>")] Vec<u32>);
+
+ // Normal
+ is_equal(
+ S2(vec![1, 2, 0, 3]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 0,
+ 3
+ ]"#]],
+ );
+ is_equal(S2(vec![]), expect![[r#"[]"#]]);
+ // Null cases
+ check_deserialization(S2(vec![1, 0, 2]), r#"[1, null, 2]"#);
+ check_error_deserialization::<S2>(
+ r#"["not_a_number"]"#,
+ expect![[r#"invalid type: string "not_a_number", expected u32 at line 1 column 15"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S3(#[serde_as(as = "Vec<DefaultOnNull<DisplayFromStr>>")] Vec<u32>);
+
+ // Normal
+ is_equal(
+ S3(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#]],
+ );
+ // Null case
+ check_deserialization(S3(vec![0, 3, 0]), r#"[null,"3",null]"#);
+ check_error_deserialization::<S3>(
+ r#"[null,3,null]"#,
+ expect![[r#"invalid type: integer `3`, expected a string at line 1 column 7"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/enum_map.rs b/third_party/rust/serde_with/tests/serde_as/enum_map.rs
new file mode 100644
index 0000000000..57a0b8ade4
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/enum_map.rs
@@ -0,0 +1,459 @@
+use super::*;
+use core::{fmt::Write as _, str::FromStr};
+use serde_test::Configure;
+use serde_with::EnumMap;
+use std::net::IpAddr;
+
+fn bytes_debug_readable(bytes: &[u8]) -> String {
+ let mut result = String::with_capacity(bytes.len() * 2);
+ for &byte in bytes {
+ match byte {
+ non_printable if !(0x20..0x7f).contains(&non_printable) => {
+ write!(result, "\\x{:02x}", byte).unwrap();
+ }
+ b'\\' => result.push_str("\\\\"),
+ _ => {
+ result.push(byte as char);
+ }
+ }
+ }
+ result
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+enum EnumValue {
+ Int(i32),
+ String(String),
+ Unit,
+ Tuple(i32, String, bool),
+ Struct {
+ a: i32,
+ b: String,
+ c: bool,
+ },
+ Ip(IpAddr, IpAddr),
+ #[serde(rename = "$value")]
+ Extra(serde_json::Value),
+}
+
+#[serde_as]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+struct VecEnumValues {
+ #[serde_as(as = "EnumMap")]
+ vec: Vec<EnumValue>,
+}
+
+#[test]
+fn json_round_trip() {
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ EnumValue::Int(456),
+ EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ EnumValue::Tuple(1, "Middle".to_string(), false),
+ EnumValue::Struct {
+ a: 666,
+ b: "BBB".to_string(),
+ c: true,
+ },
+ ],
+ };
+
+ let json = serde_json::to_string_pretty(&values).unwrap();
+ expect_test::expect![[r#"
+ {
+ "vec": {
+ "Int": 123,
+ "String": "FooBar",
+ "Int": 456,
+ "String": "XXX",
+ "Unit": null,
+ "Tuple": [
+ 1,
+ "Middle",
+ false
+ ],
+ "Struct": {
+ "a": 666,
+ "b": "BBB",
+ "c": true
+ }
+ }
+ }"#]]
+ .assert_eq(&json);
+ let deser_values: VecEnumValues = serde_json::from_str(&json).unwrap();
+ assert_eq!(values, deser_values);
+}
+
+#[test]
+fn ron_serialize() {
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ EnumValue::Int(456),
+ EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ EnumValue::Tuple(1, "Middle".to_string(), false),
+ EnumValue::Struct {
+ a: 666,
+ b: "BBB".to_string(),
+ c: true,
+ },
+ ],
+ };
+
+ let pretty_config = ron::ser::PrettyConfig::new().new_line("\n".into());
+ let ron = ron::ser::to_string_pretty(&values, pretty_config).unwrap();
+ expect_test::expect![[r#"
+ (
+ vec: {
+ "Int": 123,
+ "String": "FooBar",
+ "Int": 456,
+ "String": "XXX",
+ "Unit": (),
+ "Tuple": (1, "Middle", false),
+ "Struct": (
+ a: 666,
+ b: "BBB",
+ c: true,
+ ),
+ },
+ )"#]]
+ .assert_eq(&ron);
+ // TODO deserializing a Strings as an Identifier seems unsupported
+ let deser_values: ron::Value = ron::de::from_str(&ron).unwrap();
+ expect_test::expect![[r#"
+ Map(
+ Map(
+ {
+ String(
+ "vec",
+ ): Map(
+ Map(
+ {
+ String(
+ "Int",
+ ): Number(
+ Integer(
+ 456,
+ ),
+ ),
+ String(
+ "String",
+ ): String(
+ "XXX",
+ ),
+ String(
+ "Struct",
+ ): Map(
+ Map(
+ {
+ String(
+ "a",
+ ): Number(
+ Integer(
+ 666,
+ ),
+ ),
+ String(
+ "b",
+ ): String(
+ "BBB",
+ ),
+ String(
+ "c",
+ ): Bool(
+ true,
+ ),
+ },
+ ),
+ ),
+ String(
+ "Tuple",
+ ): Seq(
+ [
+ Number(
+ Integer(
+ 1,
+ ),
+ ),
+ String(
+ "Middle",
+ ),
+ Bool(
+ false,
+ ),
+ ],
+ ),
+ String(
+ "Unit",
+ ): Unit,
+ },
+ ),
+ ),
+ },
+ ),
+ )
+ "#]]
+ .assert_debug_eq(&deser_values);
+}
+
+#[test]
+fn xml_round_trip() {
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ EnumValue::Int(456),
+ EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ // serialize_tuple and variants are not supported by XML
+ // EnumValue::Tuple(1, "Middle".to_string(), false),
+ // Cannot be deserialized. It serializes to:
+ // <Struct><EnumValue><a>666</a><b>BBB</b><c>true</c></EnumValue></Struct>
+ // EnumValue::Struct {
+ // a: 666,
+ // b: "BBB".to_string(),
+ // c: true,
+ // },
+ ],
+ };
+
+ let xml = serde_xml_rs::to_string(&values).unwrap();
+ expect_test::expect![[r#"<VecEnumValues><vec><Int>123</Int><String>FooBar</String><Int>456</Int><String>XXX</String><Unit></Unit></vec></VecEnumValues>"#]]
+ .assert_eq(&xml);
+ let deser_values: VecEnumValues = serde_xml_rs::from_str(&xml).unwrap();
+ assert_eq!(values, deser_values);
+}
+
+#[test]
+fn serde_test_round_trip() {
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ EnumValue::Int(456),
+ EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ EnumValue::Tuple(1, "Middle".to_string(), false),
+ EnumValue::Struct {
+ a: 666,
+ b: "BBB".to_string(),
+ c: true,
+ },
+ ],
+ };
+
+ use serde_test::Token::*;
+ serde_test::assert_tokens(
+ &values.readable(),
+ &[
+ Struct {
+ name: "VecEnumValues",
+ len: 1,
+ },
+ Str("vec"),
+ Map {
+ len: Option::Some(7),
+ },
+ Str("Int"),
+ I32(123),
+ Str("String"),
+ Str("FooBar"),
+ Str("Int"),
+ I32(456),
+ Str("String"),
+ Str("XXX"),
+ Str("Unit"),
+ Unit,
+ Str("Tuple"),
+ TupleStruct {
+ name: "EnumValue",
+ len: 3,
+ },
+ I32(1),
+ Str("Middle"),
+ Bool(false),
+ TupleStructEnd,
+ Str("Struct"),
+ Struct {
+ name: "EnumValue",
+ len: 3,
+ },
+ Str("a"),
+ I32(666),
+ Str("b"),
+ Str("BBB"),
+ Str("c"),
+ Bool(true),
+ StructEnd,
+ MapEnd,
+ StructEnd,
+ ],
+ );
+}
+
+#[test]
+fn serde_test_round_trip_human_readable() {
+ let values = VecEnumValues {
+ vec: vec![EnumValue::Ip(
+ IpAddr::from_str("127.0.0.1").unwrap(),
+ IpAddr::from_str("::7777:dead:beef").unwrap(),
+ )],
+ };
+
+ use serde_test::Token::*;
+ serde_test::assert_tokens(
+ &values.clone().readable(),
+ &[
+ Struct {
+ name: "VecEnumValues",
+ len: 1,
+ },
+ Str("vec"),
+ Map {
+ len: Option::Some(1),
+ },
+ Str("Ip"),
+ TupleStruct {
+ name: "EnumValue",
+ len: 2,
+ },
+ Str("127.0.0.1"),
+ Str("::7777:dead:beef"),
+ TupleStructEnd,
+ MapEnd,
+ StructEnd,
+ ],
+ );
+
+ serde_test::assert_tokens(
+ &values.compact(),
+ &[
+ Struct {
+ name: "VecEnumValues",
+ len: 1,
+ },
+ Str("vec"),
+ Map {
+ len: Option::Some(1),
+ },
+ Str("Ip"),
+ TupleStruct {
+ name: "EnumValue",
+ len: 2,
+ },
+ NewtypeVariant {
+ name: "IpAddr",
+ variant: "V4",
+ },
+ Tuple { len: 4 },
+ U8(127),
+ U8(0),
+ U8(0),
+ U8(1),
+ TupleEnd,
+ NewtypeVariant {
+ name: "IpAddr",
+ variant: "V6",
+ },
+ Tuple { len: 16 },
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0),
+ U8(0x77),
+ U8(0x77),
+ U8(0xde),
+ U8(0xad),
+ U8(0xbe),
+ U8(0xef),
+ TupleEnd,
+ TupleStructEnd,
+ MapEnd,
+ StructEnd,
+ ],
+ );
+}
+
+// Bincode does not support Deserializer::deserialize_identifier
+// https://github.com/bincode-org/bincode/blob/e0ac3245162ba668ba04591897dd88ff5b3096b8/src/de/mod.rs#L442
+
+#[test]
+fn rmp_round_trip() {
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ EnumValue::Int(456),
+ EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ EnumValue::Tuple(1, "Middle".to_string(), false),
+ EnumValue::Struct {
+ a: 666,
+ b: "BBB".to_string(),
+ c: true,
+ },
+ EnumValue::Ip(
+ IpAddr::from_str("127.0.0.1").unwrap(),
+ IpAddr::from_str("::7777:dead:beef").unwrap(),
+ ),
+ ],
+ };
+
+ let rmp = rmp_serde::to_vec(&values).unwrap();
+ expect_test::expect![[r#"\x91\x88\xa3Int{\xa6String\xa6FooBar\xa3Int\xcd\x01\xc8\xa6String\xa3XXX\xa4Unit\xc0\xa5Tuple\x93\x01\xa6Middle\xc2\xa6Struct\x93\xcd\x02\x9a\xa3BBB\xc3\xa2Ip\x92\x81\xa2V4\x94\x7f\x00\x00\x01\x81\xa2V6\xdc\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ww\xcc\xde\xcc\xad\xcc\xbe\xcc\xef"#]]
+ .assert_eq(&bytes_debug_readable(&rmp));
+ let deser_values: VecEnumValues = rmp_serde::from_read(&*rmp).unwrap();
+ assert_eq!(values, deser_values);
+}
+
+#[test]
+fn yaml_round_trip() {
+ // Duplicate enum variants do not work with YAML
+ let values = VecEnumValues {
+ vec: vec![
+ EnumValue::Int(123),
+ EnumValue::String("FooBar".to_string()),
+ // EnumValue::Int(456),
+ // EnumValue::String("XXX".to_string()),
+ EnumValue::Unit,
+ EnumValue::Tuple(1, "Middle".to_string(), false),
+ EnumValue::Struct {
+ a: 666,
+ b: "BBB".to_string(),
+ c: true,
+ },
+ ],
+ };
+
+ let yaml = serde_yaml::to_string(&values).unwrap();
+ expect_test::expect![[r#"
+ ---
+ vec:
+ Int: 123
+ String: FooBar
+ Unit: ~
+ Tuple:
+ - 1
+ - Middle
+ - false
+ Struct:
+ a: 666
+ b: BBB
+ c: true
+ "#]]
+ .assert_eq(&yaml);
+ let deser_values: VecEnumValues = serde_yaml::from_str(&yaml).unwrap();
+ assert_eq!(values, deser_values);
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/frominto.rs b/third_party/rust/serde_with/tests/serde_as/frominto.rs
new file mode 100644
index 0000000000..f7f3cac213
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/frominto.rs
@@ -0,0 +1,187 @@
+use super::*;
+use core::convert::TryFrom;
+use serde_with::{FromInto, TryFromInto};
+
+#[derive(Clone, Debug, PartialEq)]
+enum IntoSerializable {
+ A,
+ B,
+ C,
+}
+
+impl From<IntoSerializable> for String {
+ fn from(value: IntoSerializable) -> Self {
+ match value {
+ IntoSerializable::A => "String A",
+ IntoSerializable::B => "Some other value",
+ IntoSerializable::C => "Looks like 123",
+ }
+ .to_string()
+ }
+}
+
+#[derive(Debug, PartialEq)]
+enum FromDeserializable {
+ Zero,
+ Odd(u32),
+ Even(u32),
+}
+
+impl From<u32> for FromDeserializable {
+ fn from(value: u32) -> Self {
+ match value {
+ 0 => FromDeserializable::Zero,
+ e if e % 2 == 0 => FromDeserializable::Even(e),
+ o => FromDeserializable::Odd(o),
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum LikeBool {
+ Trueish,
+ Falseisch,
+}
+
+impl From<bool> for LikeBool {
+ fn from(b: bool) -> Self {
+ if b {
+ LikeBool::Trueish
+ } else {
+ LikeBool::Falseisch
+ }
+ }
+}
+
+impl From<LikeBool> for bool {
+ fn from(lb: LikeBool) -> Self {
+ match lb {
+ LikeBool::Trueish => true,
+ LikeBool::Falseisch => false,
+ }
+ }
+}
+
+#[test]
+fn test_frominto_ser() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize)]
+ struct S(#[serde_as(serialize_as = "FromInto<String>")] IntoSerializable);
+
+ check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]);
+ check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]);
+ check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]);
+}
+
+#[test]
+fn test_tryfrominto_ser() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize)]
+ struct S(#[serde_as(serialize_as = "TryFromInto<String>")] IntoSerializable);
+
+ check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]);
+ check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]);
+ check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]);
+}
+
+#[test]
+fn test_frominto_de() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize)]
+ struct S(#[serde_as(deserialize_as = "FromInto<u32>")] FromDeserializable);
+
+ check_deserialization(S(FromDeserializable::Zero), "0");
+ check_deserialization(S(FromDeserializable::Odd(1)), "1");
+ check_deserialization(S(FromDeserializable::Odd(101)), "101");
+ check_deserialization(S(FromDeserializable::Even(2)), "2");
+ check_deserialization(S(FromDeserializable::Even(202)), "202");
+}
+
+#[test]
+fn test_tryfrominto_de() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize)]
+ struct S(#[serde_as(deserialize_as = "TryFromInto<u32>")] FromDeserializable);
+
+ check_deserialization(S(FromDeserializable::Zero), "0");
+ check_deserialization(S(FromDeserializable::Odd(1)), "1");
+ check_deserialization(S(FromDeserializable::Odd(101)), "101");
+ check_deserialization(S(FromDeserializable::Even(2)), "2");
+ check_deserialization(S(FromDeserializable::Even(202)), "202");
+}
+
+#[test]
+fn test_frominto_de_and_ser() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize, Serialize)]
+ struct S(#[serde_as(as = "FromInto<bool>")] LikeBool);
+
+ is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]);
+ is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]);
+}
+
+#[test]
+fn test_tryfrominto_de_and_ser() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize, Serialize)]
+ struct S(#[serde_as(as = "TryFromInto<bool>")] LikeBool);
+
+ is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]);
+ is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]);
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum TryIntoSerializable {
+ Works,
+ Fails,
+}
+
+impl TryFrom<TryIntoSerializable> for String {
+ type Error = &'static str;
+
+ fn try_from(value: TryIntoSerializable) -> Result<Self, Self::Error> {
+ match value {
+ TryIntoSerializable::Works => Ok("Works".to_string()),
+ TryIntoSerializable::Fails => Err("Fails cannot be turned into String"),
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+enum TryFromDeserializable {
+ Zero,
+}
+
+impl TryFrom<u32> for TryFromDeserializable {
+ type Error = &'static str;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ match value {
+ 0 => Ok(TryFromDeserializable::Zero),
+ _ => Err("Number is not zero"),
+ }
+ }
+}
+
+#[test]
+fn test_tryfrominto_ser_with_error() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize)]
+ struct S(#[serde_as(serialize_as = "TryFromInto<String>")] TryIntoSerializable);
+
+ check_serialization(S(TryIntoSerializable::Works), expect![[r#""Works""#]]);
+ check_error_serialization(
+ S(TryIntoSerializable::Fails),
+ expect![[r#"Fails cannot be turned into String"#]],
+ );
+}
+
+#[test]
+fn test_tryfrominto_de_with_error() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize)]
+ struct S(#[serde_as(deserialize_as = "TryFromInto<u32>")] TryFromDeserializable);
+
+ check_deserialization(S(TryFromDeserializable::Zero), "0");
+ check_error_deserialization::<S>("1", expect![[r#"Number is not zero"#]]);
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/lib.rs b/third_party/rust/serde_with/tests/serde_as/lib.rs
new file mode 100644
index 0000000000..0b40ce0c26
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/lib.rs
@@ -0,0 +1,1129 @@
+#![allow(
+ // clippy is broken and shows wrong warnings
+ // clippy on stable does not know yet about the lint name
+ unknown_lints,
+ // https://github.com/rust-lang/rust-clippy/issues/8867
+ clippy::derive_partial_eq_without_eq,
+)]
+
+extern crate alloc;
+
+mod collections;
+mod default_on;
+mod enum_map;
+mod frominto;
+mod map_tuple_list;
+mod pickfirst;
+mod serde_as_macro;
+mod serde_conv;
+mod time;
+#[path = "../utils.rs"]
+mod utils;
+
+use crate::utils::*;
+use alloc::{
+ collections::{BTreeMap, BTreeSet, LinkedList, VecDeque},
+ rc::{Rc, Weak as RcWeak},
+ sync::{Arc, Weak as ArcWeak},
+};
+use core::cell::{Cell, RefCell};
+use expect_test::expect;
+use serde::{Deserialize, Serialize};
+use serde_with::{
+ formats::{Flexible, Strict},
+ serde_as, BoolFromInt, BytesOrString, CommaSeparator, DisplayFromStr, NoneAsEmptyString,
+ OneOrMany, Same, StringWithSeparator,
+};
+use std::{
+ collections::HashMap,
+ sync::{Mutex, RwLock},
+};
+
+#[test]
+fn test_basic_wrappers() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SBox(#[serde_as(as = "Box<DisplayFromStr>")] Box<u32>);
+
+ is_equal(SBox(Box::new(123)), expect![[r#""123""#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SRc(#[serde_as(as = "Rc<DisplayFromStr>")] Rc<u32>);
+
+ is_equal(SRc(Rc::new(123)), expect![[r#""123""#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize)]
+ struct SRcWeak(#[serde_as(as = "RcWeak<DisplayFromStr>")] RcWeak<u32>);
+
+ check_serialization(SRcWeak(RcWeak::new()), expect![[r#"null"#]]);
+ let s: SRcWeak = serde_json::from_str("null").unwrap();
+ assert!(s.0.upgrade().is_none());
+ let s: SRcWeak = serde_json::from_str("\"123\"").unwrap();
+ assert!(s.0.upgrade().is_none());
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SArc(#[serde_as(as = "Arc<DisplayFromStr>")] Arc<u32>);
+
+ is_equal(SArc(Arc::new(123)), expect![[r#""123""#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize)]
+ struct SArcWeak(#[serde_as(as = "ArcWeak<DisplayFromStr>")] ArcWeak<u32>);
+
+ check_serialization(SArcWeak(ArcWeak::new()), expect![[r#"null"#]]);
+ let s: SArcWeak = serde_json::from_str("null").unwrap();
+ assert!(s.0.upgrade().is_none());
+ let s: SArcWeak = serde_json::from_str("\"123\"").unwrap();
+ assert!(s.0.upgrade().is_none());
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SCell(#[serde_as(as = "Cell<DisplayFromStr>")] Cell<u32>);
+
+ is_equal(SCell(Cell::new(123)), expect![[r#""123""#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SRefCell(#[serde_as(as = "RefCell<DisplayFromStr>")] RefCell<u32>);
+
+ is_equal(SRefCell(RefCell::new(123)), expect![[r#""123""#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize)]
+ struct SMutex(#[serde_as(as = "Mutex<DisplayFromStr>")] Mutex<u32>);
+
+ check_serialization(SMutex(Mutex::new(123)), expect![[r#""123""#]]);
+ let s: SMutex = serde_json::from_str("\"123\"").unwrap();
+ assert_eq!(*s.0.lock().unwrap(), 123);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize)]
+ struct SRwLock(#[serde_as(as = "RwLock<DisplayFromStr>")] RwLock<u32>);
+
+ let expected = expect![[r#""123""#]];
+ check_serialization(SRwLock(RwLock::new(123)), expected);
+ let s: SRwLock = serde_json::from_str("\"123\"").unwrap();
+ assert_eq!(*s.0.read().unwrap(), 123);
+}
+
+#[test]
+fn test_option() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "_")] Option<u32>);
+
+ is_equal(S(None), expect![[r#"null"#]]);
+ is_equal(S(Some(9)), expect![[r#"9"#]]);
+ check_error_deserialization::<S>(
+ r#"{}"#,
+ expect![[r#"invalid type: map, expected u32 at line 1 column 0"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Struct {
+ #[serde_as(as = "_")]
+ value: Option<u32>,
+ }
+ check_error_deserialization::<Struct>(
+ r#"{}"#,
+ expect![[r#"missing field `value` at line 1 column 2"#]],
+ );
+}
+
+#[test]
+fn test_result() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(
+ #[serde_as(as = "Result<StringWithSeparator<CommaSeparator, u32>, DisplayFromStr>")]
+ Result<Vec<u32>, u32>,
+ );
+
+ is_equal(
+ S(Ok(vec![1, 2, 3])),
+ expect![[r#"
+ {
+ "Ok": "1,2,3"
+ }"#]],
+ );
+ is_equal(
+ S(Err(9)),
+ expect![[r#"
+ {
+ "Err": "9"
+ }"#]],
+ );
+ check_error_deserialization::<S>(r#"{}"#, expect![[r#"expected value at line 1 column 2"#]]);
+}
+
+#[test]
+fn test_display_fromstr() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "DisplayFromStr")] u32);
+
+ is_equal(S(123), expect![[r#""123""#]]);
+}
+
+#[test]
+fn test_tuples() {
+ use std::net::IpAddr;
+ let ip = "1.2.3.4".parse().unwrap();
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1(#[serde_as(as = "(DisplayFromStr,)")] (u32,));
+ is_equal(
+ S1((1,)),
+ expect![[r#"
+ [
+ "1"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2a(#[serde_as(as = "(DisplayFromStr, DisplayFromStr)")] (u32, IpAddr));
+ is_equal(
+ S2a((555_888, ip)),
+ expect![[r#"
+ [
+ "555888",
+ "1.2.3.4"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2b(#[serde_as(as = "(_, DisplayFromStr)")] (u32, IpAddr));
+ is_equal(
+ S2b((987, ip)),
+ expect![[r#"
+ [
+ 987,
+ "1.2.3.4"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2c(#[serde_as(as = "(Same, DisplayFromStr)")] (u32, IpAddr));
+ is_equal(
+ S2c((987, ip)),
+ expect![[r#"
+ [
+ 987,
+ "1.2.3.4"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S6(
+ #[serde_as(as = "(Same, Same, Same, Same, Same, Same)")] (u8, u16, u32, i8, i16, i32),
+ );
+ is_equal(
+ S6((8, 16, 32, -8, 16, -32)),
+ expect![[r#"
+ [
+ 8,
+ 16,
+ 32,
+ -8,
+ 16,
+ -32
+ ]"#]],
+ );
+}
+
+#[test]
+fn test_arrays() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S0(#[serde_as(as = "[DisplayFromStr; 0]")] [u32; 0]);
+ is_equal(S0([]), expect![[r#"[]"#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1(#[serde_as(as = "[DisplayFromStr; 1]")] [u32; 1]);
+ is_equal(
+ S1([1]),
+ expect![[r#"
+ [
+ "1"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(#[serde_as(as = "[Same; 2]")] [u32; 2]);
+ is_equal(
+ S2([11, 22]),
+ expect![[r#"
+ [
+ 11,
+ 22
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S32(#[serde_as(as = "[Same; 32]")] [u32; 32]);
+ is_equal(
+ S32([
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ ]),
+ expect![[r#"
+ [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31
+ ]"#]],
+ );
+}
+
+#[test]
+fn test_sequence_like_types() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1(#[serde_as(as = "Box<[Same]>")] Box<[u32]>);
+ is_equal(
+ S1(vec![1, 2, 3, 99].into()),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3,
+ 99
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(#[serde_as(as = "BTreeSet<Same>")] BTreeSet<u32>);
+ is_equal(
+ S2(vec![1, 2, 3, 99].into_iter().collect()),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3,
+ 99
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S3(#[serde_as(as = "LinkedList<Same>")] LinkedList<u32>);
+ is_equal(
+ S3(vec![1, 2, 3, 99].into_iter().collect()),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3,
+ 99
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S4(#[serde_as(as = "Vec<Same>")] Vec<u32>);
+ is_equal(
+ S4(vec![1, 2, 3, 99]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3,
+ 99
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S5(#[serde_as(as = "VecDeque<Same>")] VecDeque<u32>);
+ is_equal(
+ S5(vec![1, 2, 3, 99].into()),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3,
+ 99
+ ]"#]],
+ );
+}
+
+#[test]
+fn test_none_as_empty_string() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "NoneAsEmptyString")] Option<String>);
+
+ is_equal(S(None), expect![[r#""""#]]);
+ is_equal(S(Some("Hello".to_string())), expect![[r#""Hello""#]]);
+}
+
+#[test]
+fn test_bytes_or_string() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "BytesOrString")] Vec<u8>);
+
+ is_equal(
+ S(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3
+ ]"#]],
+ );
+ check_deserialization(S(vec![72, 101, 108, 108, 111]), r#""Hello""#);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SVec(#[serde_as(as = "Vec<BytesOrString>")] Vec<Vec<u8>>);
+
+ is_equal(
+ SVec(vec![vec![1, 2, 3]]),
+ expect![[r#"
+ [
+ [
+ 1,
+ 2,
+ 3
+ ]
+ ]"#]],
+ );
+ check_deserialization(
+ SVec(vec![
+ vec![72, 101, 108, 108, 111],
+ vec![87, 111, 114, 108, 100],
+ vec![1, 2, 3],
+ ]),
+ r#"["Hello","World",[1,2,3]]"#,
+ );
+}
+
+#[test]
+fn string_with_separator() {
+ use serde_with::{rust::StringWithSeparator, CommaSeparator, SpaceSeparator};
+
+ #[serde_as]
+ #[derive(Deserialize, Serialize)]
+ struct A {
+ #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
+ tags: Vec<String>,
+ #[serde_as(as = "StringWithSeparator::<CommaSeparator, String>")]
+ // more_tags: Vec<String>,
+ more_tags: BTreeSet<String>,
+ }
+
+ let v: A = serde_json::from_str(
+ r##"{
+ "tags": "#hello #world",
+ "more_tags": "foo,bar,bar"
+}"##,
+ )
+ .unwrap();
+ assert_eq!(vec!["#hello", "#world"], v.tags);
+ assert_eq!(2, v.more_tags.len());
+
+ let x = A {
+ tags: vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ more_tags: Default::default(),
+ };
+ assert_eq!(
+ r#"{"tags":"1 2 3","more_tags":""}"#,
+ serde_json::to_string(&x).unwrap()
+ );
+}
+
+#[test]
+fn test_vec_skip_error() {
+ use serde_with::VecSkipError;
+
+ #[serde_as]
+ #[derive(Debug, PartialEq, Deserialize, Serialize)]
+ struct S {
+ tag: String,
+ #[serde_as(as = "VecSkipError<_>")]
+ values: Vec<u8>,
+ }
+
+ check_deserialization(
+ S {
+ tag: "type".into(),
+ values: vec![0, 1],
+ },
+ r#"{"tag":"type","values":[0, "str", 1, [10, 11], -2, {}, 300]}"#,
+ );
+ is_equal(
+ S {
+ tag: "round-trip".into(),
+ values: vec![0, 255],
+ },
+ expect![[r#"
+ {
+ "tag": "round-trip",
+ "values": [
+ 0,
+ 255
+ ]
+ }"#]],
+ );
+}
+
+#[test]
+fn test_serialize_reference() {
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S1<'a>(#[serde_as(as = "Vec<DisplayFromStr>")] &'a Vec<u32>);
+ check_serialization(
+ S1(&vec![1, 2]),
+ expect![[r#"
+ [
+ "1",
+ "2"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S1a<'a>(#[serde_as(as = "&Vec<DisplayFromStr>")] &'a Vec<u32>);
+ check_serialization(
+ S1(&vec![1, 2]),
+ expect![[r#"
+ [
+ "1",
+ "2"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S1Mut<'a>(#[serde_as(as = "Vec<DisplayFromStr>")] &'a mut Vec<u32>);
+ check_serialization(
+ S1(&vec![1, 2]),
+ expect![[r#"
+ [
+ "1",
+ "2"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S1aMut<'a>(#[serde_as(as = "&mut Vec<DisplayFromStr>")] &'a mut Vec<u32>);
+ check_serialization(
+ S1(&vec![1, 2]),
+ expect![[r#"
+ [
+ "1",
+ "2"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S2<'a>(#[serde_as(as = "&[DisplayFromStr]")] &'a [u32]);
+ check_serialization(
+ S2(&[1, 2]),
+ expect![[r#"
+ [
+ "1",
+ "2"
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S3<'a>(
+ #[serde_as(as = "&BTreeMap<DisplayFromStr, DisplayFromStr>")] &'a BTreeMap<bool, u32>,
+ );
+ let bmap = vec![(false, 123), (true, 456)].into_iter().collect();
+ check_serialization(
+ S3(&bmap),
+ expect![[r#"
+ {
+ "false": "123",
+ "true": "456"
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S4<'a>(#[serde_as(as = "&Vec<(_, DisplayFromStr)>")] &'a BTreeMap<bool, u32>);
+ let bmap = vec![(false, 123), (true, 456)].into_iter().collect();
+ check_serialization(
+ S4(&bmap),
+ expect![[r#"
+ [
+ [
+ false,
+ "123"
+ ],
+ [
+ true,
+ "456"
+ ]
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize)]
+ struct S5<'a>(
+ #[serde_as(as = "&BTreeMap<DisplayFromStr, &Vec<(_, _)>>")]
+ &'a Vec<(u32, &'a BTreeMap<bool, String>)>,
+ );
+ let bmap0 = vec![(false, "123".to_string()), (true, "456".to_string())]
+ .into_iter()
+ .collect();
+ let bmap1 = vec![(true, "Hello".to_string()), (false, "World".to_string())]
+ .into_iter()
+ .collect();
+ let vec = vec![(111, &bmap0), (999, &bmap1)];
+ check_serialization(
+ S5(&vec),
+ expect![[r#"
+ {
+ "111": [
+ [
+ false,
+ "123"
+ ],
+ [
+ true,
+ "456"
+ ]
+ ],
+ "999": [
+ [
+ false,
+ "World"
+ ],
+ [
+ true,
+ "Hello"
+ ]
+ ]
+ }"#]],
+ );
+}
+
+#[test]
+fn test_big_arrays() {
+ // Single Big Array
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct S1(#[serde_as(as = "[_; 64]")] [u8; 64]);
+ is_equal_compact(
+ S1([0; 64]),
+ expect![[
+ r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#
+ ]],
+ );
+ // Too few entries
+ check_error_deserialization::<S1>(
+ r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#,
+ expect![[r#"invalid length 14, expected an array of size 64 at line 1 column 29"#]],
+ );
+ // Too many entries
+ check_error_deserialization::<S1>(
+ r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#,
+ expect![[r#"trailing characters at line 1 column 130"#]],
+ );
+
+ // Single Big Array
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct S2(#[serde_as(as = "[DisplayFromStr; 40]")] [u8; 40]);
+ is_equal_compact(
+ S2([0; 40]),
+ expect![[
+ r#"["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]"#
+ ]],
+ );
+
+ // Nested Big Arrays
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct S3(#[serde_as(as = "[[_; 34]; 33]")] [[u8; 34]; 33]);
+ is_equal_compact(
+ S3([[0; 34]; 33]),
+ expect![[
+ r#"[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]"#
+ ]],
+ );
+}
+
+#[test]
+fn test_bytes() {
+ // The test case is copied from
+ // https://github.com/serde-rs/bytes/blob/cbae606b9dc225fc094b031cc84eac9493da2058/tests/test_derive.rs
+ // Original code by @dtolnay
+
+ use alloc::borrow::Cow;
+ use serde_test::{assert_de_tokens, assert_tokens, Token};
+ use serde_with::Bytes;
+
+ #[serde_as]
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ struct Test<'a> {
+ #[serde_as(as = "Bytes")]
+ array: [u8; 52],
+
+ #[serde_as(as = "Bytes")]
+ slice: &'a [u8],
+
+ #[serde_as(as = "Bytes")]
+ vec: Vec<u8>,
+
+ #[serde_as(as = "Bytes")]
+ cow_slice: Cow<'a, [u8]>,
+
+ #[serde_as(as = "Box<Bytes>")]
+ boxed_array: Box<[u8; 52]>,
+
+ #[serde_as(as = "Bytes")]
+ boxed_array2: Box<[u8; 52]>,
+
+ #[serde_as(as = "Bytes")]
+ boxed_slice: Box<[u8]>,
+
+ #[serde_as(as = "Option<Bytes>")]
+ opt_slice: Option<&'a [u8]>,
+
+ #[serde_as(as = "Option<Bytes>")]
+ opt_vec: Option<Vec<u8>>,
+
+ #[serde_as(as = "Option<Bytes>")]
+ opt_cow_slice: Option<Cow<'a, [u8]>>,
+ }
+
+ let test = Test {
+ array: *b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz",
+ slice: b"...",
+ vec: b"...".to_vec(),
+ cow_slice: Cow::Borrowed(b"..."),
+ boxed_array: Box::new(*b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ boxed_array2: Box::new(*b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ boxed_slice: b"...".to_vec().into_boxed_slice(),
+ opt_slice: Some(b"..."),
+ opt_vec: Some(b"...".to_vec()),
+ opt_cow_slice: Some(Cow::Borrowed(b"...")),
+ };
+
+ assert_tokens(
+ &test,
+ &[
+ Token::Struct {
+ name: "Test",
+ len: 10,
+ },
+ Token::Str("array"),
+ Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("slice"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("vec"),
+ Token::Bytes(b"..."),
+ Token::Str("cow_slice"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("boxed_array"),
+ Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("boxed_array2"),
+ Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("boxed_slice"),
+ Token::Bytes(b"..."),
+ Token::Str("opt_slice"),
+ Token::Some,
+ Token::BorrowedBytes(b"..."),
+ Token::Str("opt_vec"),
+ Token::Some,
+ Token::Bytes(b"..."),
+ Token::Str("opt_cow_slice"),
+ Token::Some,
+ Token::BorrowedBytes(b"..."),
+ Token::StructEnd,
+ ],
+ );
+
+ // Test string deserialization
+ assert_de_tokens(
+ &test,
+ &[
+ Token::Struct {
+ name: "Test",
+ len: 10,
+ },
+ Token::Str("array"),
+ Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("slice"),
+ Token::BorrowedStr("..."),
+ Token::Str("vec"),
+ Token::Bytes(b"..."),
+ Token::Str("cow_slice"),
+ Token::BorrowedStr("..."),
+ Token::Str("boxed_array"),
+ Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("boxed_array2"),
+ Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"),
+ Token::Str("boxed_slice"),
+ Token::Bytes(b"..."),
+ Token::Str("opt_slice"),
+ Token::Some,
+ Token::BorrowedStr("..."),
+ Token::Str("opt_vec"),
+ Token::Some,
+ Token::Bytes(b"..."),
+ Token::Str("opt_cow_slice"),
+ Token::Some,
+ Token::BorrowedStr("..."),
+ Token::StructEnd,
+ ],
+ );
+}
+
+#[test]
+fn test_one_or_many_prefer_one() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1Vec(#[serde_as(as = "OneOrMany<_>")] Vec<u32>);
+
+ // Normal
+ is_equal(S1Vec(vec![]), expect![[r#"[]"#]]);
+ is_equal(S1Vec(vec![1]), expect![[r#"1"#]]);
+ is_equal(
+ S1Vec(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3
+ ]"#]],
+ );
+ check_deserialization(S1Vec(vec![1]), r#"1"#);
+ check_deserialization(S1Vec(vec![1]), r#"[1]"#);
+ check_error_deserialization::<S1Vec>(r#"{}"#, expect![[r#"a list or single element"#]]);
+ check_error_deserialization::<S1Vec>(r#""xx""#, expect![[r#"a list or single element"#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2Vec(#[serde_as(as = "OneOrMany<DisplayFromStr>")] Vec<u32>);
+
+ // Normal
+ is_equal(S2Vec(vec![]), expect![[r#"[]"#]]);
+ is_equal(S2Vec(vec![1]), expect![[r#""1""#]]);
+ is_equal(
+ S2Vec(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#]],
+ );
+ check_deserialization(S2Vec(vec![1]), r#""1""#);
+ check_deserialization(S2Vec(vec![1]), r#"["1"]"#);
+ check_error_deserialization::<S2Vec>(r#"{}"#, expect![[r#"a list or single element"#]]);
+ check_error_deserialization::<S2Vec>(r#""xx""#, expect![[r#"a list or single element"#]]);
+}
+
+#[test]
+fn test_one_or_many_prefer_many() {
+ use serde_with::formats::PreferMany;
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1Vec(#[serde_as(as = "OneOrMany<_, PreferMany>")] Vec<u32>);
+
+ // Normal
+ is_equal(S1Vec(vec![]), expect![[r#"[]"#]]);
+ is_equal(
+ S1Vec(vec![1]),
+ expect![[r#"
+ [
+ 1
+ ]"#]],
+ );
+ is_equal(
+ S1Vec(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3
+ ]"#]],
+ );
+ check_deserialization(S1Vec(vec![1]), r#"1"#);
+ check_deserialization(S1Vec(vec![1]), r#"[1]"#);
+ check_error_deserialization::<S1Vec>(r#"{}"#, expect![[r#"a list or single element"#]]);
+ check_error_deserialization::<S1Vec>(r#""xx""#, expect![[r#"a list or single element"#]]);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2Vec(#[serde_as(as = "OneOrMany<DisplayFromStr, PreferMany>")] Vec<u32>);
+
+ // Normal
+ is_equal(S2Vec(vec![]), expect![[r#"[]"#]]);
+ is_equal(
+ S2Vec(vec![1]),
+ expect![[r#"
+ [
+ "1"
+ ]"#]],
+ );
+ is_equal(
+ S2Vec(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#]],
+ );
+ check_deserialization(S2Vec(vec![1]), r#""1""#);
+ check_deserialization(S2Vec(vec![1]), r#"["1"]"#);
+ check_error_deserialization::<S2Vec>(r#"{}"#, expect![[r#"a list or single element"#]]);
+ check_error_deserialization::<S2Vec>(r#""xx""#, expect![[r#"a list or single element"#]]);
+}
+
+/// Test that Cow borrows from the input
+#[test]
+fn test_borrow_cow_str() {
+ use alloc::borrow::Cow;
+ use serde_test::{assert_ser_tokens, Token};
+ use serde_with::BorrowCow;
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S1<'a> {
+ #[serde_as(as = "BorrowCow")]
+ cow: Cow<'a, str>,
+ #[serde_as(as = "Option<BorrowCow>")]
+ opt: Option<Cow<'a, str>>,
+ #[serde_as(as = "Box<BorrowCow>")]
+ b: Box<Cow<'a, str>>,
+ #[serde_as(as = "[BorrowCow; 1]")]
+ arr: [Cow<'a, str>; 1],
+ }
+
+ assert_ser_tokens(
+ &S1 {
+ cow: "abc".into(),
+ opt: Some("foo".into()),
+ b: Box::new("bar".into()),
+ arr: ["def".into()],
+ },
+ &[
+ Token::Struct { name: "S1", len: 4 },
+ Token::Str("cow"),
+ Token::BorrowedStr("abc"),
+ Token::Str("opt"),
+ Token::Some,
+ Token::BorrowedStr("foo"),
+ Token::Str("b"),
+ Token::BorrowedStr("bar"),
+ Token::Str("arr"),
+ Token::Tuple { len: 1 },
+ Token::BorrowedStr("def"),
+ Token::TupleEnd,
+ Token::StructEnd,
+ ],
+ );
+ let s1: S1<'_> = serde_json::from_str(
+ r#"{
+ "cow": "abc",
+ "opt": "foo",
+ "b": "bar",
+ "arr": ["def"]
+ }"#,
+ )
+ .unwrap();
+ assert!(matches!(s1.cow, Cow::Borrowed(_)));
+ assert!(matches!(s1.opt, Some(Cow::Borrowed(_))));
+ assert!(matches!(*s1.b, Cow::Borrowed(_)));
+ assert!(matches!(s1.arr, [Cow::Borrowed(_)]));
+ let s1: S1<'_> = serde_json::from_str(
+ r#"{
+ "cow": "a\"c",
+ "opt": "f\"o",
+ "b": "b\"r",
+ "arr": ["d\"f"]
+ }"#,
+ )
+ .unwrap();
+ assert!(matches!(s1.cow, Cow::Owned(_)));
+ assert!(matches!(s1.opt, Some(Cow::Owned(_))));
+ assert!(matches!(*s1.b, Cow::Owned(_)));
+ assert!(matches!(s1.arr, [Cow::Owned(_)]));
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2<'a> {
+ #[serde_as(as = "BorrowCow")]
+ cow: Cow<'a, [u8]>,
+ #[serde_as(as = "Option<BorrowCow>")]
+ opt: Option<Cow<'a, [u8]>>,
+ }
+
+ assert_ser_tokens(
+ &S2 {
+ cow: b"abc"[..].into(),
+ opt: Some(b"foo"[..].into()),
+ },
+ &[
+ Token::Struct { name: "S2", len: 2 },
+ Token::Str("cow"),
+ Token::Seq { len: Some(3) },
+ Token::U8(b'a'),
+ Token::U8(b'b'),
+ Token::U8(b'c'),
+ Token::SeqEnd,
+ Token::Str("opt"),
+ Token::Some,
+ Token::Seq { len: Some(3) },
+ Token::U8(b'f'),
+ Token::U8(b'o'),
+ Token::U8(b'o'),
+ Token::SeqEnd,
+ Token::StructEnd,
+ ],
+ );
+ let tokens = &[
+ Token::Struct { name: "S2", len: 2 },
+ Token::Str("cow"),
+ Token::BorrowedBytes(b"abc"),
+ Token::Str("opt"),
+ Token::Some,
+ Token::BorrowedBytes(b"foo"),
+ Token::StructEnd,
+ ];
+ let mut deser = serde_test::Deserializer::new(tokens);
+ let s2 = S2::deserialize(&mut deser).unwrap();
+ assert!(matches!(s2.cow, Cow::Borrowed(_)));
+ assert!(matches!(s2.opt, Some(Cow::Borrowed(_))));
+
+ // Check that a manual borrow works too
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S3<'a>(
+ #[serde(borrow = "'a")]
+ #[serde_as(as = "BorrowCow")]
+ Cow<'a, [u8]>,
+ // TODO add a test for Cow<'b, [u8; N]>
+ // #[serde_as(as = "BorrowCow")]
+ // Cow<'b, [u8; N]>,
+ );
+ let tokens = &[
+ Token::NewtypeStruct { name: "S3" },
+ Token::BorrowedBytes(b"abc"),
+ ];
+
+ let mut deser = serde_test::Deserializer::new(tokens);
+ let s3 = S3::deserialize(&mut deser).unwrap();
+ assert!(matches!(s3.0, Cow::Borrowed(_)));
+}
+
+#[test]
+fn test_boolfromint() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "BoolFromInt")] bool);
+
+ is_equal(S(false), expect![[r#"0"#]]);
+ is_equal(S(true), expect![[r#"1"#]]);
+ check_error_deserialization::<S>(
+ "2",
+ expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]],
+ );
+ check_error_deserialization::<S>(
+ "-100",
+ expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]],
+ );
+ check_error_deserialization::<S>(
+ "18446744073709551615",
+ expect![[
+ r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"#
+ ]],
+ );
+ check_error_deserialization::<S>(
+ r#""""#,
+ expect![[r#"invalid type: string "", expected an integer 0 or 1 at line 1 column 2"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SStrict(#[serde_as(as = "BoolFromInt<Strict>")] bool);
+
+ is_equal(SStrict(false), expect![[r#"0"#]]);
+ is_equal(SStrict(true), expect![[r#"1"#]]);
+ check_error_deserialization::<SStrict>(
+ "2",
+ expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]],
+ );
+ check_error_deserialization::<SStrict>(
+ "-100",
+ expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]],
+ );
+ check_error_deserialization::<SStrict>(
+ "18446744073709551615",
+ expect![[
+ r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"#
+ ]],
+ );
+ check_error_deserialization::<SStrict>(
+ r#""""#,
+ expect![[r#"invalid type: string "", expected an integer 0 or 1 at line 1 column 2"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SFlexible(#[serde_as(as = "BoolFromInt<Flexible>")] bool);
+
+ is_equal(SFlexible(false), expect![[r#"0"#]]);
+ is_equal(SFlexible(true), expect![[r#"1"#]]);
+ check_deserialization::<SFlexible>(SFlexible(true), "2");
+ check_deserialization::<SFlexible>(SFlexible(true), "-100");
+ check_deserialization::<SFlexible>(SFlexible(true), "18446744073709551615");
+ check_error_deserialization::<SFlexible>(
+ r#""""#,
+ expect![[r#"invalid type: string "", expected an integer at line 1 column 2"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs b/third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs
new file mode 100644
index 0000000000..332dcf676e
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs
@@ -0,0 +1,272 @@
+use super::*;
+use std::net::IpAddr;
+
+#[test]
+fn test_map_as_tuple_list() {
+ let ip = "1.2.3.4".parse().unwrap();
+ let ip2 = "255.255.255.255".parse().unwrap();
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SB(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] BTreeMap<u32, IpAddr>);
+
+ let map: BTreeMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect();
+ is_equal(
+ SB(map.clone()),
+ expect![[r#"
+ [
+ [
+ "1",
+ "1.2.3.4"
+ ],
+ [
+ "10",
+ "1.2.3.4"
+ ],
+ [
+ "200",
+ "255.255.255.255"
+ ]
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SB2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] BTreeMap<u32, IpAddr>);
+
+ is_equal(
+ SB2(map),
+ expect![[r#"
+ [
+ [
+ 1,
+ "1.2.3.4"
+ ],
+ [
+ 10,
+ "1.2.3.4"
+ ],
+ [
+ 200,
+ "255.255.255.255"
+ ]
+ ]"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SH(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] HashMap<u32, IpAddr>);
+
+ // HashMap serialization tests with more than 1 entry are unreliable
+ let map1: HashMap<_, _> = vec![(200, ip2)].into_iter().collect();
+ let map: HashMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect();
+ is_equal(
+ SH(map1.clone()),
+ expect![[r#"
+ [
+ [
+ "200",
+ "255.255.255.255"
+ ]
+ ]"#]],
+ );
+ check_deserialization(
+ SH(map.clone()),
+ r#"[["1","1.2.3.4"],["10","1.2.3.4"],["200","255.255.255.255"]]"#,
+ );
+ check_error_deserialization::<SH>(
+ r#"{"200":"255.255.255.255"}"#,
+ expect![[r#"invalid type: map, expected a sequence at line 1 column 0"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SH2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] HashMap<u32, IpAddr>);
+
+ is_equal(
+ SH2(map1),
+ expect![[r#"
+ [
+ [
+ 200,
+ "255.255.255.255"
+ ]
+ ]"#]],
+ );
+ check_deserialization(
+ SH2(map),
+ r#"[[1,"1.2.3.4"],[10,"1.2.3.4"],[200,"255.255.255.255"]]"#,
+ );
+ check_error_deserialization::<SH2>(
+ r#"1"#,
+ expect![[r#"invalid type: integer `1`, expected a sequence at line 1 column 1"#]],
+ );
+}
+
+#[test]
+fn test_tuple_list_as_map() {
+ let ip = "1.2.3.4".parse().unwrap();
+ let ip2 = "255.255.255.255".parse().unwrap();
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SH(#[serde_as(as = "HashMap<DisplayFromStr, DisplayFromStr>")] Vec<(u32, IpAddr)>);
+
+ is_equal(
+ SH(vec![(1, ip), (10, ip), (200, ip2)]),
+ expect![[r#"
+ {
+ "1": "1.2.3.4",
+ "10": "1.2.3.4",
+ "200": "255.255.255.255"
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SB(#[serde_as(as = "BTreeMap<DisplayFromStr, DisplayFromStr>")] Vec<(u32, IpAddr)>);
+
+ is_equal(
+ SB(vec![(1, ip), (10, ip), (200, ip2)]),
+ expect![[r#"
+ {
+ "1": "1.2.3.4",
+ "10": "1.2.3.4",
+ "200": "255.255.255.255"
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SD(#[serde_as(as = "BTreeMap<DisplayFromStr, DisplayFromStr>")] VecDeque<(u32, IpAddr)>);
+
+ is_equal(
+ SD(vec![(1, ip), (10, ip), (200, ip2)].into()),
+ expect![[r#"
+ {
+ "1": "1.2.3.4",
+ "10": "1.2.3.4",
+ "200": "255.255.255.255"
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Sll(
+ #[serde_as(as = "HashMap<DisplayFromStr, DisplayFromStr>")] LinkedList<(u32, IpAddr)>,
+ );
+
+ is_equal(
+ Sll(vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect()),
+ expect![[r#"
+ {
+ "1": "1.2.3.4",
+ "10": "1.2.3.4",
+ "200": "255.255.255.255"
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct SO(#[serde_as(as = "HashMap<DisplayFromStr, DisplayFromStr>")] Option<(u32, IpAddr)>);
+
+ is_equal(
+ SO(Some((1, ip))),
+ expect![[r#"
+ {
+ "1": "1.2.3.4"
+ }"#]],
+ );
+ is_equal(SO(None), expect![[r#"{}"#]]);
+}
+
+#[test]
+fn test_tuple_array_as_map() {
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct S1(#[serde_as(as = "BTreeMap<_, _>")] [(u8, u8); 1]);
+ is_equal(
+ S1([(1, 2)]),
+ expect![[r#"
+ {
+ "1": 2
+ }"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct S2(#[serde_as(as = "HashMap<_, _>")] [(u8, u8); 33]);
+ is_equal(
+ S2([
+ (0, 0),
+ (1, 1),
+ (2, 2),
+ (3, 3),
+ (4, 4),
+ (5, 5),
+ (6, 6),
+ (7, 7),
+ (8, 8),
+ (9, 9),
+ (10, 10),
+ (11, 11),
+ (12, 12),
+ (13, 13),
+ (14, 14),
+ (15, 15),
+ (16, 16),
+ (17, 17),
+ (18, 18),
+ (19, 19),
+ (20, 20),
+ (21, 21),
+ (22, 22),
+ (23, 23),
+ (24, 24),
+ (25, 25),
+ (26, 26),
+ (27, 27),
+ (28, 28),
+ (29, 29),
+ (30, 30),
+ (31, 31),
+ (32, 32),
+ ]),
+ expect![[r#"
+ {
+ "0": 0,
+ "1": 1,
+ "2": 2,
+ "3": 3,
+ "4": 4,
+ "5": 5,
+ "6": 6,
+ "7": 7,
+ "8": 8,
+ "9": 9,
+ "10": 10,
+ "11": 11,
+ "12": 12,
+ "13": 13,
+ "14": 14,
+ "15": 15,
+ "16": 16,
+ "17": 17,
+ "18": 18,
+ "19": 19,
+ "20": 20,
+ "21": 21,
+ "22": 22,
+ "23": 23,
+ "24": 24,
+ "25": 25,
+ "26": 26,
+ "27": 27,
+ "28": 28,
+ "29": 29,
+ "30": 30,
+ "31": 31,
+ "32": 32
+ }"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/pickfirst.rs b/third_party/rust/serde_with/tests/serde_as/pickfirst.rs
new file mode 100644
index 0000000000..ed3cacb1dd
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/pickfirst.rs
@@ -0,0 +1,134 @@
+use super::*;
+use serde_with::{CommaSeparator, PickFirst, SpaceSeparator, StringWithSeparator};
+
+#[test]
+fn test_pick_first_two() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] u32);
+
+ is_equal(S(123), expect![[r#"123"#]]);
+ check_deserialization(S(123), r#""123""#);
+ check_error_deserialization::<S>(
+ r#""Abc""#,
+ expect![[r#"PickFirst could not deserialize data"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(#[serde_as(as = "PickFirst<(DisplayFromStr, _)>")] u32);
+
+ is_equal(S2(123), expect![[r#""123""#]]);
+ check_deserialization(S2(123), r#"123"#);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S3(
+ #[serde_as(as = "PickFirst<(_, StringWithSeparator::<SpaceSeparator, String>,)>")]
+ Vec<String>,
+ );
+ is_equal(
+ S3(vec!["A".to_string(), "B".to_string(), "C".to_string()]),
+ expect![[r#"
+ [
+ "A",
+ "B",
+ "C"
+ ]"#]],
+ );
+ check_deserialization(
+ S3(vec!["A".to_string(), "B".to_string(), "C".to_string()]),
+ r#""A B C""#,
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S4(
+ #[serde_as(as = "PickFirst<(StringWithSeparator::<CommaSeparator, String>, _,)>")]
+ Vec<String>,
+ );
+ is_equal(
+ S4(vec!["A".to_string(), "B".to_string(), "C".to_string()]),
+ expect![[r#""A,B,C""#]],
+ );
+ check_deserialization(
+ S4(vec!["A".to_string(), "B".to_string(), "C".to_string()]),
+ r#"["A", "B", "C"]"#,
+ );
+}
+
+#[test]
+fn test_pick_first_three() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(
+ #[serde_as(
+ as = "PickFirst<(_, Vec<DisplayFromStr>, StringWithSeparator::<CommaSeparator, u32>)>"
+ )]
+ Vec<u32>,
+ );
+ is_equal(
+ S(vec![1, 2, 3]),
+ expect![[r#"
+ [
+ 1,
+ 2,
+ 3
+ ]"#]],
+ );
+ check_deserialization(
+ S(vec![1, 2, 3]),
+ r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#,
+ );
+ check_deserialization(S(vec![1, 2, 3]), r#""1,2,3""#);
+ check_error_deserialization::<S>(
+ r#""Abc""#,
+ expect![[r#"PickFirst could not deserialize data"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S2(
+ #[serde_as(
+ as = "PickFirst<(StringWithSeparator::<CommaSeparator, u32>, _, Vec<DisplayFromStr>)>"
+ )]
+ Vec<u32>,
+ );
+ is_equal(S2(vec![1, 2, 3]), expect![[r#""1,2,3""#]]);
+ check_deserialization(
+ S2(vec![1, 2, 3]),
+ r#"
+ [
+ "1",
+ "2",
+ "3"
+ ]"#,
+ );
+ check_deserialization(
+ S2(vec![1, 2, 3]),
+ r#"
+ [
+ 1,
+ 2,
+ 3
+ ]"#,
+ );
+}
+
+#[test]
+fn test_pick_first_four() {
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = "PickFirst<(_, _, _, _)>")] u32);
+
+ is_equal(S(123), expect![[r#"123"#]]);
+ check_error_deserialization::<S>(
+ r#""Abc""#,
+ expect![[r#"PickFirst could not deserialize data"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs b/third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs
new file mode 100644
index 0000000000..b180079d46
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs
@@ -0,0 +1,191 @@
+use super::*;
+
+/// Test that the [`serde_as`] macro can replace the `_` type and the resulting code compiles.
+#[test]
+fn test_serde_as_macro_replace_infer_type() {
+ #[serde_as]
+ #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+ struct Data {
+ #[serde_as(as = "_")]
+ a: u32,
+ #[serde_as(as = "std::vec::Vec<_>")]
+ b: Vec<u32>,
+ #[serde_as(as = "Vec<(_, _)>")]
+ c: Vec<(u32, String)>,
+ #[serde_as(as = "[_; 2]")]
+ d: [u32; 2],
+ #[serde_as(as = "Box<[_]>")]
+ e: Box<[u32]>,
+ }
+
+ is_equal(
+ Data {
+ a: 10,
+ b: vec![20, 33],
+ c: vec![(40, "Hello".into()), (55, "World".into()), (60, "!".into())],
+ d: [70, 88],
+ e: vec![99, 100, 110].into_boxed_slice(),
+ },
+ expect![[r#"
+ {
+ "a": 10,
+ "b": [
+ 20,
+ 33
+ ],
+ "c": [
+ [
+ 40,
+ "Hello"
+ ],
+ [
+ 55,
+ "World"
+ ],
+ [
+ 60,
+ "!"
+ ]
+ ],
+ "d": [
+ 70,
+ 88
+ ],
+ "e": [
+ 99,
+ 100,
+ 110
+ ]
+ }"#]],
+ );
+}
+
+/// Test that the [`serde_as`] macro supports `deserialize_as`
+#[test]
+fn test_serde_as_macro_deserialize() {
+ #[serde_as]
+ #[derive(Debug, Eq, PartialEq, Deserialize)]
+ struct Data {
+ #[serde_as(deserialize_as = "DisplayFromStr")]
+ a: u32,
+ #[serde_as(deserialize_as = "Vec<DisplayFromStr>")]
+ b: Vec<u32>,
+ #[serde_as(deserialize_as = "(DisplayFromStr, _)")]
+ c: (u32, u32),
+ }
+
+ check_deserialization(
+ Data {
+ a: 10,
+ b: vec![20, 33],
+ c: (40, 55),
+ },
+ r##"{
+ "a": "10",
+ "b": [
+ "20",
+ "33"
+ ],
+ "c": [
+ "40",
+ 55
+ ]
+}"##,
+ );
+}
+
+/// Test that the [`serde_as`] macro supports `serialize_as`
+#[test]
+fn test_serde_as_macro_serialize() {
+ #[serde_as]
+ #[derive(Debug, Eq, PartialEq, Serialize)]
+ struct Data {
+ #[serde_as(serialize_as = "DisplayFromStr")]
+ a: u32,
+ #[serde_as(serialize_as = "Vec<DisplayFromStr>")]
+ b: Vec<u32>,
+ #[serde_as(serialize_as = "(DisplayFromStr, _)")]
+ c: (u32, u32),
+ }
+
+ check_serialization(
+ Data {
+ a: 10,
+ b: vec![20, 33],
+ c: (40, 55),
+ },
+ expect![[r#"
+ {
+ "a": "10",
+ "b": [
+ "20",
+ "33"
+ ],
+ "c": [
+ "40",
+ 55
+ ]
+ }"#]],
+ );
+}
+
+/// Test that the [`serde_as`] macro supports `serialize_as` and `deserialize_as`
+#[test]
+fn test_serde_as_macro_serialize_deserialize() {
+ #[serde_as]
+ #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+ struct Data {
+ #[serde_as(serialize_as = "DisplayFromStr", deserialize_as = "DisplayFromStr")]
+ a: u32,
+ #[serde_as(
+ serialize_as = "Vec<DisplayFromStr>",
+ deserialize_as = "Vec<DisplayFromStr>"
+ )]
+ b: Vec<u32>,
+ #[serde_as(
+ serialize_as = "(DisplayFromStr, _)",
+ deserialize_as = "(DisplayFromStr, _)"
+ )]
+ c: (u32, u32),
+ }
+
+ is_equal(
+ Data {
+ a: 10,
+ b: vec![20, 33],
+ c: (40, 55),
+ },
+ expect![[r#"
+ {
+ "a": "10",
+ "b": [
+ "20",
+ "33"
+ ],
+ "c": [
+ "40",
+ 55
+ ]
+ }"#]],
+ );
+}
+
+/// Test that the [`serde_as`] macro works correctly if applied multiple times to a field
+#[test]
+fn test_serde_as_macro_multiple_field_attributes() {
+ #[serde_as]
+ #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+ struct Data {
+ #[serde_as(serialize_as = "DisplayFromStr")]
+ #[serde_as(deserialize_as = "DisplayFromStr")]
+ a: u32,
+ }
+
+ is_equal(
+ Data { a: 10 },
+ expect![[r#"
+ {
+ "a": "10"
+ }"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/serde_conv.rs b/third_party/rust/serde_with/tests/serde_as/serde_conv.rs
new file mode 100644
index 0000000000..1e510f72a3
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/serde_conv.rs
@@ -0,0 +1,52 @@
+use super::*;
+use serde_with::serde_conv;
+
+#[test]
+fn test_bool_as_string() {
+ serde_conv!(BoolAsString, bool, |x: &bool| x.to_string(), |x: String| x
+ .parse());
+
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct SWith(#[serde(with = "BoolAsString")] bool);
+
+ is_equal(SWith(false), expect![[r#""false""#]]);
+ is_equal(SWith(true), expect![[r#""true""#]]);
+ check_error_deserialization::<SWith>(
+ "123",
+ expect![[r#"invalid type: integer `123`, expected a string at line 1 column 3"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct SAs(#[serde_as(as = "BoolAsString")] bool);
+
+ is_equal(SAs(false), expect![[r#""false""#]]);
+ is_equal(SAs(true), expect![[r#""true""#]]);
+ check_error_deserialization::<SAs>(
+ "123",
+ expect![[r#"invalid type: integer `123`, expected a string at line 1 column 3"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
+ struct SAsVec(#[serde_as(as = "Vec<BoolAsString>")] Vec<bool>);
+
+ is_equal(
+ SAsVec(vec![false]),
+ expect![[r#"
+ [
+ "false"
+ ]"#]],
+ );
+ is_equal(
+ SAsVec(vec![true]),
+ expect![[r#"
+ [
+ "true"
+ ]"#]],
+ );
+ check_error_deserialization::<SAsVec>(
+ "123",
+ expect![[r#"invalid type: integer `123`, expected a sequence at line 1 column 3"#]],
+ );
+}
diff --git a/third_party/rust/serde_with/tests/serde_as/time.rs b/third_party/rust/serde_with/tests/serde_as/time.rs
new file mode 100644
index 0000000000..91808b4d8c
--- /dev/null
+++ b/third_party/rust/serde_with/tests/serde_as/time.rs
@@ -0,0 +1,521 @@
+use super::*;
+use core::time::Duration;
+use serde_with::{
+ DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds,
+ DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac,
+ DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac,
+ TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds,
+ TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac,
+};
+use std::time::SystemTime;
+
+#[test]
+fn test_duration_seconds() {
+ let zero = Duration::new(0, 0);
+ let one_second = Duration::new(1, 0);
+ let half_second = Duration::new(0, 500_000_000);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct IntStrict(#[serde_as(as = "DurationSeconds")] Duration);
+
+ is_equal(IntStrict(zero), expect![[r#"0"#]]);
+ is_equal(IntStrict(one_second), expect![[r#"1"#]]);
+ check_serialization(IntStrict(half_second), expect![[r#"1"#]]);
+ check_error_deserialization::<IntStrict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected u64 at line 1 column 3"#]],
+ );
+ check_error_deserialization::<IntStrict>(
+ r#"-1"#,
+ expect![[r#"invalid value: integer `-1`, expected u64 at line 1 column 2"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct IntFlexible(#[serde_as(as = "DurationSeconds<u64, Flexible>")] Duration);
+
+ is_equal(IntFlexible(zero), expect![[r#"0"#]]);
+ is_equal(IntFlexible(one_second), expect![[r#"1"#]]);
+ check_serialization(IntFlexible(half_second), expect![[r#"1"#]]);
+ check_deserialization(IntFlexible(half_second), r#""0.5""#);
+ check_deserialization(IntFlexible(one_second), r#""1""#);
+ check_deserialization(IntFlexible(zero), r#""0""#);
+ check_error_deserialization::<IntFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+ check_error_deserialization::<IntFlexible>(
+ r#"-1"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct F64Strict(#[serde_as(as = "DurationSeconds<f64>")] Duration);
+
+ is_equal(F64Strict(zero), expect![[r#"0.0"#]]);
+ is_equal(F64Strict(one_second), expect![[r#"1.0"#]]);
+ check_serialization(F64Strict(half_second), expect![[r#"1.0"#]]);
+ check_deserialization(F64Strict(one_second), r#"0.5"#);
+ check_error_deserialization::<F64Strict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]],
+ );
+ check_error_deserialization::<F64Strict>(
+ r#"-1.0"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct F64Flexible(#[serde_as(as = "DurationSeconds<f64, Flexible>")] Duration);
+
+ is_equal(F64Flexible(zero), expect![[r#"0.0"#]]);
+ is_equal(F64Flexible(one_second), expect![[r#"1.0"#]]);
+ check_serialization(F64Flexible(half_second), expect![[r#"1.0"#]]);
+ check_deserialization(F64Flexible(half_second), r#""0.5""#);
+ check_deserialization(F64Flexible(one_second), r#""1""#);
+ check_deserialization(F64Flexible(zero), r#""0""#);
+ check_error_deserialization::<F64Flexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+ check_error_deserialization::<F64Flexible>(
+ r#"-1"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StringStrict(#[serde_as(as = "DurationSeconds<String>")] Duration);
+
+ is_equal(StringStrict(zero), expect![[r#""0""#]]);
+ is_equal(StringStrict(one_second), expect![[r#""1""#]]);
+ check_serialization(StringStrict(half_second), expect![[r#""1""#]]);
+ check_error_deserialization::<StringStrict>(
+ r#"1"#,
+ expect![[
+ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"#
+ ]],
+ );
+ check_error_deserialization::<StringStrict>(
+ r#"-1"#,
+ expect![[
+ r#"invalid type: integer `-1`, expected a string containing a number at line 1 column 2"#
+ ]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StringFlexible(#[serde_as(as = "DurationSeconds<String, Flexible>")] Duration);
+
+ is_equal(StringFlexible(zero), expect![[r#""0""#]]);
+ is_equal(StringFlexible(one_second), expect![[r#""1""#]]);
+ check_serialization(StringFlexible(half_second), expect![[r#""1""#]]);
+ check_deserialization(StringFlexible(half_second), r#""0.5""#);
+ check_deserialization(StringFlexible(one_second), r#""1""#);
+ check_deserialization(StringFlexible(zero), r#""0""#);
+ check_error_deserialization::<StringFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+ check_error_deserialization::<StringFlexible>(
+ r#"-1"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+}
+
+#[test]
+fn test_duration_seconds_with_frac() {
+ let zero = Duration::new(0, 0);
+ let one_second = Duration::new(1, 0);
+ let half_second = Duration::new(0, 500_000_000);
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct F64Strict(#[serde_as(as = "DurationSecondsWithFrac<f64>")] Duration);
+
+ is_equal(F64Strict(zero), expect![[r#"0.0"#]]);
+ is_equal(F64Strict(one_second), expect![[r#"1.0"#]]);
+ is_equal(F64Strict(half_second), expect![[r#"0.5"#]]);
+ check_error_deserialization::<F64Strict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]],
+ );
+ check_error_deserialization::<F64Strict>(
+ r#"-1.0"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct F64Flexible(#[serde_as(as = "DurationSecondsWithFrac<f64, Flexible>")] Duration);
+
+ is_equal(F64Flexible(zero), expect![[r#"0.0"#]]);
+ is_equal(F64Flexible(one_second), expect![[r#"1.0"#]]);
+ is_equal(F64Flexible(half_second), expect![[r#"0.5"#]]);
+ check_deserialization(F64Flexible(one_second), r#""1""#);
+ check_deserialization(F64Flexible(zero), r#""0""#);
+ check_error_deserialization::<F64Flexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+ check_error_deserialization::<F64Flexible>(
+ r#"-1"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StringStrict(#[serde_as(as = "DurationSecondsWithFrac<String>")] Duration);
+
+ is_equal(StringStrict(zero), expect![[r#""0""#]]);
+ is_equal(StringStrict(one_second), expect![[r#""1""#]]);
+ is_equal(StringStrict(half_second), expect![[r#""0.5""#]]);
+ check_error_deserialization::<StringStrict>(
+ r#"1"#,
+ expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]],
+ );
+ check_error_deserialization::<StringStrict>(
+ r#"-1"#,
+ expect![[r#"invalid type: integer `-1`, expected a string at line 1 column 2"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StringFlexible(#[serde_as(as = "DurationSecondsWithFrac<String, Flexible>")] Duration);
+
+ is_equal(StringFlexible(zero), expect![[r#""0""#]]);
+ is_equal(StringFlexible(one_second), expect![[r#""1""#]]);
+ is_equal(StringFlexible(half_second), expect![[r#""0.5""#]]);
+ check_deserialization(StringFlexible(zero), r#""0""#);
+ check_error_deserialization::<StringFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+ check_error_deserialization::<StringFlexible>(
+ r#"-1"#,
+ expect![[r#"std::time::Duration cannot be negative"#]],
+ );
+}
+
+#[test]
+fn test_timestamp_seconds_systemtime() {
+ let zero = SystemTime::UNIX_EPOCH;
+ let one_second = SystemTime::UNIX_EPOCH
+ .checked_add(Duration::new(1, 0))
+ .unwrap();
+ let half_second = SystemTime::UNIX_EPOCH
+ .checked_add(Duration::new(0, 500_000_000))
+ .unwrap();
+ let minus_one_second = SystemTime::UNIX_EPOCH
+ .checked_sub(Duration::new(1, 0))
+ .unwrap();
+ let minus_half_second = SystemTime::UNIX_EPOCH
+ .checked_sub(Duration::new(0, 500_000_000))
+ .unwrap();
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructIntStrict(#[serde_as(as = "TimestampSeconds")] SystemTime);
+
+ is_equal(StructIntStrict(zero), expect![[r#"0"#]]);
+ is_equal(StructIntStrict(one_second), expect![[r#"1"#]]);
+ is_equal(StructIntStrict(minus_one_second), expect![[r#"-1"#]]);
+ check_serialization(StructIntStrict(half_second), expect![[r#"1"#]]);
+ check_serialization(StructIntStrict(minus_half_second), expect![[r#"-1"#]]);
+ check_error_deserialization::<StructIntStrict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]],
+ );
+ check_error_deserialization::<StructIntStrict>(
+ r#"0.123"#,
+ expect![[r#"invalid type: floating point `0.123`, expected i64 at line 1 column 5"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructIntFlexible(#[serde_as(as = "TimestampSeconds<i64, Flexible>")] SystemTime);
+
+ is_equal(StructIntFlexible(zero), expect![[r#"0"#]]);
+ is_equal(StructIntFlexible(one_second), expect![[r#"1"#]]);
+ is_equal(StructIntFlexible(minus_one_second), expect![[r#"-1"#]]);
+ check_serialization(StructIntFlexible(half_second), expect![[r#"1"#]]);
+ check_serialization(StructIntFlexible(minus_half_second), expect![[r#"-1"#]]);
+ check_deserialization(StructIntFlexible(one_second), r#""1""#);
+ check_deserialization(StructIntFlexible(one_second), r#"1.0"#);
+ check_deserialization(StructIntFlexible(minus_half_second), r#""-0.5""#);
+ check_deserialization(StructIntFlexible(half_second), r#"0.5"#);
+ check_error_deserialization::<StructIntFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Structf64Strict(#[serde_as(as = "TimestampSeconds<f64>")] SystemTime);
+
+ is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]);
+ is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]);
+ is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]);
+ check_serialization(Structf64Strict(half_second), expect![[r#"1.0"#]]);
+ check_serialization(Structf64Strict(minus_half_second), expect![[r#"-1.0"#]]);
+ check_deserialization(Structf64Strict(one_second), r#"0.5"#);
+ check_error_deserialization::<Structf64Strict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Structf64Flexible(#[serde_as(as = "TimestampSeconds<f64, Flexible>")] SystemTime);
+
+ is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]);
+ is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]);
+ is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]);
+ check_serialization(Structf64Flexible(half_second), expect![[r#"1.0"#]]);
+ check_serialization(Structf64Flexible(minus_half_second), expect![[r#"-1.0"#]]);
+ check_deserialization(Structf64Flexible(one_second), r#""1""#);
+ check_deserialization(Structf64Flexible(one_second), r#"1.0"#);
+ check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#);
+ check_deserialization(Structf64Flexible(half_second), r#"0.5"#);
+ check_error_deserialization::<Structf64Flexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructStringStrict(#[serde_as(as = "TimestampSeconds<String>")] SystemTime);
+
+ is_equal(StructStringStrict(zero), expect![[r#""0""#]]);
+ is_equal(StructStringStrict(one_second), expect![[r#""1""#]]);
+ is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]);
+ check_serialization(StructStringStrict(half_second), expect![[r#""1""#]]);
+ check_serialization(StructStringStrict(minus_half_second), expect![[r#""-1""#]]);
+ check_deserialization(StructStringStrict(one_second), r#""1""#);
+ check_error_deserialization::<StructStringStrict>(
+ r#""0.5""#,
+ expect![[r#"invalid digit found in string at line 1 column 5"#]],
+ );
+ check_error_deserialization::<StructStringStrict>(
+ r#""-0.5""#,
+ expect![[r#"invalid digit found in string at line 1 column 6"#]],
+ );
+ check_error_deserialization::<StructStringStrict>(
+ r#"1"#,
+ expect![[
+ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"#
+ ]],
+ );
+ check_error_deserialization::<StructStringStrict>(
+ r#"0.0"#,
+ expect![[
+ r#"invalid type: floating point `0`, expected a string containing a number at line 1 column 3"#
+ ]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructStringFlexible(#[serde_as(as = "TimestampSeconds<String, Flexible>")] SystemTime);
+
+ is_equal(StructStringFlexible(zero), expect![[r#""0""#]]);
+ is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]);
+ is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]);
+ check_serialization(StructStringFlexible(half_second), expect![[r#""1""#]]);
+ check_serialization(
+ StructStringFlexible(minus_half_second),
+ expect![[r#""-1""#]],
+ );
+ check_deserialization(StructStringFlexible(one_second), r#"1"#);
+ check_deserialization(StructStringFlexible(one_second), r#"1.0"#);
+ check_deserialization(StructStringFlexible(minus_half_second), r#""-0.5""#);
+ check_deserialization(StructStringFlexible(half_second), r#"0.5"#);
+ check_error_deserialization::<StructStringFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+}
+
+#[test]
+fn test_timestamp_seconds_with_frac_systemtime() {
+ let zero = SystemTime::UNIX_EPOCH;
+ let one_second = SystemTime::UNIX_EPOCH
+ .checked_add(Duration::new(1, 0))
+ .unwrap();
+ let half_second = SystemTime::UNIX_EPOCH
+ .checked_add(Duration::new(0, 500_000_000))
+ .unwrap();
+ let minus_one_second = SystemTime::UNIX_EPOCH
+ .checked_sub(Duration::new(1, 0))
+ .unwrap();
+ let minus_half_second = SystemTime::UNIX_EPOCH
+ .checked_sub(Duration::new(0, 500_000_000))
+ .unwrap();
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Structf64Strict(#[serde_as(as = "TimestampSecondsWithFrac<f64>")] SystemTime);
+
+ is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]);
+ is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]);
+ is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]);
+ is_equal(Structf64Strict(half_second), expect![[r#"0.5"#]]);
+ is_equal(Structf64Strict(minus_half_second), expect![[r#"-0.5"#]]);
+ check_error_deserialization::<Structf64Strict>(
+ r#""1""#,
+ expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct Structf64Flexible(
+ #[serde_as(as = "TimestampSecondsWithFrac<f64, Flexible>")] SystemTime,
+ );
+
+ is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]);
+ is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]);
+ is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]);
+ is_equal(Structf64Flexible(half_second), expect![[r#"0.5"#]]);
+ is_equal(Structf64Flexible(minus_half_second), expect![[r#"-0.5"#]]);
+ check_deserialization(Structf64Flexible(one_second), r#""1""#);
+ check_deserialization(Structf64Flexible(one_second), r#"1.0"#);
+ check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#);
+ check_deserialization(Structf64Flexible(half_second), r#"0.5"#);
+ check_error_deserialization::<Structf64Flexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructStringStrict(#[serde_as(as = "TimestampSecondsWithFrac<String>")] SystemTime);
+
+ is_equal(StructStringStrict(zero), expect![[r#""0""#]]);
+ is_equal(StructStringStrict(one_second), expect![[r#""1""#]]);
+ is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]);
+ is_equal(StructStringStrict(half_second), expect![[r#""0.5""#]]);
+ is_equal(
+ StructStringStrict(minus_half_second),
+ expect![[r#""-0.5""#]],
+ );
+ check_error_deserialization::<StructStringStrict>(
+ r#"1"#,
+ expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]],
+ );
+ check_error_deserialization::<StructStringStrict>(
+ r#"0.0"#,
+ expect![[r#"invalid type: floating point `0`, expected a string at line 1 column 3"#]],
+ );
+
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct StructStringFlexible(
+ #[serde_as(as = "TimestampSecondsWithFrac<String, Flexible>")] SystemTime,
+ );
+
+ is_equal(StructStringFlexible(zero), expect![[r#""0""#]]);
+ is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]);
+ is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]);
+ is_equal(StructStringFlexible(half_second), expect![[r#""0.5""#]]);
+ is_equal(
+ StructStringFlexible(minus_half_second),
+ expect![[r#""-0.5""#]],
+ );
+ check_deserialization(StructStringFlexible(one_second), r#"1"#);
+ check_deserialization(StructStringFlexible(one_second), r#"1.0"#);
+ check_deserialization(StructStringFlexible(half_second), r#"0.5"#);
+ check_error_deserialization::<StructStringFlexible>(
+ r#""a""#,
+ expect![[
+ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"#
+ ]],
+ );
+}
+
+macro_rules! smoketest {
+ ($($valuety:ty, $adapter:literal, $value:ident, $expect:tt;)*) => {
+ $({
+ #[serde_as]
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
+ struct S(#[serde_as(as = $adapter)] $valuety);
+ #[allow(unused_braces)]
+ is_equal(S($value), $expect);
+ })*
+ };
+}
+
+#[test]
+fn test_duration_smoketest() {
+ let one_second = Duration::new(1, 0);
+
+ smoketest! {
+ Duration, "DurationSeconds<u64>", one_second, {expect![[r#"1"#]]};
+ Duration, "DurationSeconds<f64>", one_second, {expect![[r#"1.0"#]]};
+ Duration, "DurationMilliSeconds<u64>", one_second, {expect![[r#"1000"#]]};
+ Duration, "DurationMilliSeconds<f64>", one_second, {expect![[r#"1000.0"#]]};
+ Duration, "DurationMicroSeconds<u64>", one_second, {expect![[r#"1000000"#]]};
+ Duration, "DurationMicroSeconds<f64>", one_second, {expect![[r#"1000000.0"#]]};
+ Duration, "DurationNanoSeconds<u64>", one_second, {expect![[r#"1000000000"#]]};
+ Duration, "DurationNanoSeconds<f64>", one_second, {expect![[r#"1000000000.0"#]]};
+ };
+
+ smoketest! {
+ Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]};
+ Duration, "DurationSecondsWithFrac<String>", one_second, {expect![[r#""1""#]]};
+ Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]};
+ Duration, "DurationMilliSecondsWithFrac<String>", one_second, {expect![[r#""1000""#]]};
+ Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]};
+ Duration, "DurationMicroSecondsWithFrac<String>", one_second, {expect![[r#""1000000""#]]};
+ Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]};
+ Duration, "DurationNanoSecondsWithFrac<String>", one_second, {expect![[r#""1000000000""#]]};
+ };
+}
+
+#[test]
+fn test_timestamp_systemtime_smoketest() {
+ let one_second = SystemTime::UNIX_EPOCH
+ .checked_add(Duration::new(1, 0))
+ .unwrap();
+
+ smoketest! {
+ SystemTime, "TimestampSeconds<i64>", one_second, {expect![[r#"1"#]]};
+ SystemTime, "TimestampSeconds<f64>", one_second, {expect![[r#"1.0"#]]};
+ SystemTime, "TimestampMilliSeconds<i64>", one_second, {expect![[r#"1000"#]]};
+ SystemTime, "TimestampMilliSeconds<f64>", one_second, {expect![[r#"1000.0"#]]};
+ SystemTime, "TimestampMicroSeconds<i64>", one_second, {expect![[r#"1000000"#]]};
+ SystemTime, "TimestampMicroSeconds<f64>", one_second, {expect![[r#"1000000.0"#]]};
+ SystemTime, "TimestampNanoSeconds<i64>", one_second, {expect![[r#"1000000000"#]]};
+ SystemTime, "TimestampNanoSeconds<f64>", one_second, {expect![[r#"1000000000.0"#]]};
+ };
+
+ smoketest! {
+ SystemTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]};
+ SystemTime, "TimestampSecondsWithFrac<String>", one_second, {expect![[r#""1""#]]};
+ SystemTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]};
+ SystemTime, "TimestampMilliSecondsWithFrac<String>", one_second, {expect![[r#""1000""#]]};
+ SystemTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]};
+ SystemTime, "TimestampMicroSecondsWithFrac<String>", one_second, {expect![[r#""1000000""#]]};
+ SystemTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]};
+ SystemTime, "TimestampNanoSecondsWithFrac<String>", one_second, {expect![[r#""1000000000""#]]};
+ };
+}