use super::ExtType;
use crate::{
decode::Error as DeError,
encode::{Error as SerError, SerializeIntoSlice},
Ext,
};
use byteorder::{BigEndian, ByteOrder};
use core::convert::{TryFrom, TryInto};
const EXT_TIMESTAMP: ExtType = ExtType(-1);
pub(crate) const TYPE_NAME: &'static str = "$Timestamp";
pub(crate) const FIELD_SECONDS_NAME: &'static str = "seconds";
pub(crate) const FIELD_NANOSECONDS_NAME: &'static str = "nanoseconds";
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "derive-debug"), derive(core::fmt::Debug))]
pub struct Timestamp {
seconds: i64,
nanoseconds: u32,
}
impl Timestamp {
pub const fn new(seconds: i64, nanoseconds: u32) -> Result<Timestamp, DeError> {
let mut seconds = seconds;
let mut nanoseconds = nanoseconds;
while nanoseconds >= 1_000_000_000 {
seconds += 1;
nanoseconds -= 1_000_000_000;
}
Ok(Timestamp { seconds, nanoseconds })
}
pub const fn seconds(&self) -> i64 { self.seconds }
pub const fn nanoseconds(&self) -> u32 { self.nanoseconds }
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_truncation)]
pub fn to_ext<'a>(&self, data: &'a mut [u8]) -> Result<Ext<'a>, SerError> {
if self.seconds >> 34 == 0 {
let x = (u64::from(self.nanoseconds) << 34) | self.seconds as u64;
if x & 0xffff_ffff_0000_0000_u64 == 0 {
if data.len() < 4 {
return Err(SerError::EndOfBuffer);
}
BigEndian::write_u32(data, x as u32);
Ok(Ext::new(-1, &data[0..4]))
} else {
if data.len() < 8 {
return Err(SerError::EndOfBuffer);
}
BigEndian::write_u64(data, x);
Ok(Ext::new(-1, &data[0..8]))
}
} else {
#[cfg(feature = "timestamp96")]
{
if data.len() < 12 {
return Err(SerError::EndOfBuffer);
}
BigEndian::write_u32(data, self.nanoseconds);
BigEndian::write_i64(&mut data[4..], self.seconds);
return Ok(Ext::new(-1, &data[0..12]));
}
#[cfg(not(feature = "timestamp96"))]
return Err(SerError::InvalidType);
}
}
}
impl SerializeIntoSlice for Timestamp {
fn write_into_slice(&self, buf: &mut [u8]) -> Result<usize, SerError> {
let mut tmp = [0; 12];
let ext = self.to_ext(&mut tmp)?;
crate::ext::serialize_ext(&ext, buf)
}
}
pub fn try_deserialize(buf: &[u8]) -> Result<Timestamp, DeError> { crate::ext::try_deserialize_ext(&buf)?.try_into() }
impl<'a> TryFrom<Ext<'a>> for Timestamp {
type Error = DeError;
#[allow(clippy::cast_possible_truncation)]
fn try_from(ext: Ext<'a>) -> Result<Self, Self::Error> {
if ext.typ == EXT_TIMESTAMP {
match ext.data.len() {
4 => {
Timestamp::new(i64::from(BigEndian::read_u32(&ext.data)), 0)
}
#[allow(clippy::cast_possible_wrap)]
8 => {
let value = BigEndian::read_u64(&ext.data);
Timestamp::new((value & 0x0000_0003_ffff_ffff_u64) as i64, (value >> 34) as u32)
}
#[cfg(feature = "timestamp96")]
12 => {
let nanos = BigEndian::read_u32(&ext.data[0..4]);
let s = BigEndian::read_i64(&ext.data[4..12]);
Timestamp::new(s, nanos)
}
_ => Err(DeError::InvalidType),
}
} else {
Err(DeError::InvalidType)
}
}
}
#[cfg(feature = "serde")]
impl ::serde::ser::Serialize for Timestamp {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct(TYPE_NAME, 2)?;
s.serialize_field(FIELD_SECONDS_NAME, &self.seconds)?;
s.serialize_field(FIELD_NANOSECONDS_NAME, &self.nanoseconds)?;
s.end()
}
}
#[cfg(feature = "serde")]
impl<'de> ::serde::de::Deserialize<'de> for Timestamp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: ::serde::de::Deserializer<'de> {
use crate::encode::Binary;
struct TimestampVisitor<'a>(core::marker::PhantomData<&'a ()>);
impl<'de: 'a, 'a> ::serde::de::Visitor<'de> for TimestampVisitor<'a> {
type Value = Timestamp;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a MsgPack ext data") }
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where A: serde::de::SeqAccess<'de> {
let typ: Option<ExtType> = seq.next_element()?;
let data: Option<Binary> = seq.next_element()?;
match (typ, data) {
(Some(typ), Some(data)) => Ok(Ext::new(typ.0, &data).try_into().map_err(::serde::de::Error::custom)?),
(Some(_), None) => Err(::serde::de::Error::custom("ext data not found")),
_ => Err(::serde::de::Error::custom("ext type field not found")),
}
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where V: ::serde::de::MapAccess<'de> {
enum Field {
Seconds,
Nanoseconds,
Type,
Data,
}
impl<'de> ::serde::de::Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
struct FieldVisitor;
impl<'de> ::serde::de::Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("`seconds`, `secs`, `s`, `nanoseconds`, `nanos`, `ns`, `type` or `data`")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: serde::de::Error {
match v {
"seconds" | "secs" | "s" => Ok(Field::Seconds),
"nanoseconds" | "nanos" | "ns" => Ok(Field::Nanoseconds),
"type" => Ok(Field::Type),
"data" => Ok(Field::Data),
_ => Err(::serde::de::Error::unknown_field(
v,
&["seconds", "secs", "s", "nanoseconds", "nanos", "ns", "type", "data"],
)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
let mut typ = None;
let mut data = None;
let mut seconds = None;
let mut nanoseconds = None;
loop {
match map.next_key::<Field>() {
Ok(Some(Field::Type)) => typ = Some(map.next_value::<ExtType>()?),
Ok(Some(Field::Data)) => data = Some(map.next_value::<Binary>()?),
Ok(Some(Field::Seconds)) => seconds = Some(map.next_value::<i64>()?),
Ok(Some(Field::Nanoseconds)) => nanoseconds = Some(map.next_value::<u32>()?),
Ok(None) => break, Err(_e) => {
map.next_value()?;
}
}
}
match (typ, data, seconds, nanoseconds) {
(Some(typ), Some(data), None, None) => Ok(Ext::new(typ.0, &data).try_into().map_err(::serde::de::Error::custom)?),
(None, None, Some(seconds), Some(nanoseconds)) => {
Ok(Timestamp::new(seconds, nanoseconds).map_err(::serde::de::Error::custom)?)
}
(None, None, Some(seconds), None) => Ok(Timestamp::new(seconds, 0).map_err(::serde::de::Error::custom)?),
(None, None, None, Some(nanoseconds)) => Ok(Timestamp::new(0, nanoseconds).map_err(::serde::de::Error::custom)?),
(Some(_), None, _, _) => Err(::serde::de::Error::custom("ext data not found")),
_ => Err(::serde::de::Error::custom("ext type field not found")),
}
}
}
static FIELDS: [&str; 2] = [FIELD_SECONDS_NAME, FIELD_NANOSECONDS_NAME];
deserializer.deserialize_struct(TYPE_NAME, &FIELDS, TimestampVisitor(core::marker::PhantomData))
}
}