use std::{
cmp::Ordering,
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
#[derive(Debug)]
pub struct TimestampStyleConversionError {
kind: TimestampStyleConversionErrorType,
}
impl TimestampStyleConversionError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &TimestampStyleConversionErrorType {
&self.kind
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(
self,
) -> (
TimestampStyleConversionErrorType,
Option<Box<dyn Error + Send + Sync>>,
) {
(self.kind, None)
}
}
impl Display for TimestampStyleConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
TimestampStyleConversionErrorType::StyleInvalid => {
f.write_str("given value is not a valid style")
}
}
}
}
impl Error for TimestampStyleConversionError {}
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum TimestampStyleConversionErrorType {
StyleInvalid,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Timestamp {
style: Option<TimestampStyle>,
unix: u64,
}
impl Timestamp {
#[must_use = "creating a timestamp does nothing on its own"]
pub const fn new(unix: u64, style: Option<TimestampStyle>) -> Self {
Self { style, unix }
}
#[must_use = "retrieving the style does nothing on its own"]
pub const fn style(&self) -> Option<TimestampStyle> {
self.style
}
#[must_use = "retrieving the unix timestamp does nothing on its own"]
pub const fn unix(&self) -> u64 {
self.unix
}
}
impl Ord for Timestamp {
fn cmp(&self, other: &Timestamp) -> Ordering {
self.unix.cmp(&other.unix)
}
}
impl PartialOrd for Timestamp {
fn partial_cmp(&self, other: &Timestamp) -> Option<Ordering> {
self.unix.partial_cmp(&other.unix)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TimestampStyle {
LongDateTime,
LongDate,
LongTime,
RelativeTime,
ShortDateTime,
ShortDate,
ShortTime,
}
impl TimestampStyle {
#[must_use = "retrieving the character of a style does nothing on its own"]
pub const fn style(self) -> &'static str {
match self {
Self::LongDateTime => "F",
Self::LongDate => "D",
Self::LongTime => "T",
Self::RelativeTime => "R",
Self::ShortDateTime => "f",
Self::ShortDate => "d",
Self::ShortTime => "t",
}
}
}
impl Display for TimestampStyle {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str(self.style())
}
}
impl TryFrom<&str> for TimestampStyle {
type Error = TimestampStyleConversionError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
"F" => Self::LongDateTime,
"D" => Self::LongDate,
"T" => Self::LongTime,
"R" => Self::RelativeTime,
"f" => Self::ShortDateTime,
"d" => Self::ShortDate,
"t" => Self::ShortTime,
_ => {
return Err(TimestampStyleConversionError {
kind: TimestampStyleConversionErrorType::StyleInvalid,
})
}
})
}
}
#[cfg(test)]
mod tests {
use super::{
Timestamp, TimestampStyle, TimestampStyleConversionError, TimestampStyleConversionErrorType,
};
use static_assertions::assert_impl_all;
use std::{cmp::Ordering, error::Error, fmt::Debug, hash::Hash};
assert_impl_all!(TimestampStyleConversionErrorType: Debug, Send, Sync);
assert_impl_all!(TimestampStyleConversionError: Debug, Error, Send, Sync);
assert_impl_all!(
TimestampStyle: Clone,
Copy,
Debug,
Eq,
Hash,
PartialEq,
Send,
Sync
);
assert_impl_all!(
Timestamp: Clone,
Copy,
Debug,
Eq,
Hash,
PartialEq,
Send,
Sync
);
const TIMESTAMP_OLD_STYLED: Timestamp = Timestamp::new(1, Some(TimestampStyle::RelativeTime));
const TIMESTAMP_OLD: Timestamp = Timestamp::new(1, None);
const TIMESTAMP_NEW_STYLED: Timestamp = Timestamp::new(2, Some(TimestampStyle::ShortDate));
const TIMESTAMP_NEW: Timestamp = Timestamp::new(2, None);
#[test]
fn timestamp_style_modifiers() {
assert_eq!("F", TimestampStyle::LongDateTime.style());
assert_eq!("D", TimestampStyle::LongDate.style());
assert_eq!("T", TimestampStyle::LongTime.style());
assert_eq!("R", TimestampStyle::RelativeTime.style());
assert_eq!("f", TimestampStyle::ShortDateTime.style());
assert_eq!("d", TimestampStyle::ShortDate.style());
assert_eq!("t", TimestampStyle::ShortTime.style());
}
#[test]
fn timestamp_style_try_from() -> Result<(), TimestampStyleConversionError> {
assert_eq!(TimestampStyle::try_from("F")?, TimestampStyle::LongDateTime);
assert_eq!(TimestampStyle::try_from("D")?, TimestampStyle::LongDate);
assert_eq!(TimestampStyle::try_from("T")?, TimestampStyle::LongTime);
assert_eq!(TimestampStyle::try_from("R")?, TimestampStyle::RelativeTime);
assert_eq!(
TimestampStyle::try_from("f")?,
TimestampStyle::ShortDateTime
);
assert_eq!(TimestampStyle::try_from("d")?, TimestampStyle::ShortDate);
assert_eq!(TimestampStyle::try_from("t")?, TimestampStyle::ShortTime);
Ok(())
}
#[test]
fn timestamp_cmp() {
assert!(TIMESTAMP_NEW > TIMESTAMP_OLD);
assert!(Timestamp::new(2, None).cmp(&TIMESTAMP_NEW) == Ordering::Equal);
assert!(TIMESTAMP_OLD < TIMESTAMP_NEW);
}
#[test]
fn timestamp_cmp_styles() {
assert!(TIMESTAMP_NEW_STYLED > TIMESTAMP_OLD);
assert!(TIMESTAMP_NEW > TIMESTAMP_OLD_STYLED);
assert!(TIMESTAMP_NEW_STYLED > TIMESTAMP_OLD_STYLED);
assert!(TIMESTAMP_NEW_STYLED.cmp(&TIMESTAMP_NEW) == Ordering::Equal);
assert!(
Timestamp::new(2, Some(TimestampStyle::RelativeTime)).cmp(&TIMESTAMP_NEW_STYLED)
== Ordering::Equal
);
assert!(TIMESTAMP_OLD_STYLED < TIMESTAMP_NEW);
assert!(TIMESTAMP_OLD < TIMESTAMP_NEW_STYLED);
assert!(TIMESTAMP_OLD_STYLED < TIMESTAMP_NEW_STYLED);
}
}