From c47eff7fcc654a04785e42c1c2d10d114a48f615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristofer=20Karlstr=C3=B6m?= Date: Mon, 7 Oct 2024 07:08:53 +0200 Subject: [PATCH 1/2] Added tests without chrono enabled --- tests/generation.rs | 15 ++++- tests/nullable.rs | 28 +++++++++- tests/schemas/chrono_logical_dates.avsc | 17 ++++++ tests/schemas/chrono_logical_dates.rs | 19 +++++++ tests/schemas/logical_dates.rs | 13 ++--- tests/schemas/mod.rs | 2 + .../nullable_chrono_logical_dates.avsc | 18 ++++++ .../schemas/nullable_chrono_logical_dates.rs | 56 +++++++++++++++++++ tests/schemas/nullable_logical_dates.rs | 26 ++++----- 9 files changed, 166 insertions(+), 28 deletions(-) create mode 100644 tests/schemas/chrono_logical_dates.avsc create mode 100644 tests/schemas/chrono_logical_dates.rs create mode 100644 tests/schemas/nullable_chrono_logical_dates.avsc create mode 100644 tests/schemas/nullable_chrono_logical_dates.rs diff --git a/tests/generation.rs b/tests/generation.rs index 4d34c27..87f6e0b 100644 --- a/tests/generation.rs +++ b/tests/generation.rs @@ -127,6 +127,14 @@ fn gen_nullable_bytes() { fn gen_nullable_logical_dates() { validate_generation( "nullable_logical_dates", + Generator::builder().nullable(true).build().unwrap(), + ); +} + +#[test] +fn gen_nullable_chrono_logical_dates() { + validate_generation( + "nullable_chrono_logical_dates", Generator::builder() .nullable(true) .use_chrono_dates(true) @@ -142,8 +150,13 @@ fn gen_decimals() { #[test] fn gen_logical_dates() { + validate_generation("logical_dates", Generator::builder().build().unwrap()); +} + +#[test] +fn gen_chrono_logical_dates() { validate_generation( - "logical_dates", + "chrono_logical_dates", Generator::builder().use_chrono_dates(true).build().unwrap(), ); } diff --git a/tests/nullable.rs b/tests/nullable.rs index 51f80b6..4d73b14 100644 --- a/tests/nullable.rs +++ b/tests/nullable.rs @@ -37,12 +37,13 @@ fn deser_nullable_bytes() { } #[test] -fn deser_nullable_logical_dates() { +fn deser_nullable_chrono_logical_dates() { let serialized = r#"{"birthday":null,"meeting_time":null,"release_datetime_micro":1681601301000000}"#; - let val = serde_json::from_str::(serialized) - .unwrap(); + let val = + serde_json::from_str::(serialized) + .unwrap(); assert!( val.birthday == chrono::DateTime::::from_timestamp(1681601653, 0).unwrap(), "Should use schema-defined default value when null" @@ -57,3 +58,24 @@ fn deser_nullable_logical_dates() { "Deserialized value is different from payload value" ); } + +#[test] +fn deser_nullable_logical_dates() { + let serialized = + r#"{"birthday":null,"meeting_time":null,"release_datetime_micro":1681601301000000}"#; + + let val = serde_json::from_str::(serialized) + .unwrap(); + assert!( + val.birthday == 1681601653, + "Should use schema-defined default value when null" + ); + assert!( + val.meeting_time.is_none(), + "Schema-defined optional should remain optional in Rust" + ); + assert!( + val.release_datetime_micro == 1681601301000000, + "Deserialized value is different from payload value" + ); +} diff --git a/tests/schemas/chrono_logical_dates.avsc b/tests/schemas/chrono_logical_dates.avsc new file mode 100644 index 0000000..8b9c254 --- /dev/null +++ b/tests/schemas/chrono_logical_dates.avsc @@ -0,0 +1,17 @@ +{ + "type": "record", + "name": "DateLogicalType", + "fields": [ { + "name": "birthday", + "type": {"type": "int", "logicalType": "date"} + }, { + "name": "meeting_time", + "type": ["null", {"type": "long", "logicalType": "timestamp-millis"}], + "default": null + }, { + "name": "release_datetime_micro", + "type": {"type": "long", "logicalType": "timestamp-micros"}, + "default": 1570903062000000 + } ], + "doc": "Date type" +} diff --git a/tests/schemas/chrono_logical_dates.rs b/tests/schemas/chrono_logical_dates.rs new file mode 100644 index 0000000..ad37f6e --- /dev/null +++ b/tests/schemas/chrono_logical_dates.rs @@ -0,0 +1,19 @@ + +/// Date type +#[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] +pub struct DateLogicalType { + #[serde(with = "chrono::serde::ts_seconds")] + pub birthday: chrono::DateTime, + #[serde(with = "chrono::serde::ts_milliseconds_option")] + #[serde(default = "default_datelogicaltype_meeting_time")] + pub meeting_time: Option>, + #[serde(with = "chrono::serde::ts_microseconds")] + #[serde(default = "default_datelogicaltype_release_datetime_micro")] + pub release_datetime_micro: chrono::DateTime, +} + +#[inline(always)] +fn default_datelogicaltype_meeting_time() -> Option> { None } + +#[inline(always)] +fn default_datelogicaltype_release_datetime_micro() -> chrono::DateTime { chrono::DateTime::::from_timestamp_micros(1570903062000000).unwrap() } diff --git a/tests/schemas/logical_dates.rs b/tests/schemas/logical_dates.rs index ad37f6e..b6c5ad8 100644 --- a/tests/schemas/logical_dates.rs +++ b/tests/schemas/logical_dates.rs @@ -2,18 +2,15 @@ /// Date type #[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] pub struct DateLogicalType { - #[serde(with = "chrono::serde::ts_seconds")] - pub birthday: chrono::DateTime, - #[serde(with = "chrono::serde::ts_milliseconds_option")] + pub birthday: i32, #[serde(default = "default_datelogicaltype_meeting_time")] - pub meeting_time: Option>, - #[serde(with = "chrono::serde::ts_microseconds")] + pub meeting_time: Option, #[serde(default = "default_datelogicaltype_release_datetime_micro")] - pub release_datetime_micro: chrono::DateTime, + pub release_datetime_micro: i64, } #[inline(always)] -fn default_datelogicaltype_meeting_time() -> Option> { None } +fn default_datelogicaltype_meeting_time() -> Option { None } #[inline(always)] -fn default_datelogicaltype_release_datetime_micro() -> chrono::DateTime { chrono::DateTime::::from_timestamp_micros(1570903062000000).unwrap() } +fn default_datelogicaltype_release_datetime_micro() -> i64 { 1570903062000000 } diff --git a/tests/schemas/mod.rs b/tests/schemas/mod.rs index 7db547e..0d01270 100644 --- a/tests/schemas/mod.rs +++ b/tests/schemas/mod.rs @@ -9,6 +9,7 @@ pub mod enums_sanitize; pub mod fixed; pub mod interop; pub mod logical_dates; +pub mod chrono_logical_dates; pub mod map_default; pub mod map_multiple_def; pub mod mono_valued_union; @@ -22,6 +23,7 @@ pub mod nested_record_partial_default; pub mod nullable; pub mod nullable_bytes; pub mod nullable_logical_dates; +pub mod nullable_chrono_logical_dates; pub mod optional_array; pub mod optional_arrays; pub mod record; diff --git a/tests/schemas/nullable_chrono_logical_dates.avsc b/tests/schemas/nullable_chrono_logical_dates.avsc new file mode 100644 index 0000000..85a143e --- /dev/null +++ b/tests/schemas/nullable_chrono_logical_dates.avsc @@ -0,0 +1,18 @@ +{ + "type": "record", + "name": "DateLogicalType", + "fields": [ { + "name": "birthday", + "type": {"type": "int", "logicalType": "date"}, + "default": 1681601653 + }, { + "name": "meeting_time", + "type": ["null", {"type": "long", "logicalType": "timestamp-millis"}], + "default": null + }, { + "name": "release_datetime_micro", + "type": {"type": "long", "logicalType": "timestamp-micros"}, + "default": 1570903062000000 + } ], + "doc": "Date type" +} diff --git a/tests/schemas/nullable_chrono_logical_dates.rs b/tests/schemas/nullable_chrono_logical_dates.rs new file mode 100644 index 0000000..73eca6b --- /dev/null +++ b/tests/schemas/nullable_chrono_logical_dates.rs @@ -0,0 +1,56 @@ + +/// Date type +#[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct DateLogicalType { + #[serde(deserialize_with = "nullable_datelogicaltype_birthday")] + #[serde(serialize_with = "chrono::serde::ts_seconds::serialize")] + pub birthday: chrono::DateTime, + pub meeting_time: Option>, + #[serde(deserialize_with = "nullable_datelogicaltype_release_datetime_micro")] + #[serde(serialize_with = "chrono::serde::ts_microseconds::serialize")] + pub release_datetime_micro: chrono::DateTime, +} + +#[inline(always)] +fn nullable_datelogicaltype_birthday<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + #[derive(serde::Deserialize)] + struct Wrapper(#[serde(with = "chrono::serde::ts_seconds")] chrono::DateTime); + let opt = Option::::deserialize(deserializer)?.map(|w| w.0); + Ok(opt.unwrap_or_else(|| default_datelogicaltype_birthday() )) +} + +#[inline(always)] +fn nullable_datelogicaltype_release_datetime_micro<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + #[derive(serde::Deserialize)] + struct Wrapper(#[serde(with = "chrono::serde::ts_microseconds")] chrono::DateTime); + let opt = Option::::deserialize(deserializer)?.map(|w| w.0); + Ok(opt.unwrap_or_else(|| default_datelogicaltype_release_datetime_micro() )) +} + +#[inline(always)] +fn default_datelogicaltype_birthday() -> chrono::DateTime { chrono::DateTime::::from_timestamp(1681601653, 0).unwrap() } + +#[inline(always)] +fn default_datelogicaltype_meeting_time() -> Option> { None } + +#[inline(always)] +fn default_datelogicaltype_release_datetime_micro() -> chrono::DateTime { chrono::DateTime::::from_timestamp_micros(1570903062000000).unwrap() } + +impl Default for DateLogicalType { + fn default() -> DateLogicalType { + DateLogicalType { + birthday: default_datelogicaltype_birthday(), + meeting_time: default_datelogicaltype_meeting_time(), + release_datetime_micro: default_datelogicaltype_release_datetime_micro(), + } + } +} diff --git a/tests/schemas/nullable_logical_dates.rs b/tests/schemas/nullable_logical_dates.rs index 73eca6b..41a8c41 100644 --- a/tests/schemas/nullable_logical_dates.rs +++ b/tests/schemas/nullable_logical_dates.rs @@ -4,46 +4,40 @@ #[serde(default)] pub struct DateLogicalType { #[serde(deserialize_with = "nullable_datelogicaltype_birthday")] - #[serde(serialize_with = "chrono::serde::ts_seconds::serialize")] - pub birthday: chrono::DateTime, - pub meeting_time: Option>, + pub birthday: i32, + pub meeting_time: Option, #[serde(deserialize_with = "nullable_datelogicaltype_release_datetime_micro")] - #[serde(serialize_with = "chrono::serde::ts_microseconds::serialize")] - pub release_datetime_micro: chrono::DateTime, + pub release_datetime_micro: i64, } #[inline(always)] -fn nullable_datelogicaltype_birthday<'de, D>(deserializer: D) -> Result, D::Error> +fn nullable_datelogicaltype_birthday<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::Deserialize; - #[derive(serde::Deserialize)] - struct Wrapper(#[serde(with = "chrono::serde::ts_seconds")] chrono::DateTime); - let opt = Option::::deserialize(deserializer)?.map(|w| w.0); + let opt = Option::deserialize(deserializer)?; Ok(opt.unwrap_or_else(|| default_datelogicaltype_birthday() )) } #[inline(always)] -fn nullable_datelogicaltype_release_datetime_micro<'de, D>(deserializer: D) -> Result, D::Error> +fn nullable_datelogicaltype_release_datetime_micro<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::Deserialize; - #[derive(serde::Deserialize)] - struct Wrapper(#[serde(with = "chrono::serde::ts_microseconds")] chrono::DateTime); - let opt = Option::::deserialize(deserializer)?.map(|w| w.0); + let opt = Option::deserialize(deserializer)?; Ok(opt.unwrap_or_else(|| default_datelogicaltype_release_datetime_micro() )) } #[inline(always)] -fn default_datelogicaltype_birthday() -> chrono::DateTime { chrono::DateTime::::from_timestamp(1681601653, 0).unwrap() } +fn default_datelogicaltype_birthday() -> i32 { 1681601653 } #[inline(always)] -fn default_datelogicaltype_meeting_time() -> Option> { None } +fn default_datelogicaltype_meeting_time() -> Option { None } #[inline(always)] -fn default_datelogicaltype_release_datetime_micro() -> chrono::DateTime { chrono::DateTime::::from_timestamp_micros(1570903062000000).unwrap() } +fn default_datelogicaltype_release_datetime_micro() -> i64 { 1570903062000000 } impl Default for DateLogicalType { fn default() -> DateLogicalType { From f380bf4afd45df70abb8852aba3e659796e911ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristofer=20Karlstr=C3=B6m?= Date: Mon, 7 Oct 2024 07:13:28 +0200 Subject: [PATCH 2/2] fix: Only serialize using chrono if chrono dates is enabled --- src/templates.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/templates.rs b/src/templates.rs index 049ee1d..7861269 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -889,6 +889,7 @@ impl Templater { union.variants()[1], Schema::TimestampMillis | Schema::LocalTimestampMillis ) + && self.use_chrono_dates { w.insert(name_std.clone(), "chrono::serde::ts_milliseconds_option"); } else if union.is_nullable() @@ -897,6 +898,7 @@ impl Templater { union.variants()[1], Schema::TimestampMicros | Schema::LocalTimestampMicros ) + && self.use_chrono_dates { w.insert(name_std.clone(), "chrono::serde::ts_microseconds_option"); } else if union.is_nullable() @@ -905,6 +907,7 @@ impl Templater { union.variants()[1], Schema::TimestampNanos | Schema::LocalTimestampNanos ) + && self.use_chrono_dates { w.insert(name_std.clone(), "chrono::serde::ts_nanoseconds_option"); };