From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/serde_with/tests/base64.rs | 144 +++ third_party/rust/serde_with/tests/chrono_0_4.rs | 742 ++++++++++++ .../tests/derives/deserialize_fromstr.rs | 96 ++ third_party/rust/serde_with/tests/derives/lib.rs | 15 + .../serde_with/tests/derives/serialize_display.rs | 39 + third_party/rust/serde_with/tests/hex.rs | 93 ++ third_party/rust/serde_with/tests/indexmap_1.rs | 262 +++++ third_party/rust/serde_with/tests/json.rs | 62 + third_party/rust/serde_with/tests/rust.rs | 386 +++++++ .../rust/serde_with/tests/serde_as/collections.rs | 44 + .../rust/serde_with/tests/serde_as/default_on.rs | 130 +++ .../rust/serde_with/tests/serde_as/enum_map.rs | 458 ++++++++ .../rust/serde_with/tests/serde_as/frominto.rs | 187 ++++ .../serde_with/tests/serde_as/key_value_map.rs | 350 ++++++ third_party/rust/serde_with/tests/serde_as/lib.rs | 1181 ++++++++++++++++++++ .../serde_with/tests/serde_as/map_tuple_list.rs | 687 ++++++++++++ .../rust/serde_with/tests/serde_as/pickfirst.rs | 149 +++ .../serde_with/tests/serde_as/serde_as_macro.rs | 307 +++++ .../rust/serde_with/tests/serde_as/serde_conv.rs | 52 + third_party/rust/serde_with/tests/serde_as/time.rs | 521 +++++++++ third_party/rust/serde_with/tests/time_0_3.rs | 279 +++++ third_party/rust/serde_with/tests/utils.rs | 79 ++ .../rust/serde_with/tests/version_numbers.rs | 79 ++ third_party/rust/serde_with/tests/with_prefix.rs | 164 +++ 24 files changed, 6506 insertions(+) create mode 100644 third_party/rust/serde_with/tests/base64.rs create mode 100644 third_party/rust/serde_with/tests/chrono_0_4.rs create mode 100644 third_party/rust/serde_with/tests/derives/deserialize_fromstr.rs create mode 100644 third_party/rust/serde_with/tests/derives/lib.rs create mode 100644 third_party/rust/serde_with/tests/derives/serialize_display.rs create mode 100644 third_party/rust/serde_with/tests/hex.rs create mode 100644 third_party/rust/serde_with/tests/indexmap_1.rs create mode 100644 third_party/rust/serde_with/tests/json.rs create mode 100644 third_party/rust/serde_with/tests/rust.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/collections.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/default_on.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/enum_map.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/frominto.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/key_value_map.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/lib.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/pickfirst.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/serde_conv.rs create mode 100644 third_party/rust/serde_with/tests/serde_as/time.rs create mode 100644 third_party/rust/serde_with/tests/time_0_3.rs create mode 100644 third_party/rust/serde_with/tests/utils.rs create mode 100644 third_party/rust/serde_with/tests/version_numbers.rs create mode 100644 third_party/rust/serde_with/tests/with_prefix.rs (limited to 'third_party/rust/serde_with/tests') diff --git a/third_party/rust/serde_with/tests/base64.rs b/third_party/rust/serde_with/tests/base64.rs new file mode 100644 index 0000000000..881ee0d2d2 --- /dev/null +++ b/third_party/rust/serde_with/tests/base64.rs @@ -0,0 +1,144 @@ +#![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, + // This allows the tests to be written more uniform and not have to special case the last clone(). + clippy::redundant_clone, +)] + +mod utils; + +use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::{ + base64::{Base64, Bcrypt, BinHex, Crypt, ImapMutf7, Standard, UrlSafe}, + formats::{Padded, Unpadded}, + serde_as, +}; + +#[test] +fn base64_vec() { + let check_equal = vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]; + let check_deser = vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d], vec![0xe0, 0x7d]]; + let check_deser_from = r#"["qrz/","4H0=","4H0"]"#; + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct BDefault(#[serde_as(as = "Vec")] Vec>); + + is_equal( + BDefault(check_equal.clone()), + expect![[r#" + [ + "AAECDQ==", + "DgUGBw==" + ]"#]], + ); + + // Check mixed padding deserialization + check_deserialization(BDefault(check_deser.clone()), check_deser_from); + + check_error_deserialization::( + r#"["0"]"#, + expect!["Encoded text cannot have a 6-bit remainder. at line 1 column 4"], + ); + check_error_deserialization::( + r#"["zz"]"#, + expect!["Invalid last symbol 122, offset 1. at line 1 column 5"], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct BPadded(#[serde_as(as = "Vec>")] Vec>); + + is_equal( + BPadded(check_equal.clone()), + expect![[r#" + [ + "AAECDQ==", + "DgUGBw==" + ]"#]], + ); + check_deserialization(BPadded(check_deser.clone()), check_deser_from); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct BUnpadded(#[serde_as(as = "Vec>")] Vec>); + + is_equal( + BUnpadded(check_equal.clone()), + expect![[r#" + [ + "AAECDQ", + "DgUGBw" + ]"#]], + ); + check_deserialization(BUnpadded(check_deser.clone()), check_deser_from); +} + +#[test] +fn base64_different_charsets() { + let bytes = [ + 0x69_u8, 0xb7, 0x1d, 0x79, 0xf8, 0x21, 0x8a, 0x39, 0x25, 0x9a, 0x7a, 0x29, 0xaa, 0xbb, + 0x2d, 0xba, 0xfc, 0x31, 0xcb, 0x30, 0x01, 0x08, 0x31, 0x05, 0x18, 0x72, 0x09, 0x28, 0xb3, + 0x0d, 0x38, 0xf4, 0x11, 0x49, 0x35, 0x15, 0x59, 0x76, 0x19, 0xd3, 0x5d, 0xb7, 0xe3, 0x9e, + 0xbb, 0xf3, 0xdf, 0xbf, 0x00, + ]; + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64Standard(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64Standard(bytes.to_vec()), + expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/AA==""#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64UrlSafe(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64UrlSafe(bytes.to_vec()), + expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_AA==""#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64Crypt(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64Crypt(bytes.to_vec()), + expect![[r#""OPQRSTUVWXYZabcdefghijklmn./0123456789ABCDEFGHIJKLMNopqrstuvwxyz..==""#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64Bcrypt(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64Bcrypt(bytes.to_vec()), + expect![[r#""YZabcdefghijklmnopqrstuvwx./ABCDEFGHIJKLMNOPQRSTUVWXyz0123456789..==""#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64ImapMutf7(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64ImapMutf7(bytes.to_vec()), + expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+,AA==""#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B64BinHex(#[serde_as(as = "Base64")] Vec); + + is_equal( + B64BinHex(bytes.to_vec()), + expect![[r##""CDEFGHIJKLMNPQRSTUVXYZ[`ab!\"#$%&'()*+,-0123456789@ABcdehijklmpqr!!==""##]], + ); +} diff --git a/third_party/rust/serde_with/tests/chrono_0_4.rs b/third_party/rust/serde_with/tests/chrono_0_4.rs new file mode 100644 index 0000000000..21855f404c --- /dev/null +++ b/third_party/rust/serde_with/tests/chrono_0_4.rs @@ -0,0 +1,742 @@ +#![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 utils; + +use crate::utils::{ + check_deserialization, check_error_deserialization, check_serialization, is_equal, +}; +use alloc::collections::BTreeMap; +use chrono_0_4::{DateTime, Duration, Local, NaiveDateTime, Utc}; +use core::{iter::FromIterator, str::FromStr}; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::{ + formats::Flexible, serde_as, DurationMicroSeconds, DurationMicroSecondsWithFrac, + DurationMilliSeconds, DurationMilliSecondsWithFrac, DurationNanoSeconds, + DurationNanoSecondsWithFrac, DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, + TimestampMicroSecondsWithFrac, TimestampMilliSeconds, TimestampMilliSecondsWithFrac, + TimestampNanoSeconds, TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac, +}; + +fn new_datetime(secs: i64, nsecs: u32) -> DateTime { + DateTime::from_utc(NaiveDateTime::from_timestamp_opt(secs, nsecs).unwrap(), Utc) +} + +#[test] +fn json_datetime_from_any_to_string_deserialization() { + #[derive(Debug, PartialEq, Deserialize)] + struct S( + #[serde(with = "serde_with::chrono_0_4::datetime_utc_ts_seconds_from_any")] DateTime, + ); + + // just integers + check_deserialization( + vec![ + S(new_datetime(1_478_563_200, 0)), + S(new_datetime(0, 0)), + S(new_datetime(-86000, 0)), + ], + r#"[ + 1478563200, + 0, + -86000 + ]"#, + ); + + // floats, shows precision errors in subsecond part + check_deserialization( + vec![ + S(new_datetime(1_478_563_200, 122_999_906)), + S(new_datetime(0, 0)), + S(new_datetime(-86000, 998_999_999)), + ], + r#"[ + 1478563200.123, + 0.000, + -86000.999 + ]"#, + ); + + // string representation of floats + check_deserialization( + vec![ + S(new_datetime(1_478_563_200, 123_000_000)), + S(new_datetime(0, 0)), + S(new_datetime(-86000, 999_000_000)), + ], + r#"[ + "1478563200.123", + "0.000", + "-86000.999" + ]"#, + ); +} + +#[test] +fn test_chrono_naive_date_time() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct S(#[serde_as(as = "DateTime")] NaiveDateTime); + + is_equal( + S(NaiveDateTime::from_str("1994-11-05T08:15:30").unwrap()), + expect![[r#""1994-11-05T08:15:30Z""#]], + ); +} + +#[test] +fn test_chrono_option_naive_date_time() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct S(#[serde_as(as = "Option>")] Option); + + is_equal( + S(NaiveDateTime::from_str("1994-11-05T08:15:30").ok()), + expect![[r#""1994-11-05T08:15:30Z""#]], + ); +} + +#[test] +fn test_chrono_vec_option_naive_date_time() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct S(#[serde_as(as = "Vec>>")] Vec>); + + is_equal( + S(vec![ + NaiveDateTime::from_str("1994-11-05T08:15:30").ok(), + NaiveDateTime::from_str("1994-11-05T08:15:31").ok(), + ]), + expect![[r#" + [ + "1994-11-05T08:15:30Z", + "1994-11-05T08:15:31Z" + ]"#]], + ); +} + +#[test] +fn test_chrono_btreemap_naive_date_time() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct S(#[serde_as(as = "BTreeMap<_, DateTime>")] BTreeMap); + + is_equal( + S(BTreeMap::from_iter(vec![ + (1, NaiveDateTime::from_str("1994-11-05T08:15:30").unwrap()), + (2, NaiveDateTime::from_str("1994-11-05T08:15:31").unwrap()), + ])), + expect![[r#" + { + "1": "1994-11-05T08:15:30Z", + "2": "1994-11-05T08:15:31Z" + }"#]], + ); +} + +#[test] +fn test_chrono_duration_seconds() { + let zero = Duration::zero(); + let one_second = Duration::seconds(1); + let half_second = Duration::nanoseconds(500_000_000); + let minus_one_second = zero - one_second; + let minus_half_second = zero - half_second; + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StructIntStrict(#[serde_as(as = "DurationSeconds")] Duration); + + 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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], + ); + check_error_deserialization::( + r#"9223372036854775808"#, + expect![[ + r#"invalid value: integer `9223372036854775808`, expected i64 at line 1 column 19"# + ]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StructIntFlexible(#[serde_as(as = "DurationSeconds")] Duration); + + is_equal(StructIntFlexible(zero), expect![[r#"0"#]]); + is_equal(StructIntFlexible(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(half_second), r#""0.5""#); + check_deserialization(StructIntFlexible(minus_half_second), r#""-0.5""#); + check_deserialization(StructIntFlexible(one_second), r#""1""#); + check_deserialization(StructIntFlexible(minus_one_second), r#""-1""#); + check_deserialization(StructIntFlexible(zero), r#""0""#); + check_error_deserialization::( + 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 = "DurationSeconds")] Duration); + + 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_deserialization(Structf64Strict(minus_one_second), r#"-0.5"#); + check_error_deserialization::( + 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 = "DurationSeconds")] Duration); + + 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(half_second), r#""0.5""#); + check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); + check_deserialization(Structf64Flexible(one_second), r#""1""#); + check_deserialization(Structf64Flexible(minus_one_second), r#""-1""#); + check_deserialization(Structf64Flexible(zero), r#""0""#); + check_error_deserialization::( + 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 = "DurationSeconds")] Duration); + + 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_error_deserialization::( + r#"1"#, + expect![[ + r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# + ]], + ); + check_error_deserialization::( + 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 StructStringFlexible(#[serde_as(as = "DurationSeconds")] Duration); + + 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_deserialization(StructStringFlexible(half_second), r#""0.5""#); + check_deserialization(StructStringFlexible(one_second), r#""1""#); + check_deserialization(StructStringFlexible(zero), r#""0""#); + check_error_deserialization::( + 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_chrono_duration_seconds_with_frac() { + let zero = Duration::zero(); + let one_second = Duration::seconds(1); + let half_second = Duration::nanoseconds(500_000_000); + let minus_one_second = zero - one_second; + let minus_half_second = zero - half_second; + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Structf64Strict(#[serde_as(as = "DurationSecondsWithFrac")] Duration); + + 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::( + 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 = "DurationSecondsWithFrac")] Duration); + + 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(minus_half_second), expect![[r#"-0.5"#]]); + check_deserialization(Structf64Flexible(one_second), r#""1""#); + check_deserialization(Structf64Flexible(minus_one_second), r#""-1""#); + check_deserialization(Structf64Flexible(half_second), r#""0.5""#); + check_deserialization(Structf64Flexible(zero), r#""0""#); + check_error_deserialization::( + 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 = "DurationSecondsWithFrac")] Duration); + + 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""#]], + ); + is_equal( + StructStringStrict(minus_half_second), + expect![[r#""-0.5""#]], + ); + check_error_deserialization::( + r#"1"#, + expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], + ); + check_error_deserialization::( + r#"-1"#, + expect![[r#"invalid type: integer `-1`, expected a string at line 1 column 2"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StructStringFlexible( + #[serde_as(as = "DurationSecondsWithFrac")] Duration, + ); + + 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(zero), r#""0""#); + check_error_deserialization::( + 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_chrono_timestamp_seconds() { + let zero = DateTime::::from_utc(NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), Utc); + let one_second = zero + Duration::seconds(1); + let half_second = zero + Duration::nanoseconds(500_000_000); + let minus_one_second = zero - Duration::seconds(1); + let minus_half_second = zero - Duration::nanoseconds(500_000_000); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StructIntStrict(#[serde_as(as = "TimestampSeconds")] DateTime); + + 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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], + ); + check_error_deserialization::( + 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")] DateTime); + + 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::( + 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")] DateTime); + + 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::( + 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")] DateTime); + + 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::( + 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")] DateTime); + + 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::( + r#""0.5""#, + expect![[r#"invalid digit found in string at line 1 column 5"#]], + ); + check_error_deserialization::( + r#""-0.5""#, + expect![[r#"invalid digit found in string at line 1 column 6"#]], + ); + check_error_deserialization::( + r#"1"#, + expect![[ + r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# + ]], + ); + check_error_deserialization::( + 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")] DateTime, + ); + + 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::( + 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_chrono_timestamp_seconds_with_frac() { + let zero = DateTime::::from_utc(NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), Utc); + let one_second = zero + Duration::seconds(1); + let half_second = zero + Duration::nanoseconds(500_000_000); + let minus_one_second = zero - Duration::seconds(1); + let minus_half_second = zero - Duration::nanoseconds(500_000_000); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Structf64Strict(#[serde_as(as = "TimestampSecondsWithFrac")] DateTime); + + 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::( + 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")] DateTime, + ); + + 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(minus_half_second), r#""-0.5""#); + check_error_deserialization::( + 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")] DateTime); + + 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::( + r#"1"#, + expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], + ); + check_error_deserialization::( + 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")] DateTime, + ); + + 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::( + 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:expr, $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 zero = Duration::seconds(0); + let one_second = Duration::seconds(1); + + smoketest! { + Duration, "DurationSeconds", one_second, {expect![[r#"1"#]]}; + Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + Duration, "DurationSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + Duration, "DurationSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + Duration, "DurationSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + Duration, "DurationSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + Duration, "DurationSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_datetime_utc_smoketest() { + let zero = DateTime::::from_utc(NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), Utc); + let one_second = zero + Duration::seconds(1); + + smoketest! { + DateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; + DateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + DateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + DateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + DateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + DateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + DateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_datetime_local_smoketest() { + let zero = DateTime::::from_utc(NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), Utc) + .with_timezone(&Local); + let one_second = zero + Duration::seconds(1); + + smoketest! { + DateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; + DateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + DateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + DateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + DateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + DateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + DateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_naive_datetime_smoketest() { + let zero = NaiveDateTime::from_timestamp_opt(0, 0).unwrap(); + let one_second = zero + Duration::seconds(1); + + smoketest! { + NaiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; + NaiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + NaiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + NaiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + NaiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + NaiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + NaiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + NaiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + NaiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + NaiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + NaiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + NaiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + NaiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + NaiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + NaiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + NaiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + NaiveDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + NaiveDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + NaiveDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + NaiveDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + NaiveDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} diff --git a/third_party/rust/serde_with/tests/derives/deserialize_fromstr.rs b/third_party/rust/serde_with/tests/derives/deserialize_fromstr.rs new file mode 100644 index 0000000000..26d60bf08f --- /dev/null +++ b/third_party/rust/serde_with/tests/derives/deserialize_fromstr.rs @@ -0,0 +1,96 @@ +use super::*; +use core::{ + num::ParseIntError, + str::{FromStr, ParseBoolError}, +}; +use pretty_assertions::assert_eq; +use serde_with::DeserializeFromStr; + +#[derive(Debug, PartialEq, DeserializeFromStr)] +struct A { + a: u32, + b: bool, +} + +impl FromStr for A { + type Err = String; + + /// Parse a value like `123<>true` + fn from_str(s: &str) -> Result { + let mut parts = s.split("<>"); + let number = parts + .next() + .ok_or_else(|| "Missing first value".to_string())? + .parse() + .map_err(|err: ParseIntError| err.to_string())?; + let bool = parts + .next() + .ok_or_else(|| "Missing second value".to_string())? + .parse() + .map_err(|err: ParseBoolError| err.to_string())?; + Ok(Self { a: number, b: bool }) + } +} + +#[test] +fn test_deserialize_fromstr() { + check_deserialization(A { a: 159, b: true }, "\"159<>true\""); + check_deserialization(A { a: 999, b: false }, "\"999<>false\""); + check_deserialization(A { a: 0, b: true }, "\"0<>true\""); +} + +#[test] +fn test_deserialize_from_bytes() { + use serde::de::{value::Error, Deserialize, Deserializer, Visitor}; + + // Unfortunately serde_json is too clever (i.e. handles bytes gracefully) + // so instead create a custom deserializer which can only deserialize bytes. + // All other deserialize_* fns are forwarded to deserialize_bytes + struct ByteDeserializer(&'static [u8]); + + impl<'de> Deserializer<'de> for ByteDeserializer { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_bytes(self.0) + } + + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } + } + + // callstack: A::deserialize -> deserialize_str -> deserialize_any -> + // deserialize_bytes -> visit_bytes -> visit_str -> success! + let a = A::deserialize(ByteDeserializer(b"159<>true")).unwrap(); + + assert_eq!(A { a: 159, b: true }, a); +} + +#[test] +fn test_deserialize_fromstr_in_vec() { + check_deserialization( + vec![ + A { a: 123, b: false }, + A { a: 0, b: true }, + A { a: 999, b: true }, + ], + r#"[ + "123<>false", + "0<>true", + "999<>true" + ]"#, + ); +} diff --git a/third_party/rust/serde_with/tests/derives/lib.rs b/third_party/rust/serde_with/tests/derives/lib.rs new file mode 100644 index 0000000000..a43a3c3535 --- /dev/null +++ b/third_party/rust/serde_with/tests/derives/lib.rs @@ -0,0 +1,15 @@ +#![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, +)] + +mod deserialize_fromstr; +mod serialize_display; +#[path = "../utils.rs"] +mod utils; + +use expect_test::expect; +use utils::*; diff --git a/third_party/rust/serde_with/tests/derives/serialize_display.rs b/third_party/rust/serde_with/tests/derives/serialize_display.rs new file mode 100644 index 0000000000..e660fa7535 --- /dev/null +++ b/third_party/rust/serde_with/tests/derives/serialize_display.rs @@ -0,0 +1,39 @@ +use super::*; +use core::fmt; +use serde_with::SerializeDisplay; + +#[derive(Debug, SerializeDisplay)] +struct A { + a: u32, + b: bool, +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "->{} <> {}<-", self.a, self.b) + } +} + +#[test] +fn test_serialize_display() { + check_serialization(A { a: 123, b: false }, expect![[r#""->123 <> false<-""#]]); + check_serialization(A { a: 0, b: true }, expect![[r#""->0 <> true<-""#]]); + check_serialization(A { a: 999, b: true }, expect![[r#""->999 <> true<-""#]]); +} + +#[test] +fn test_serialize_display_in_vec() { + check_serialization( + vec![ + A { a: 123, b: false }, + A { a: 0, b: true }, + A { a: 999, b: true }, + ], + expect![[r#" + [ + "->123 <> false<-", + "->0 <> true<-", + "->999 <> true<-" + ]"#]], + ); +} diff --git a/third_party/rust/serde_with/tests/hex.rs b/third_party/rust/serde_with/tests/hex.rs new file mode 100644 index 0000000000..2994ae475e --- /dev/null +++ b/third_party/rust/serde_with/tests/hex.rs @@ -0,0 +1,93 @@ +#![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, +)] + +mod utils; + +use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::{ + formats::{Lowercase, Uppercase}, + hex::Hex, + serde_as, +}; + +#[test] +fn hex_vec() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B(#[serde_as(as = "Vec")] Vec>); + + is_equal( + B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), + expect![[r#" + [ + "0001020d", + "0e050607" + ]"#]], + ); + + // Check mixed case deserialization + check_deserialization( + B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), + r#"["aaBCff","E07d"]"#, + ); + + check_error_deserialization::( + r#"["0"]"#, + expect![[r#"Odd number of digits at line 1 column 5"#]], + ); + check_error_deserialization::( + r#"["zz"]"#, + expect![[r#"Invalid character 'z' at position 0 at line 1 column 6"#]], + ); +} + +#[test] +fn hex_vec_lowercase() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B(#[serde_as(as = "Vec>")] Vec>); + + is_equal( + B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), + expect![[r#" + [ + "0001020d", + "0e050607" + ]"#]], + ); + + // Check mixed case deserialization + check_deserialization( + B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), + r#"["aaBCff","E07d"]"#, + ); +} + +#[test] +fn hex_vec_uppercase() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct B(#[serde_as(as = "Vec>")] Vec>); + + is_equal( + B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), + expect![[r#" + [ + "0001020D", + "0E050607" + ]"#]], + ); + + // Check mixed case deserialization + check_deserialization( + B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), + r#"["aaBCff","E07d"]"#, + ); +} diff --git a/third_party/rust/serde_with/tests/indexmap_1.rs b/third_party/rust/serde_with/tests/indexmap_1.rs new file mode 100644 index 0000000000..0f53bb3b66 --- /dev/null +++ b/third_party/rust/serde_with/tests/indexmap_1.rs @@ -0,0 +1,262 @@ +#![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, +)] + +mod utils; + +use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; +use core::iter::FromIterator; +use expect_test::expect; +use indexmap_1::{IndexMap, IndexSet}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr, Same}; +use std::net::IpAddr; + +#[test] +fn test_indexmap() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S(#[serde_as(as = "IndexMap")] IndexMap); + + // Normal + is_equal( + S([(1, 1), (3, 3), (111, 111)].iter().cloned().collect()), + expect![[r#" + { + "1": "1", + "3": "3", + "111": "111" + }"#]], + ); + is_equal(S(IndexMap::default()), expect![[r#"{}"#]]); +} + +#[test] +fn test_indexset() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S(#[serde_as(as = "IndexSet")] IndexSet); + + // Normal + is_equal( + S([1, 2, 3, 4, 5].iter().cloned().collect()), + expect![[r#" + [ + "1", + "2", + "3", + "4", + "5" + ]"#]], + ); + is_equal(S(IndexSet::default()), expect![[r#"[]"#]]); +} + +#[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 SI(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] IndexMap); + + let map: IndexMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); + is_equal( + SI(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 SI2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] IndexMap); + + is_equal( + SI2(map), + expect![[r#" + [ + [ + 1, + "1.2.3.4" + ], + [ + 10, + "1.2.3.4" + ], + [ + 200, + "255.255.255.255" + ] + ]"#]], + ); +} + +#[test] +fn duplicate_key_first_wins_indexmap() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] IndexMap); + + // Different value and key always works + is_equal( + S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "2": 2, + "3": 3 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "2": 1, + "3": 1 + }"#]], + ); + + // Duplicate keys, the first one is used + check_deserialization( + S(IndexMap::from_iter(vec![(1, 1), (2, 2)])), + r#"{"1": 1, "2": 2, "1": 3}"#, + ); +} + +#[test] +fn prohibit_duplicate_key_indexmap() { + #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] + struct S( + #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] IndexMap, + ); + + // Different value and key always works + is_equal( + S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "2": 2, + "3": 3 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "2": 1, + "3": 1 + }"#]], + ); + + // Duplicate keys are an error + check_error_deserialization::( + r#"{"1": 1, "2": 2, "1": 3}"#, + expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], + ); +} + +#[test] +fn duplicate_value_last_wins_indexset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] IndexSet); + + #[derive(Debug, Eq, Deserialize, Serialize)] + struct W(i32, bool); + impl PartialEq for W { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl std::hash::Hash for W { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + self.0.hash(state) + } + } + + // Different values always work + is_equal( + S(IndexSet::from_iter(vec![ + W(1, true), + W(2, false), + W(3, true), + ])), + expect![[r#" + [ + [ + 1, + true + ], + [ + 2, + false + ], + [ + 3, + true + ] + ]"#]], + ); + + let value: S = serde_json::from_str( + r#"[ + [1, false], + [1, true], + [2, true], + [2, false] + ]"#, + ) + .unwrap(); + let entries: Vec<_> = value.0.into_iter().collect(); + assert_eq!(1, entries[0].0); + assert!(entries[0].1); + assert_eq!(2, entries[1].0); + assert!(!entries[1].1); +} + +#[test] +fn prohibit_duplicate_value_indexset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] IndexSet); + + is_equal( + S(IndexSet::from_iter(vec![1, 2, 3, 4])), + expect![[r#" + [ + 1, + 2, + 3, + 4 + ]"#]], + ); + check_error_deserialization::( + r#"[1, 2, 3, 4, 1]"#, + expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], + ); +} diff --git a/third_party/rust/serde_with/tests/json.rs b/third_party/rust/serde_with/tests/json.rs new file mode 100644 index 0000000000..1babc44fee --- /dev/null +++ b/third_party/rust/serde_with/tests/json.rs @@ -0,0 +1,62 @@ +#![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, +)] + +mod utils; + +use crate::utils::is_equal; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::{json::JsonString, serde_as, DisplayFromStr}; +use std::collections::BTreeMap; + +#[test] +fn test_jsonstring() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Struct { + #[serde_as(as = "JsonString")] + value: Nested, + } + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Nested { + #[serde_as(as = "DisplayFromStr")] + value: u32, + } + + is_equal( + Struct { + value: Nested { value: 444 }, + }, + expect![[r#" + { + "value": "{\"value\":\"444\"}" + }"#]], + ); +} + +#[test] +fn test_jsonstring_nested() { + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Struct { + #[serde_as(as = "JsonString>")] + value: BTreeMap<[u8; 2], u32>, + } + + is_equal( + Struct { + value: BTreeMap::from([([1, 2], 3), ([4, 5], 6)]), + }, + expect![[r#" + { + "value": "[[\"[1,2]\",3],[\"[4,5]\",6]]" + }"#]], + ); +} diff --git a/third_party/rust/serde_with/tests/rust.rs b/third_party/rust/serde_with/tests/rust.rs new file mode 100644 index 0000000000..9b3efefff2 --- /dev/null +++ b/third_party/rust/serde_with/tests/rust.rs @@ -0,0 +1,386 @@ +#![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 utils; + +use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; +use alloc::collections::{BTreeMap, BTreeSet}; +use core::{cmp, iter::FromIterator as _}; +use expect_test::expect; +use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; +use pretty_assertions::assert_eq; +use serde::{Deserialize, Serialize}; + +#[test] +fn prohibit_duplicate_value_hashset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] HashSet); + + is_equal( + S(HashSet::from_iter(vec![1, 2, 3, 4])), + expect![[r#" + [ + 4, + 1, + 3, + 2 + ]"#]], + ); + check_error_deserialization::( + r#"[1, 2, 3, 4, 1]"#, + expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], + ); +} + +#[test] +fn prohibit_duplicate_value_btreeset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] BTreeSet); + + is_equal( + S(BTreeSet::from_iter(vec![1, 2, 3, 4])), + expect![[r#" + [ + 1, + 2, + 3, + 4 + ]"#]], + ); + check_error_deserialization::( + r#"[1, 2, 3, 4, 1]"#, + expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], + ); +} + +#[test] +fn prohibit_duplicate_key_hashmap() { + #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] + struct S( + #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] HashMap, + ); + + // Different value and key always works + is_equal( + S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "3": 3, + "2": 2 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "3": 1, + "2": 1 + }"#]], + ); + + // Duplicate keys are an error + check_error_deserialization::( + r#"{"1": 1, "2": 2, "1": 3}"#, + expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], + ); +} + +#[test] +fn prohibit_duplicate_key_btreemap() { + #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] + struct S( + #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] BTreeMap, + ); + + // Different value and key always works + is_equal( + S(BTreeMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "2": 2, + "3": 3 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(BTreeMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "2": 1, + "3": 1 + }"#]], + ); + + // Duplicate keys are an error + check_error_deserialization::( + r#"{"1": 1, "2": 2, "1": 3}"#, + expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], + ); +} + +#[test] +fn duplicate_key_first_wins_hashmap() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] HashMap); + + // Different value and key always works + is_equal( + S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "3": 3, + "2": 2 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "3": 1, + "2": 1 + }"#]], + ); + + // Duplicate keys, the first one is used + check_deserialization( + S(HashMap::from_iter(vec![(1, 1), (2, 2)])), + r#"{"1": 1, "2": 2, "1": 3}"#, + ); +} + +#[test] +fn duplicate_key_first_wins_btreemap() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] BTreeMap); + + // Different value and key always works + is_equal( + S(BTreeMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), + expect![[r#" + { + "1": 1, + "2": 2, + "3": 3 + }"#]], + ); + + // Same value for different keys is ok + is_equal( + S(BTreeMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), + expect![[r#" + { + "1": 1, + "2": 1, + "3": 1 + }"#]], + ); + + // Duplicate keys, the first one is used + check_deserialization( + S(BTreeMap::from_iter(vec![(1, 1), (2, 2)])), + r#"{"1": 1, "2": 2, "1": 3}"#, + ); +} + +#[test] +fn duplicate_value_first_wins_hashset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(HashSet); + // struct S(#[serde(with = "::serde_with::rust::sets_first_value_wins")] HashSet); + + #[derive(Debug, Eq, Deserialize, Serialize)] + struct W(i32, bool); + impl PartialEq for W { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl std::hash::Hash for W { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + self.0.hash(state) + } + } + + // Different values always work + is_equal( + S(HashSet::from_iter(vec![ + W(1, true), + W(2, false), + W(3, true), + ])), + expect![[r#" + [ + [ + 1, + true + ], + [ + 3, + true + ], + [ + 2, + false + ] + ]"#]], + ); + + let value: S = serde_json::from_str( + r#"[ + [1, false], + [1, true], + [2, true], + [2, false] + ]"#, + ) + .unwrap(); + let entries: Vec<_> = value.0.into_iter().collect(); + assert_eq!(1, entries[0].0); + assert!(!entries[0].1); + assert_eq!(2, entries[1].0); + assert!(entries[1].1); +} + +#[test] +fn duplicate_value_last_wins_hashset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] HashSet); + + #[derive(Debug, Eq, Deserialize, Serialize)] + struct W(i32, bool); + impl PartialEq for W { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl std::hash::Hash for W { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + self.0.hash(state) + } + } + + // Different values always work + is_equal( + S(HashSet::from_iter(vec![ + W(1, true), + W(2, false), + W(3, true), + ])), + expect![[r#" + [ + [ + 1, + true + ], + [ + 3, + true + ], + [ + 2, + false + ] + ]"#]], + ); + + let value: S = serde_json::from_str( + r#"[ + [1, false], + [1, true], + [2, true], + [2, false] + ]"#, + ) + .unwrap(); + let entries: Vec<_> = value.0.into_iter().collect(); + assert_eq!(1, entries[0].0); + assert!(entries[0].1); + assert_eq!(2, entries[1].0); + assert!(!entries[1].1); +} + +#[test] +fn duplicate_value_last_wins_btreeset() { + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] BTreeSet); + #[derive(Debug, Eq, Deserialize, Serialize)] + struct W(i32, bool); + impl PartialEq for W { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl Ord for W { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.0.cmp(&other.0) + } + } + impl PartialOrd for W { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + // Different values always work + is_equal( + S(BTreeSet::from_iter(vec![ + W(1, true), + W(2, false), + W(3, true), + ])), + expect![[r#" + [ + [ + 1, + true + ], + [ + 2, + false + ], + [ + 3, + true + ] + ]"#]], + ); + + let value: S = serde_json::from_str( + r#"[ + [1, false], + [1, true], + [2, true], + [2, false] + ]"#, + ) + .unwrap(); + let entries: Vec<_> = value.0.into_iter().collect(); + assert_eq!(1, entries[0].0); + assert!(entries[0].1); + assert_eq!(2, entries[1].0); + assert!(!entries[1].1); +} 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")] FnvHashSet); + + // 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")] FnvHashMap); + + // 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")] 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); + + // 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>")] + value: Vec, + } + check_deserialization(Struct2 { value: vec![] }, r#"{"value":}"#); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S3(#[serde_as(as = "Vec>")] Vec); + + // 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")] 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::( + r#""12+3""#, + expect![[r#"invalid digit found in string at line 1 column 6"#]], + ); + check_error_deserialization::( + 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")] Vec); + + // 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::( + 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>")] Vec); + + // 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::( + 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..4d620c9d27 --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/enum_map.rs @@ -0,0 +1,458 @@ +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{byte:02x}").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, +} + +#[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: + // 666BBBtrue + // EnumValue::Struct { + // a: 666, + // b: "BBB".to_string(), + // c: true, + // }, + ], + }; + + let xml = serde_xml_rs::to_string(&values).unwrap(); + expect_test::expect![[r#"123FooBar456XXX"#]] + .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: null + 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 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 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 for LikeBool { + fn from(b: bool) -> Self { + if b { + LikeBool::Trueish + } else { + LikeBool::Falseisch + } + } +} + +impl From 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")] 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")] 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")] 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")] 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")] 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")] 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 for String { + type Error = &'static str; + + fn try_from(value: TryIntoSerializable) -> Result { + 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 for TryFromDeserializable { + type Error = &'static str; + + fn try_from(value: u32) -> Result { + 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")] 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")] TryFromDeserializable); + + check_deserialization(S(TryFromDeserializable::Zero), "0"); + check_error_deserialization::("1", expect![[r#"Number is not zero"#]]); +} diff --git a/third_party/rust/serde_with/tests/serde_as/key_value_map.rs b/third_party/rust/serde_with/tests/serde_as/key_value_map.rs new file mode 100644 index 0000000000..8975af4421 --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/key_value_map.rs @@ -0,0 +1,350 @@ +use super::*; +use core::str::FromStr; +use serde_test::Configure; +use serde_with::{serde_as, KeyValueMap}; +use std::net::IpAddr; + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] +struct KVMap { + #[serde_as(as = "KeyValueMap<_>")] + #[serde(bound(serialize = "E: Serialize", deserialize = "E: Deserialize<'de>"))] + foo: Vec, +} + +#[test] +fn test_kvmap_struct_json() { + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Struct { + #[serde(rename = "$key$")] + key: String, + bar: String, + baz: i32, + } + + let kvmap = KVMap { + foo: vec![ + Struct { + key: "a".to_string(), + bar: "b".to_string(), + baz: 1, + }, + Struct { + key: "c".to_string(), + bar: "d".to_string(), + baz: 2, + }, + ], + }; + + is_equal( + kvmap, + expect![[r#" + { + "a": { + "bar": "b", + "baz": 1 + }, + "c": { + "bar": "d", + "baz": 2 + } + }"#]], + ); +} + +#[test] +fn test_kvmap_tuple_struct_json() { + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct TupleStruct(String, String, i32); + + let kvmap = KVMap { + foo: vec![ + TupleStruct("a".to_string(), "b".to_string(), 1), + TupleStruct("c".to_string(), "d".to_string(), 2), + ], + }; + + is_equal( + kvmap, + expect![[r#" + { + "a": [ + "b", + 1 + ], + "c": [ + "d", + 2 + ] + }"#]], + ); +} + +#[test] +fn test_kvmap_tuple_json() { + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + #[serde(transparent)] + struct Tuple((String, String, i32)); + + let kvmap = KVMap { + foo: vec![ + Tuple(("a".to_string(), "b".to_string(), 1)), + Tuple(("c".to_string(), "d".to_string(), 2)), + ], + }; + + is_equal( + kvmap, + expect![[r#" + { + "a": [ + "b", + 1 + ], + "c": [ + "d", + 2 + ] + }"#]], + ); +} + +#[test] +fn test_kvmap_map_json() { + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + #[serde(transparent)] + struct Map(BTreeMap); + + let kvmap = KVMap { + foo: vec![ + Map(BTreeMap::from([ + ("$key$".to_string(), "a".to_string()), + ("bar".to_string(), "b".to_string()), + ("baz".to_string(), "1".to_string()), + ])), + Map(BTreeMap::from([ + ("$key$".to_string(), "c".to_string()), + ("bar".to_string(), "d".to_string()), + ("baz".to_string(), "2".to_string()), + ])), + ], + }; + + is_equal( + kvmap, + expect![[r#" + { + "a": { + "bar": "b", + "baz": "1" + }, + "c": { + "bar": "d", + "baz": "2" + } + }"#]], + ); +} + +#[test] +fn test_kvmap_seq_json() { + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + #[serde(transparent)] + struct Seq(Vec); + + let kvmap = KVMap { + foo: vec![ + Seq(vec!["a".to_string(), "b".to_string(), "1".to_string()]), + Seq(vec!["c".to_string(), "d".to_string(), "2".to_string()]), + ], + }; + + is_equal( + kvmap, + expect![[r#" + { + "a": [ + "b", + "1" + ], + "c": [ + "d", + "2" + ] + }"#]], + ); +} + +#[test] +fn test_kvmap_complex_key_serde_test() { + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + struct ComplexKey { + #[serde(rename = "$key$")] + net: (IpAddr, u8), + bar: String, + baz: i32, + } + + let kvmap = KVMap { + foo: vec![ + ComplexKey { + net: (IpAddr::from_str("127.0.0.0").unwrap(), 24), + bar: "b".to_string(), + baz: 1, + }, + ComplexKey { + net: (IpAddr::from_str("::7777:dead:beef").unwrap(), 48), + bar: "d".to_string(), + baz: 2, + }, + ], + }; + + { + use serde_test::Token::*; + serde_test::assert_tokens( + &kvmap.clone().readable(), + &[ + Map { + len: Option::Some(2), + }, + Tuple { len: 2 }, + Str("127.0.0.0"), + U8(24), + TupleEnd, + Struct { + name: "ComplexKey", + len: 2, + }, + Str("bar"), + Str("b"), + Str("baz"), + I32(1), + StructEnd, + Tuple { len: 2 }, + Str("::7777:dead:beef"), + U8(48), + TupleEnd, + Struct { + name: "ComplexKey", + len: 2, + }, + Str("bar"), + Str("d"), + Str("baz"), + I32(2), + StructEnd, + MapEnd, + ], + ); + + serde_test::assert_tokens( + &kvmap.compact(), + &[ + Map { + len: Option::Some(2), + }, + Tuple { len: 2 }, + NewtypeVariant { + name: "IpAddr", + variant: "V4", + }, + Tuple { len: 4 }, + U8(127), + U8(0), + U8(0), + U8(0), + TupleEnd, + U8(24), + TupleEnd, + Struct { + name: "ComplexKey", + len: 2, + }, + Str("bar"), + Str("b"), + Str("baz"), + I32(1), + StructEnd, + Tuple { len: 2 }, + 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, + U8(48), + TupleEnd, + Struct { + name: "ComplexKey", + len: 2, + }, + Str("bar"), + Str("d"), + Str("baz"), + I32(2), + StructEnd, + MapEnd, + ], + ); + } +} + +#[test] +fn test_kvmap_complex_key_yaml() { + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + struct ComplexKey { + #[serde(rename = "$key$")] + net: (IpAddr, u8), + bar: String, + baz: i32, + } + + let kvmap = KVMap { + foo: vec![ + ComplexKey { + net: (IpAddr::from_str("127.0.0.0").unwrap(), 24), + bar: "b".to_string(), + baz: 1, + }, + ComplexKey { + net: (IpAddr::from_str("::7777:dead:beef").unwrap(), 48), + bar: "d".to_string(), + baz: 2, + }, + ], + }; + + let yaml = serde_yaml::to_string(&kvmap).unwrap(); + expect_test::expect![[r#" + ? - 127.0.0.0 + - 24 + : bar: b + baz: 1 + ? - ::7777:dead:beef + - 48 + : bar: d + baz: 2 + "#]] + .assert_eq(&yaml); + let deser_values = serde_yaml::from_str(&yaml).unwrap(); + assert_eq!(kvmap, deser_values); +} 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..122c02fdb8 --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/lib.rs @@ -0,0 +1,1181 @@ +#![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 key_value_map; +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::{CommaSeparator, Flexible, Strict}, + serde_as, BoolFromInt, BytesOrString, DisplayFromStr, Map, NoneAsEmptyString, OneOrMany, Same, + Seq, 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")] Box); + + is_equal(SBox(Box::new(123)), expect![[r#""123""#]]); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct SRc(#[serde_as(as = "Rc")] Rc); + + is_equal(SRc(Rc::new(123)), expect![[r#""123""#]]); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize)] + struct SRcWeak(#[serde_as(as = "RcWeak")] RcWeak); + + 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")] Arc); + + is_equal(SArc(Arc::new(123)), expect![[r#""123""#]]); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize)] + struct SArcWeak(#[serde_as(as = "ArcWeak")] ArcWeak); + + 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")] Cell); + + is_equal(SCell(Cell::new(123)), expect![[r#""123""#]]); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct SRefCell(#[serde_as(as = "RefCell")] RefCell); + + is_equal(SRefCell(RefCell::new(123)), expect![[r#""123""#]]); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize)] + struct SMutex(#[serde_as(as = "Mutex")] Mutex); + + 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")] RwLock); + + 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); + + is_equal(S(None), expect![[r#"null"#]]); + is_equal(S(Some(9)), expect![[r#"9"#]]); + check_error_deserialization::( + 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, + } + check_error_deserialization::( + 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, DisplayFromStr>")] + Result, 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::(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")] BTreeSet); + 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")] LinkedList); + 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")] Vec); + 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")] VecDeque); + 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); + + 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); + + 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")] Vec>); + + 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::{ + formats::{CommaSeparator, SpaceSeparator}, + StringWithSeparator, + }; + + #[serde_as] + #[derive(Deserialize, Serialize)] + struct A { + #[serde_as(as = "StringWithSeparator::")] + tags: Vec, + #[serde_as(as = "StringWithSeparator::")] + // more_tags: Vec, + more_tags: BTreeSet, + } + + 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, + } + + 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")] &'a Vec); + check_serialization( + S1(&vec![1, 2]), + expect![[r#" + [ + "1", + "2" + ]"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize)] + struct S1a<'a>(#[serde_as(as = "&Vec")] &'a Vec); + check_serialization( + S1(&vec![1, 2]), + expect![[r#" + [ + "1", + "2" + ]"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize)] + struct S1Mut<'a>(#[serde_as(as = "Vec")] &'a mut Vec); + check_serialization( + S1(&vec![1, 2]), + expect![[r#" + [ + "1", + "2" + ]"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize)] + struct S1aMut<'a>(#[serde_as(as = "&mut Vec")] &'a mut Vec); + 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")] &'a BTreeMap, + ); + 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); + 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>")] + &'a Vec<(u32, &'a BTreeMap)>, + ); + 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::( + 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::( + 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, + + #[serde_as(as = "Bytes")] + cow_slice: Cow<'a, [u8]>, + + #[serde_as(as = "Box")] + 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")] + opt_slice: Option<&'a [u8]>, + + #[serde_as(as = "Option")] + opt_vec: Option>, + + #[serde_as(as = "Option")] + opt_cow_slice: Option>, + } + + 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); + + // 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::( + r#"{}"#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: map, expected u32 + Many: invalid type: map, expected a sequence"#]], + ); + check_error_deserialization::( + r#""xx""#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: string "xx", expected u32 + Many: invalid type: string "xx", expected a sequence"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S2Vec(#[serde_as(as = "OneOrMany")] Vec); + + // 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::( + r#"{}"#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: map, expected a string + Many: invalid type: map, expected a sequence"#]], + ); + check_error_deserialization::( + r#""xx""#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid digit found in string + Many: invalid type: string "xx", expected a sequence"#]], + ); +} + +#[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); + + // 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::( + r#"{}"#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: map, expected u32 + Many: invalid type: map, expected a sequence"#]], + ); + check_error_deserialization::( + r#""xx""#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: string "xx", expected u32 + Many: invalid type: string "xx", expected a sequence"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S2Vec(#[serde_as(as = "OneOrMany")] Vec); + + // 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::( + r#"{}"#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid type: map, expected a string + Many: invalid type: map, expected a sequence"#]], + ); + check_error_deserialization::( + r#""xx""#, + expect![[r#" + OneOrMany could not deserialize any variant: + One: invalid digit found in string + Many: invalid type: string "xx", expected a sequence"#]], + ); +} + +/// 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")] + opt: Option>, + #[serde_as(as = "Box")] + b: Box>, + #[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")] + opt: Option>, + } + + 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::( + "2", + expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]], + ); + check_error_deserialization::( + "-100", + expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]], + ); + check_error_deserialization::( + "18446744073709551615", + expect![[ + r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"# + ]], + ); + check_error_deserialization::( + 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")] bool); + + is_equal(SStrict(false), expect![[r#"0"#]]); + is_equal(SStrict(true), expect![[r#"1"#]]); + check_error_deserialization::( + "2", + expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]], + ); + check_error_deserialization::( + "-100", + expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]], + ); + check_error_deserialization::( + "18446744073709551615", + expect![[ + r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"# + ]], + ); + check_error_deserialization::( + 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")] bool); + + is_equal(SFlexible(false), expect![[r#"0"#]]); + is_equal(SFlexible(true), expect![[r#"1"#]]); + check_deserialization::(SFlexible(true), "2"); + check_deserialization::(SFlexible(true), "-100"); + check_deserialization::(SFlexible(true), "18446744073709551615"); + check_error_deserialization::( + 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..59b00d614e --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/map_tuple_list.rs @@ -0,0 +1,687 @@ +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 SM(#[serde_as(as = "Seq<(DisplayFromStr, DisplayFromStr)>")] BTreeMap); + + let map: BTreeMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); + is_equal( + SM(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 SB(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] BTreeMap); + + 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); + + 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); + + // 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::( + 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); + + 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::( + 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 SM(#[serde_as(as = "Map")] Vec<(u32, IpAddr)>); + + is_equal( + SM(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 SH(#[serde_as(as = "HashMap")] 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")] 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")] 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")] 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")] 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 S0(#[serde_as(as = "Map<_, _>")] [(u8, u8); 1]); + is_equal( + S1([(1, 2)]), + expect![[r#" + { + "1": 2 + }"#]], + ); + + #[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 + }"#]], + ); +} + +// Test that the `Seq` conversion works when the inner type is explicity specified. +#[test] +fn test_map_as_tuple_with_nested_complex_type() { + #[serde_as] + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct S0(#[serde_as(as = "Seq<(_, Vec<_>)>")] BTreeMap>); + + let value = S0(BTreeMap::from([ + (1, Vec::from([1, 2])), + (2, Vec::from([3, 4])), + ])); + is_equal( + value, + expect![[r#" + [ + [ + 1, + [ + 1, + 2 + ] + ], + [ + 2, + [ + 3, + 4 + ] + ] + ]"#]], + ); +} + +#[test] +fn test_map_as_tuple_list_works_with_serializer_that_needs_length_to_serialize_sequence() { + use serde::{ + ser::{ + SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, + SerializeTupleStruct, SerializeTupleVariant, + }, + Serializer, + }; + use std::fmt::{self, Debug, Display}; + + #[derive(Debug)] + enum TestError { + LengthRequired, + Other, + } + + impl Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(self, f) + } + } + + impl std::error::Error for TestError {} + + impl serde::ser::Error for TestError { + fn custom(_: T) -> Self + where + T: Display, + { + TestError::Other + } + } + + struct TestSerializer; + + impl<'a> Serializer for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + + fn serialize_bool(self, _: bool) -> Result { + Ok(()) + } + + fn serialize_i8(self, _: i8) -> Result { + Ok(()) + } + + fn serialize_i16(self, _: i16) -> Result { + Ok(()) + } + + fn serialize_i32(self, _: i32) -> Result { + Ok(()) + } + + fn serialize_i64(self, _: i64) -> Result { + Ok(()) + } + + fn serialize_u8(self, _: u8) -> Result { + Ok(()) + } + + fn serialize_u16(self, _: u16) -> Result { + Ok(()) + } + + fn serialize_u32(self, _: u32) -> Result { + Ok(()) + } + + fn serialize_u64(self, _: u64) -> Result { + Ok(()) + } + + fn serialize_f32(self, _: f32) -> Result { + Ok(()) + } + + fn serialize_f64(self, _: f64) -> Result { + Ok(()) + } + + fn serialize_char(self, _: char) -> Result { + Ok(()) + } + + fn serialize_str(self, _: &str) -> Result { + Ok(()) + } + + fn serialize_bytes(self, _: &[u8]) -> Result { + Ok(()) + } + + fn serialize_none(self) -> Result { + Ok(()) + } + + fn serialize_some(self, v: &T) -> Result + where + T: Serialize + ?Sized, + { + v.serialize(self) + } + + fn serialize_unit(self) -> Result { + Ok(()) + } + + fn serialize_unit_struct(self, _: &'static str) -> Result { + Ok(()) + } + + fn serialize_unit_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + ) -> Result { + Ok(()) + } + + fn serialize_newtype_struct( + self, + _: &'static str, + value: &T, + ) -> Result + where + T: Serialize + ?Sized, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + value: &T, + ) -> Result + where + T: Serialize + ?Sized, + { + value.serialize(self) + } + + fn serialize_seq(self, len: Option) -> Result { + len.map(|_| self).ok_or(TestError::LengthRequired) + } + + fn serialize_tuple(self, _: usize) -> Result { + Ok(self) + } + + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Ok(self) + } + + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Ok(self) + } + + fn serialize_map(self, _: Option) -> Result { + Ok(self) + } + + fn serialize_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Ok(self) + } + + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Ok(self) + } + } + + impl<'a> SerializeMap for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + key.serialize(&mut **self) + } + + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeSeq for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeStruct for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_field(&mut self, _: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeStructVariant for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_field(&mut self, _: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeTuple for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeTupleStruct for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + impl<'a> SerializeTupleVariant for &'a mut TestSerializer { + type Ok = (); + type Error = TestError; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } + } + + #[serde_as] + #[derive(Debug, Default, Serialize)] + struct Data { + #[serde_as(as = "Seq<(_, _)>")] + xs: HashMap, + } + + Data::default().serialize(&mut TestSerializer).unwrap(); +} 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..8c221df86b --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/pickfirst.rs @@ -0,0 +1,149 @@ +use super::*; +use serde_with::{ + formats::{CommaSeparator, SpaceSeparator}, + PickFirst, 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::( + r#""Abc""#, + expect![[r#" + PickFirst could not deserialize any variant: + First: invalid type: string "Abc", expected u32 + Second: invalid digit found in string"#]], + ); + + #[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::,)>")] + Vec, + ); + 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::, _,)>")] + Vec, + ); + 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, StringWithSeparator::)>" + )] + Vec, + ); + 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::( + r#""Abc""#, + expect![[r#" + PickFirst could not deserialize any variant: + First: invalid type: string "Abc", expected a sequence + Second: invalid type: string "Abc", expected a sequence + Third: invalid digit found in string"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct S2( + #[serde_as( + as = "PickFirst<(StringWithSeparator::, _, Vec)>" + )] + Vec, + ); + 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::( + r#""Abc""#, + expect![[r#" + PickFirst could not deserialize any variant: + First: invalid type: string "Abc", expected u32 + Second: invalid type: string "Abc", expected u32 + Third: invalid type: string "Abc", expected u32 + Fourth: invalid type: string "Abc", expected u32"#]], + ); +} 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..8ef8fcb6c5 --- /dev/null +++ b/third_party/rust/serde_with/tests/serde_as/serde_as_macro.rs @@ -0,0 +1,307 @@ +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, + #[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")] + b: Vec, + #[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")] + b: Vec, + #[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", + deserialize_as = "Vec" + )] + b: Vec, + #[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" + }"#]], + ); +} + +/// Ensure that `serde_as` applies `default` if both the field and the conversion are option. +#[test] +fn test_default_on_option() { + #[serde_as] + #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "Option")] + a: Option, + } + + is_equal( + Data { a: None }, + expect![[r#" + { + "a": null + }"#]], + ); + is_equal( + Data { a: Some(123) }, + expect![[r#" + { + "a": "123" + }"#]], + ); + check_deserialization(Data { a: None }, "{}"); + + #[serde_as] + #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] + struct DataNoDefault { + #[serde_as(as = "Option", no_default)] + a: Option, + } + + is_equal( + DataNoDefault { a: None }, + expect![[r#" + { + "a": null + }"#]], + ); + is_equal( + DataNoDefault { a: Some(123) }, + expect![[r#" + { + "a": "123" + }"#]], + ); + check_error_deserialization::( + "{}", + expect!["missing field `a` at line 1 column 2"], + ); + + fn default_555() -> Option { + Some(555) + } + + #[serde_as] + #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] + struct DataExplicitDefault { + #[serde_as(as = "Option")] + #[serde(default = "default_555")] + a: Option, + } + + is_equal( + DataExplicitDefault { a: None }, + expect![[r#" + { + "a": null + }"#]], + ); + is_equal( + DataExplicitDefault { a: Some(123) }, + expect![[r#" + { + "a": "123" + }"#]], + ); + check_deserialization(DataExplicitDefault { a: Some(555) }, "{}"); + + #[serde_as] + #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] + struct DataString { + #[serde_as(as = "NoneAsEmptyString")] + a: Option, + } + + is_equal( + DataString { a: None }, + expect![[r#" + { + "a": "" + }"#]], + ); + is_equal( + DataString { + a: Some("123".to_string()), + }, + expect![[r#" + { + "a": "123" + }"#]], + ); + check_deserialization(DataString { a: None }, r#"{"a": ""}"#); + check_deserialization( + DataString { + a: Some("555".to_string()), + }, + r#"{"a": "555"}"#, + ); + check_error_deserialization::( + "{}", + expect!["missing field `a` at line 1 column 2"], + ); +} 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::( + "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::( + "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")] Vec); + + is_equal( + SAsVec(vec![false]), + expect![[r#" + [ + "false" + ]"#]], + ); + is_equal( + SAsVec(vec![true]), + expect![[r#" + [ + "true" + ]"#]], + ); + check_error_deserialization::( + "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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected u64 at line 1 column 3"#]], + ); + check_error_deserialization::( + 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")] 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::( + 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::( + r#"-1"#, + expect![[r#"std::time::Duration cannot be negative"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct F64Strict(#[serde_as(as = "DurationSeconds")] 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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], + ); + check_error_deserialization::( + r#"-1.0"#, + expect![[r#"std::time::Duration cannot be negative"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct F64Flexible(#[serde_as(as = "DurationSeconds")] 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::( + 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::( + r#"-1"#, + expect![[r#"std::time::Duration cannot be negative"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StringStrict(#[serde_as(as = "DurationSeconds")] 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::( + r#"1"#, + expect![[ + r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# + ]], + ); + check_error_deserialization::( + 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")] 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::( + 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::( + 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")] 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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], + ); + check_error_deserialization::( + r#"-1.0"#, + expect![[r#"std::time::Duration cannot be negative"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct F64Flexible(#[serde_as(as = "DurationSecondsWithFrac")] 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::( + 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::( + r#"-1"#, + expect![[r#"std::time::Duration cannot be negative"#]], + ); + + #[serde_as] + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct StringStrict(#[serde_as(as = "DurationSecondsWithFrac")] 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::( + r#"1"#, + expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], + ); + check_error_deserialization::( + 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")] 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::( + 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::( + 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::( + r#""1""#, + expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], + ); + check_error_deserialization::( + 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")] 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::( + 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")] 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::( + 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")] 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::( + 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")] 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::( + r#""0.5""#, + expect![[r#"invalid digit found in string at line 1 column 5"#]], + ); + check_error_deserialization::( + r#""-0.5""#, + expect![[r#"invalid digit found in string at line 1 column 6"#]], + ); + check_error_deserialization::( + r#"1"#, + expect![[ + r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# + ]], + ); + check_error_deserialization::( + 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")] 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::( + 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")] 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::( + 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")] 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::( + 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")] 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::( + r#"1"#, + expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], + ); + check_error_deserialization::( + 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")] 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::( + 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", one_second, {expect![[r#"1"#]]}; + Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + Duration, "DurationNanoSecondsWithFrac", 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", one_second, {expect![[r#"1"#]]}; + SystemTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + SystemTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + SystemTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + SystemTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + SystemTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + SystemTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + SystemTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + SystemTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + SystemTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + SystemTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + SystemTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + SystemTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + SystemTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + SystemTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + SystemTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; +} diff --git a/third_party/rust/serde_with/tests/time_0_3.rs b/third_party/rust/serde_with/tests/time_0_3.rs new file mode 100644 index 0000000000..214ec39f8f --- /dev/null +++ b/third_party/rust/serde_with/tests/time_0_3.rs @@ -0,0 +1,279 @@ +#![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, +)] + +mod utils; + +use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::{ + serde_as, DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds, + DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac, + DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac, + TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds, + TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac, +}; +use time_0_3::{Duration, OffsetDateTime, PrimitiveDateTime, UtcOffset}; + +/// Create a [`PrimitiveDateTime`] for the Unix Epoch +fn unix_epoch_primitive() -> PrimitiveDateTime { + PrimitiveDateTime::new( + time_0_3::Date::from_ordinal_date(1970, 1).unwrap(), + time_0_3::Time::from_hms_nano(0, 0, 0, 0).unwrap(), + ) +} + +macro_rules! smoketest { + ($($valuety:ty, $adapter:literal, $value:expr, $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 zero = Duration::seconds(0); + let one_second = Duration::seconds(1); + + smoketest! { + Duration, "DurationSeconds", one_second, {expect![[r#"1"#]]}; + Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; + Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + Duration, "DurationSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + Duration, "DurationSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + Duration, "DurationSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + Duration, "DurationSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + Duration, "DurationSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_datetime_utc_smoketest() { + let zero = OffsetDateTime::UNIX_EPOCH; + let one_second = zero + Duration::seconds(1); + + smoketest! { + OffsetDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; + OffsetDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + OffsetDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + OffsetDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + OffsetDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + OffsetDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + OffsetDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + OffsetDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + OffsetDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + OffsetDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + OffsetDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + OffsetDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + OffsetDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + OffsetDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + OffsetDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + OffsetDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + OffsetDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + OffsetDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + OffsetDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + OffsetDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + OffsetDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_naive_datetime_smoketest() { + let zero = unix_epoch_primitive(); + let one_second = zero + Duration::seconds(1); + + smoketest! { + PrimitiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; + PrimitiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; + PrimitiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; + PrimitiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; + PrimitiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; + PrimitiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; + PrimitiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; + PrimitiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; + }; + + smoketest! { + PrimitiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; + PrimitiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; + PrimitiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; + PrimitiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; + PrimitiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; + PrimitiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; + PrimitiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; + PrimitiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; + }; + + smoketest! { + PrimitiveDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; + PrimitiveDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; + PrimitiveDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; + PrimitiveDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; + PrimitiveDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; + }; +} + +#[test] +fn test_offset_datetime_rfc2822() { + #[serde_as] + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde_as(as = "time_0_3::format_description::well_known::Rfc2822")] OffsetDateTime); + + is_equal( + S(OffsetDateTime::UNIX_EPOCH), + expect![[r#""Thu, 01 Jan 1970 00:00:00 +0000""#]], + ); + + check_error_deserialization::( + r#""Foobar""#, + expect![[r#"the 'weekday' component could not be parsed at line 1 column 8"#]], + ); + check_error_deserialization::( + r#""Fri, 2000""#, + expect![[r#"a character literal was not valid at line 1 column 11"#]], + ); +} + +#[test] +fn test_offset_datetime_rfc3339() { + #[serde_as] + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S(#[serde_as(as = "time_0_3::format_description::well_known::Rfc3339")] OffsetDateTime); + + is_equal( + S(OffsetDateTime::UNIX_EPOCH), + expect![[r#""1970-01-01T00:00:00Z""#]], + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(482_196_050_520_000_000) + .unwrap() + .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), + ), + r#""1985-04-12T23:20:50.52Z""#, + ); + check_deserialization::( + S(OffsetDateTime::from_unix_timestamp(851_042_397) + .unwrap() + .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap())), + r#""1996-12-19T16:39:57-08:00""#, + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(662_687_999_999_999_999) + .unwrap() + .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), + ), + r#""1990-12-31T23:59:60Z""#, + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(662_687_999_999_999_999) + .unwrap() + .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap()), + ), + r#""1990-12-31T15:59:60-08:00""#, + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(-1_041_337_172_130_000_000) + .unwrap() + .to_offset(UtcOffset::from_hms(0, 20, 0).unwrap()), + ), + r#""1937-01-01T12:00:27.87+00:20""#, + ); + + check_error_deserialization::( + r#""Foobar""#, + expect![[r#"the 'year' component could not be parsed at line 1 column 8"#]], + ); + check_error_deserialization::( + r#""2000-AA""#, + expect![[r#"the 'month' component could not be parsed at line 1 column 9"#]], + ); +} + +#[test] +fn test_offset_datetime_iso8601() { + /// The default configuration for [`Iso8601`]. + const DEFAULT_CONFIG: time_0_3::format_description::well_known::iso8601::EncodedConfig = + time_0_3::format_description::well_known::iso8601::Config::DEFAULT.encode(); + + #[serde_as] + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct S( + #[serde_as(as = "time_0_3::format_description::well_known::Iso8601")] + OffsetDateTime, + ); + + is_equal( + S(OffsetDateTime::UNIX_EPOCH), + expect![[r#""1970-01-01T00:00:00.000000000Z""#]], + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(482_196_050_520_000_000) + .unwrap() + .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), + ), + r#""1985-04-12T23:20:50.52Z""#, + ); + check_deserialization::( + S(OffsetDateTime::from_unix_timestamp(851_042_397) + .unwrap() + .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap())), + r#""1996-12-19T16:39:57-08:00""#, + ); + check_deserialization::( + S( + OffsetDateTime::from_unix_timestamp_nanos(-1_041_337_172_130_000_000) + .unwrap() + .to_offset(UtcOffset::from_hms(0, 20, 0).unwrap()), + ), + r#""1937-01-01T12:00:27.87+00:20""#, + ); + + check_error_deserialization::( + r#""Foobar""#, + expect![[r#"the 'year' component could not be parsed at line 1 column 8"#]], + ); + check_error_deserialization::( + r#""2000-AA""#, + expect!["unexpected trailing characters at line 1 column 9"], + ); +} diff --git a/third_party/rust/serde_with/tests/utils.rs b/third_party/rust/serde_with/tests/utils.rs new file mode 100644 index 0000000000..d9247ffff9 --- /dev/null +++ b/third_party/rust/serde_with/tests/utils.rs @@ -0,0 +1,79 @@ +#![allow(dead_code)] + +use core::fmt::Debug; +use expect_test::Expect; +use pretty_assertions::assert_eq; +use serde::{de::DeserializeOwned, Serialize}; + +#[track_caller] +pub fn is_equal(value: T, expected: Expect) +where + T: Debug + DeserializeOwned + PartialEq + Serialize, +{ + let serialized = serde_json::to_string_pretty(&value).unwrap(); + expected.assert_eq(&serialized); + assert_eq!( + value, + serde_json::from_str::(&serialized).unwrap(), + "Deserialization differs from expected value." + ); +} + +/// Like [`is_equal`] but not pretty-print +#[track_caller] +pub fn is_equal_compact(value: T, expected: Expect) +where + T: Debug + DeserializeOwned + PartialEq + Serialize, +{ + let serialized = serde_json::to_string(&value).unwrap(); + expected.assert_eq(&serialized); + assert_eq!( + value, + serde_json::from_str::(&serialized).unwrap(), + "Deserialization differs from expected value." + ); +} + +#[track_caller] +pub fn check_deserialization(value: T, deserialize_from: &str) +where + T: Debug + DeserializeOwned + PartialEq, +{ + assert_eq!( + value, + serde_json::from_str::(deserialize_from).unwrap(), + "Deserialization differs from expected value." + ); +} + +#[track_caller] +pub fn check_serialization(value: T, serialize_to: Expect) +where + T: Debug + Serialize, +{ + serialize_to.assert_eq(&serde_json::to_string_pretty(&value).unwrap()); +} + +#[track_caller] +pub fn check_error_serialization(value: T, error_msg: Expect) +where + T: Debug + Serialize, +{ + error_msg.assert_eq( + &serde_json::to_string_pretty(&value) + .unwrap_err() + .to_string(), + ); +} + +#[track_caller] +pub fn check_error_deserialization(deserialize_from: &str, error_msg: Expect) +where + T: Debug + DeserializeOwned, +{ + error_msg.assert_eq( + &serde_json::from_str::(deserialize_from) + .unwrap_err() + .to_string(), + ) +} diff --git a/third_party/rust/serde_with/tests/version_numbers.rs b/third_party/rust/serde_with/tests/version_numbers.rs new file mode 100644 index 0000000000..be520a2a66 --- /dev/null +++ b/third_party/rust/serde_with/tests/version_numbers.rs @@ -0,0 +1,79 @@ +// Needed to suppress a 2021 incompatibility warning in the macro generated code +// The non_fmt_panic lint is not yet available on most Rust versions +#![allow(unknown_lints, non_fmt_panics)] + +use version_sync::{assert_contains_regex, assert_html_root_url_updated}; + +#[test] +fn test_changelog() { + assert_contains_regex!("CHANGELOG.md", r#"## \[{version}\]"#); +} + +#[test] +fn test_html_root_url() { + assert_html_root_url_updated!("src/lib.rs"); +} + +#[test] +fn test_serde_with_macros_dependency() { + version_sync::assert_contains_regex!( + "../serde_with/Cargo.toml", + r#"^serde_with_macros = .*? version = "={version}""# + ); + version_sync::assert_contains_regex!( + "../serde_with_macros/Cargo.toml", + r#"^version = "{version}""# + ); +} + +/// Check that all docs.rs links point to the current version +/// +/// Parse all docs.rs links in `*.rs` and `*.md` files and check that they point to the current version. +/// If a link should point to latest version this can be done by using `latest` in the version. +/// The `*` version specifier is not allowed. +/// +/// Arguably this should be part of version-sync. There is an open issue for this feature: +/// https://github.com/mgeisler/version-sync/issues/72 +#[test] +fn test_docs_rs_url_point_to_current_version() -> Result<(), Box> { + let pkg_name = env!("CARGO_PKG_NAME"); + let pkg_version = env!("CARGO_PKG_VERSION"); + + let re = regex::Regex::new(&format!( + "https?://docs.rs/{pkg_name}/((\\d[^/]+|\\*|latest))/" + ))?; + let mut error = false; + + for entry in glob::glob("**/*.rs")?.chain(glob::glob("**/README.md")?) { + let entry = entry?; + let content = std::fs::read_to_string(&entry)?; + for (line_number, line) in content.split('\n').enumerate() { + for capture in re.captures_iter(line) { + match capture + .get(1) + .expect("Will exist if regex matches") + .as_str() + { + "latest" => {} + version if version != pkg_version => { + error = true; + println!( + "{}:{} pkg_version is {} but found URL {}", + entry.display(), + line_number + 1, + pkg_version, + capture.get(0).expect("Group 0 always exists").as_str() + ) + } + _ => {} + } + } + } + } + + if error { + panic!("Found wrong URLs in file(s)"); + } else { + Ok(()) + } +} diff --git a/third_party/rust/serde_with/tests/with_prefix.rs b/third_party/rust/serde_with/tests/with_prefix.rs new file mode 100644 index 0000000000..bc7a78711e --- /dev/null +++ b/third_party/rust/serde_with/tests/with_prefix.rs @@ -0,0 +1,164 @@ +#![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 utils; + +use crate::utils::is_equal; +use alloc::collections::BTreeMap; +use core::iter::FromIterator; +use expect_test::expect; +use serde::{Deserialize, Serialize}; +use serde_with::with_prefix; +use std::collections::HashMap; + +#[test] +fn test_flatten_with_prefix() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Match { + #[serde(flatten, with = "prefix_player1")] + player1: Player, + #[serde(flatten, with = "prefix_player2")] + player2: Option, + #[serde(flatten, with = "prefix_player3")] + player3: Option, + #[serde(flatten, with = "prefix_tag")] + tags: HashMap, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Player { + name: String, + votes: u64, + } + + with_prefix!(prefix_player1 "player1_"); + with_prefix!(prefix_player2 "player2_"); + with_prefix!(prefix_player3 "player3_"); + with_prefix!(prefix_tag "tag_"); + + let m = Match { + player1: Player { + name: "name1".to_owned(), + votes: 1, + }, + player2: Some(Player { + name: "name2".to_owned(), + votes: 2, + }), + player3: None, + tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), + }; + + is_equal( + m, + expect![[r#" + { + "player1_name": "name1", + "player1_votes": 1, + "player2_name": "name2", + "player2_votes": 2, + "tag_t": "T" + }"#]], + ); +} + +#[test] +fn test_plain_with_prefix() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Match { + #[serde(with = "prefix_player1")] + player1: Player, + #[serde(with = "prefix_player2")] + player2: Option, + #[serde(with = "prefix_player3")] + player3: Option, + #[serde(with = "prefix_tag")] + tags: HashMap, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Player { + name: String, + votes: u64, + } + + with_prefix!(prefix_player1 "player1_"); + with_prefix!(prefix_player2 "player2_"); + with_prefix!(prefix_player3 "player3_"); + with_prefix!(prefix_tag "tag_"); + + let m = Match { + player1: Player { + name: "name1".to_owned(), + votes: 1, + }, + player2: Some(Player { + name: "name2".to_owned(), + votes: 2, + }), + player3: None, + tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), + }; + + is_equal( + m, + expect![[r#" + { + "player1": { + "player1_name": "name1", + "player1_votes": 1 + }, + "player2": { + "player2_name": "name2", + "player2_votes": 2 + }, + "player3": null, + "tags": { + "tag_t": "T" + } + }"#]], + ); +} + +/// Ensure that with_prefix works for unit type enum variants. +#[test] +fn test_enum_unit_variant_with_prefix() { + #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] + enum Foo { + One, + Two, + Three, + } + + #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] + struct Data { + stuff: String, + + #[serde(flatten, with = "foo")] + foo: BTreeMap, + } + with_prefix!(foo "foo_"); + + let d = Data { + stuff: "Stuff".to_owned(), + foo: BTreeMap::from_iter(vec![(Foo::One, 1), (Foo::Two, 2), (Foo::Three, 3)]), + }; + + is_equal( + d, + expect![[r#" + { + "stuff": "Stuff", + "foo_One": 1, + "foo_Two": 2, + "foo_Three": 3 + }"#]], + ); +} -- cgit v1.2.3