Skip to content

Commit

Permalink
Add with::option_time_offset_date_time_as_timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
bouzuya committed Dec 31, 2023
1 parent ab487df commit d2d6854
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/with.rs
Expand Up @@ -78,6 +78,8 @@ pub mod lat_lng;
pub mod option_chrono_date_time_as_timestamp;
pub mod option_lat_lng;
pub mod option_string_as_reference;
#[cfg(feature = "time")]
pub mod option_time_offset_date_time_as_timestamp;
pub mod option_timestamp;
pub mod string_as_reference;
#[cfg(feature = "time")]
Expand Down
108 changes: 108 additions & 0 deletions src/with/option_time_offset_date_time_as_timestamp.rs
@@ -0,0 +1,108 @@
//! (De)serialize `Option<time::OffsetDateTime>` as `timestampValue`.

use prost_types::Timestamp;

/// Deserialize `Option<time::OffsetDateTime>` from `timestampValue` or `nullValue
///
/// # Example
///
/// ```rust
/// # fn main() -> anyhow::Result<()> {
/// use google_api_proto::google::firestore::v1::{value::ValueType, Value};
/// use prost_types::Timestamp;
/// use serde_firestore_value::{from_value, with::option_time_offset_date_time_as_timestamp};
///
/// #[derive(Debug, Eq, PartialEq, serde::Deserialize)]
/// struct S(
/// #[serde(deserialize_with = "option_time_offset_date_time_as_timestamp::deserialize")]
/// Option<time::OffsetDateTime>,
/// );
///
/// let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
/// 1_000_000_002_i128,
/// )?));
/// let v = Value {
/// value_type: Some(ValueType::TimestampValue(Timestamp {
/// seconds: 1_i64,
/// nanos: 2_i32,
/// })),
/// };
/// let d = from_value::<'_, S>(&v)?;
/// assert_eq!(d, o);
///
/// let o = S(None);
/// let v = Value {
/// value_type: Some(ValueType::NullValue(0)),
/// };
/// let d = from_value::<'_, S>(&v)?;
/// assert_eq!(d, o);
/// # Ok(())
/// # }
/// ```
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<time::OffsetDateTime>, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(
crate::with::option_timestamp::deserialize(deserializer)?.map(
|Timestamp { seconds, nanos }| {
time::OffsetDateTime::from_unix_timestamp_nanos(
i128::from(seconds) * 1_000_000_000_i128 + i128::from(nanos),
)
.expect("timestamp")
},
),
)
}

/// Serialize `Option<time::OffsetDateTime>` as `timestampValue` or `nullValue`.
///
/// # Example
///
/// ```rust
/// # fn main() -> anyhow::Result<()> {
/// use google_api_proto::google::firestore::v1::{value::ValueType, Value};
/// use prost_types::Timestamp;
/// use serde_firestore_value::{to_value, with::option_time_offset_date_time_as_timestamp};
///
/// #[derive(Debug, Eq, PartialEq, serde::Serialize)]
/// struct S(
/// #[serde(serialize_with = "option_time_offset_date_time_as_timestamp::serialize")]
/// Option<time::OffsetDateTime>,
/// );
///
/// let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
/// 1_000_000_002_i128,
/// )?));
/// let v = Value {
/// value_type: Some(ValueType::TimestampValue(Timestamp {
/// seconds: 1_i64,
/// nanos: 2_i32,
/// })),
/// };
/// let s = to_value(&o)?;
/// assert_eq!(s, v);
///
/// let o = S(None);
/// let v = Value {
/// value_type: Some(ValueType::NullValue(0)),
/// };
/// let s = to_value(&o)?;
/// assert_eq!(s, v);
/// # Ok(())
/// # }
/// ```
pub fn serialize<S>(
option_offset_date_time: &Option<time::OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let option_timestamp = option_offset_date_time.map(|offset_date_time| Timestamp {
seconds: offset_date_time.unix_timestamp(),
nanos: i32::try_from(offset_date_time.unix_timestamp_nanos() % 1_000_000_000_i128)
.expect("nanos"),
});
crate::with::option_timestamp::serialize(&option_timestamp, serializer)
}
100 changes: 100 additions & 0 deletions tests/v0-3-0.rs
Expand Up @@ -105,3 +105,103 @@ mod chrono_feature {
Ok(())
}
}

