greg 0.2.5

Simple Unobtrusive Date & Time library
Documentation
use std::fmt;

use serde::{
	Serialize,
	Serializer,
	Deserialize,
	Deserializer
};
use serde::de::{
	Error,
	Visitor,
};

use super::{
	Point,
	Span,
};

macro_rules! serialize_field {
	($Type:ty, $field:ident) => {
		impl Serialize for $Type {
			fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
				where S: Serializer
			{
				self.$field.serialize(serializer)
			}
		}
	}
}

macro_rules! deserialize {
	(
		$Type:ty,
		$Visitor:ident,
		$expect:literal,
		$f:ident,
		$( $methods:item )+ ) =>
	{
		struct $Visitor;

		impl<'de,> Visitor<'de> for $Visitor {
			type Value = $Type;

			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
				formatter.write_str($expect)
			}
			$( $methods )+
		}

		impl<'de> Deserialize<'de> for $Type {
			fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
				where D: Deserializer<'de>
			{
				deserializer.$f($Visitor)
			}
		}
	}
}

/*
 *	POINT
 */

serialize_field!(Point, timestamp);
deserialize!(
	Point,
	PointVisitor,
	"a 64-bit timestamp",
	deserialize_i64,
	fn visit_i64<E: Error>(self, value: i64) -> Result<Self::Value, E> {
		Ok(Point::from_epoch(value))
	}
);

/*
 *	SPAN
 */

serialize_field!(Span, seconds);
deserialize!(
	Span,
	SpanVisitor,
	"a 64-bit duration in seconds",
	deserialize_u64,
	fn visit_u64<E: Error>(self, value: u64) -> Result<Self::Value, E> {
		Ok(Span::from_seconds(value))
	}
	fn visit_i64<E: Error>(self, value: i64) -> Result<Self::Value, E> {
		value.try_into()
			.map(Span::from_seconds)
			.map_err(E::custom)
	}
);

#[cfg(test)]
mod tests {
	use std::fmt;
	use serde::de::value;
	use super::*;

	/// [`fmt::Formatter`] is the only [`Serializer`] without extra deps
	struct FmtAbuser<T>(T);
	impl<T: Serialize> fmt::Display for FmtAbuser<T> {
		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
			self.0.serialize(f)
		}
	}
	macro_rules! assert_serialize_eq {
		($a:expr, $b:expr) => {
			assert_eq!(
				FmtAbuser($a).to_string(),
				FmtAbuser($b).to_string()
			);
		};
	}

	#[test]
	fn point() {
		type Ser = value::I64Deserializer<value::Error>;

		let cases = [
			i64::MIN,
			i32::MIN.into(),
			0,
			Point::now().timestamp,
			i32::MAX.into(),
			u32::MAX.into(),
			i64::MAX
		];
		for case in cases {
			let before = Point::from_epoch(case);
			let after = Point::deserialize(Ser::new(case)).unwrap();
			assert_eq!(before, after);

			assert_serialize_eq!(before, case);
		}
	}

	#[test]
	fn span() {
		type Ser = value::U64Deserializer<value::Error>;

		let cases = [
			Span::ZERO,
			Span::SECOND,
			Span::MINUTE,
			Span::HOUR,
			Span::DAY,
			Span::WEEK,
			Span::from_seconds(u32::MAX.into()),
			Span::from_seconds(u64::MAX),
		];
		for case in cases {
			let after = Span::deserialize(Ser::new(case.seconds)).unwrap();
			assert_eq!(case, after);

			assert_serialize_eq!(case, case.seconds);
		}
	}
}