use core::ops::{Add, AddAssign, Sub, SubAssign};
use derive_more::derive::Deref;
use super::error::SfsError;
use crate::error::Error;
use crate::fs::error::FsError;
use crate::fs::types::{Time, Timespec};
#[derive(
Debug,
Clone,
Copy,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::derive::Add,
derive_more::derive::AddAssign,
derive_more::derive::Sub,
derive_more::derive::SubAssign,
)]
pub struct TimeStamp(i64);
impl From<i64> for TimeStamp {
fn from(value: i64) -> Self {
Self(value)
}
}
impl From<TimeStamp> for i64 {
fn from(value: TimeStamp) -> Self {
value.0
}
}
impl Add<i64> for TimeStamp {
type Output = Self;
fn add(self, rhs: i64) -> Self::Output {
Self(*self + rhs)
}
}
impl AddAssign<i64> for TimeStamp {
fn add_assign(&mut self, rhs: i64) {
*self = *self + rhs;
}
}
impl Sub<i64> for TimeStamp {
type Output = Self;
fn sub(self, rhs: i64) -> Self::Output {
Self(*self - rhs)
}
}
impl SubAssign<i64> for TimeStamp {
fn sub_assign(&mut self, rhs: i64) {
*self = *self - rhs;
}
}
impl From<TimeStamp> for Timespec {
fn from(value: TimeStamp) -> Self {
Self {
tv_sec: Time(value.0 / 65536),
tv_nsec: unsafe { u32::try_from(((value.0.abs() % 65536) * 1_000_000_000) / 65536).unwrap_unchecked() },
}
}
}
impl TryFrom<Timespec> for TimeStamp {
type Error = Error<SfsError>;
fn try_from(value: Timespec) -> Result<Self, Self::Error> {
Ok(Self(
value
.tv_sec
.0
.checked_mul(65536)
.and_then(|res| res.checked_add((i64::from(value.tv_nsec) * 65536) / 1_000_000_000))
.ok_or_else(|| Error::Fs(FsError::Implementation(SfsError::TimeStampOutOfBounds(value))))?,
))
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl TryFrom<std::time::SystemTime> for TimeStamp {
type Error = Error<SfsError>;
fn try_from(value: std::time::SystemTime) -> Result<Self, Self::Error> {
Timespec::from(value).try_into()
}
}
impl TimeStamp {
#[allow(clippy::doc_link_with_quotes)]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn now() -> Result<Self, Error<SfsError>> {
std::time::SystemTime::now().try_into()
}
}
#[cfg(test)]
mod test {
use crate::fs::sfs::time_stamp::TimeStamp;
use crate::fs::types::{Time, Timespec};
#[test]
fn i64_conversion() {
assert_eq!(0_i64, TimeStamp::from(0_i64).into());
assert_eq!(1_000_000_i64, TimeStamp::from(1_000_000_i64).into());
assert_eq!(-1_000_000_i64, TimeStamp::from(-1_000_000_i64).into());
}
#[test]
fn time_conversion() {
const YEAR_2000_TIMESTAMP: i64 = 946_684_800;
assert_eq!(
TimeStamp::from(YEAR_2000_TIMESTAMP * 65536),
(Timespec {
tv_sec: Time(YEAR_2000_TIMESTAMP),
tv_nsec: 0
})
.try_into()
.unwrap()
);
assert_eq!(
TimeStamp::from(YEAR_2000_TIMESTAMP * 65536 + 768),
(Timespec {
tv_sec: Time(YEAR_2000_TIMESTAMP),
tv_nsec: u32::try_from((768 * 1_000_000_000_u64) / 65536_u64).unwrap()
})
.try_into()
.unwrap()
);
assert_eq!(
Timespec {
tv_sec: Time(YEAR_2000_TIMESTAMP),
tv_nsec: 0
},
TimeStamp::from(YEAR_2000_TIMESTAMP * 65536).into()
);
assert_eq!(
Timespec {
tv_sec: Time(YEAR_2000_TIMESTAMP),
tv_nsec: 123_456 - 1386
},
TimeStamp::from(YEAR_2000_TIMESTAMP * 65536 + (123_456 * 65536) / 1_000_000_000).into()
);
}
#[test]
fn now_conversion() {
let timestamp_now = TimeStamp::now().unwrap();
let timespec_now = Timespec::now();
assert!(
timestamp_now <= TimeStamp::try_from(timespec_now).unwrap() + 1
&& TimeStamp::try_from(timespec_now).unwrap() <= timestamp_now + 1
);
assert!(
(Timespec::now()
- Timespec {
tv_sec: Time(0),
tv_nsec: 15259
})
<= Timespec::from(TimeStamp::now().unwrap())
&& Timespec::from(TimeStamp::now().unwrap()) <= Timespec::now() + Time(1)
);
}
}