#[cfg(feature = "time")]
mod time_feature {
use google_api_proto::google::firestore::v1::{value::ValueType, Value};
use prost_types::Timestamp;
use serde_firestore_value::{
from_value, to_value, with::option_time_offset_date_time_as_timestamp,
};

#[test]
fn test_deserialize_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Deserialize)]
struct S(
#[serde(deserialize_with = "option_time_offset_date_time_as_timestamp::deserialize")]
Option<time::OffsetDateTime>,
);

let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
nanos: 2_i32,
})),
};
let d = from_value::<'_, S>(&v)?;
assert_eq!(d, o);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let d = from_value::<'_, S>(&v)?;
assert_eq!(d, o);
Ok(())
}

#[test]
fn test_serialize_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Serialize)]
struct S(
#[serde(serialize_with = "option_time_offset_date_time_as_timestamp::serialize")]
Option<time::OffsetDateTime>,
);

let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
nanos: 2_i32,
})),
};
let s = to_value(&o)?;
assert_eq!(s, v);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let s = to_value(&o)?;
assert_eq!(s, v);
Ok(())
}

#[test]
fn test_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
struct S(
#[serde(with = "option_time_offset_date_time_as_timestamp")]
Option<time::OffsetDateTime>,
);

let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
nanos: 2_i32,
})),
};
let s = to_value(&o)?;
let d = from_value::<'_, S>(&s)?;
assert_eq!(s, v);
assert_eq!(d, o);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let s = to_value(&o)?;
let d = from_value::<'_, S>(&s)?;
assert_eq!(s, v);
assert_eq!(d, o);
Ok(())
}
}
52 changes: 40 additions & 12 deletions tests/with_time_offset_date_time_as_timestamp.rs
Expand Up @@ -2,19 +2,21 @@
mod time_feature {
use google_api_proto::google::firestore::v1::{value::ValueType, Value};
use prost_types::Timestamp;
use serde_firestore_value::{from_value, to_value, with::time_offset_date_time_as_timestamp};
use serde_firestore_value::{
from_value, to_value, with::option_time_offset_date_time_as_timestamp,
};

#[test]
fn test_deserialize_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Deserialize)]
struct S(
#[serde(deserialize_with = "time_offset_date_time_as_timestamp::deserialize")]
time::OffsetDateTime,
#[serde(deserialize_with = "option_time_offset_date_time_as_timestamp::deserialize")]
Option<time::OffsetDateTime>,
);

let o = S(time::OffsetDateTime::from_unix_timestamp_nanos(
let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?);
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
Expand All @@ -23,20 +25,27 @@ mod time_feature {
};
let d = from_value::<'_, S>(&v)?;
assert_eq!(d, o);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let d = from_value::<'_, S>(&v)?;
assert_eq!(d, o);
Ok(())
}

#[test]
fn test_serialize_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Serialize)]
struct S(
#[serde(serialize_with = "time_offset_date_time_as_timestamp::serialize")]
time::OffsetDateTime,
#[serde(serialize_with = "option_time_offset_date_time_as_timestamp::serialize")]
Option<time::OffsetDateTime>,
);

let o = S(time::OffsetDateTime::from_unix_timestamp_nanos(
let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?);
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
Expand All @@ -45,17 +54,27 @@ mod time_feature {
};
let s = to_value(&o)?;
assert_eq!(s, v);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let s = to_value(&o)?;
assert_eq!(s, v);
Ok(())
}

#[test]
fn test_with() -> anyhow::Result<()> {
#[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
struct S(#[serde(with = "time_offset_date_time_as_timestamp")] time::OffsetDateTime);
struct S(
#[serde(with = "option_time_offset_date_time_as_timestamp")]
Option<time::OffsetDateTime>,
);

let o = S(time::OffsetDateTime::from_unix_timestamp_nanos(
let o = S(Some(time::OffsetDateTime::from_unix_timestamp_nanos(
1_000_000_002_i128,
)?);
)?));
let v = Value {
value_type: Some(ValueType::TimestampValue(Timestamp {
seconds: 1_i64,
Expand All @@ -66,6 +85,15 @@ mod time_feature {
let d = from_value::<'_, S>(&s)?;
assert_eq!(s, v);
assert_eq!(d, o);

let o = S(None);
let v = Value {
value_type: Some(ValueType::NullValue(0)),
};
let s = to_value(&o)?;
let d = from_value::<'_, S>(&s)?;
assert_eq!(s, v);
assert_eq!(d, o);
Ok(())
}
}

0 comments on commit d2d6854

Please sign in to comment.