use crate::Duration;
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct Time {
pub(crate) inner: cyclonedds_sys::dds_time_t,
}
impl Time {
pub const NEVER: Self = Time {
inner: cyclonedds_sys::TIME_NEVER,
};
#[must_use]
pub const fn from_nanos(nanos: i64) -> Self {
Self { inner: nanos }
}
#[must_use]
pub const fn from_millis(millis: i64) -> Self {
Self {
inner: millis * 1_000_000,
}
}
#[must_use]
pub const fn from_secs(secs: i64) -> Self {
Self {
inner: secs * 1_000_000_000,
}
}
#[must_use]
pub const fn as_nanos(&self) -> i64 {
self.inner
}
pub fn elapsed(&self) -> crate::Result<Duration> {
Time::try_from(std::time::SystemTime::now()).and_then(|now| {
if self.inner <= now.inner {
let nanos = now.inner - self.inner;
Ok(Duration::from_nanos(nanos))
} else {
Err(crate::Error::BadParameter)
}
})
}
#[must_use]
pub fn checked_add(&self, duration: Duration) -> Option<Self> {
self.inner
.checked_add(duration.inner)
.map(|inner| Self { inner })
}
#[must_use]
pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
self.inner
.checked_sub(duration.inner)
.map(|inner| Self { inner })
}
}
impl TryFrom<std::time::SystemTime> for Time {
type Error = crate::Error;
fn try_from(value: std::time::SystemTime) -> Result<Self, Self::Error> {
let value = value
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map_err(|_err| crate::Error::BadParameter)?;
let inner = cyclonedds_sys::dds_time_t::try_from(value.as_nanos())
.map_err(|_err| crate::Error::BadParameter)?;
if inner == Self::NEVER.inner {
Err(crate::Error::BadParameter)
} else {
Ok(Self { inner })
}
}
}
impl From<Time> for std::time::SystemTime {
fn from(time: Time) -> Self {
std::time::SystemTime::UNIX_EPOCH
+ std::time::Duration::from_nanos(time.inner.cast_unsigned())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Duration;
#[test]
fn test_time_create() {
let nanos = 1_000_000_000;
let time = Time::from_nanos(nanos);
assert_eq!(time.inner, nanos);
}
#[test]
fn test_time_from_std_system_time() {
let nanos = 1_000_000_000;
let standard =
std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos as u64);
let time = Time::try_from(standard).unwrap();
assert_eq!(time.inner, nanos);
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_time_from_never_std_system_time() {
let nanos = Time::NEVER.inner as u64;
let standard = std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos);
let result = Time::try_from(standard).unwrap_err();
assert_eq!(result, crate::Error::BadParameter);
}
#[test]
fn test_time_from_out_of_range_std_system_time() {
let standard = std::time::SystemTime::UNIX_EPOCH - std::time::Duration::from_nanos(100);
let result = Time::try_from(standard).unwrap_err();
assert_eq!(result, crate::Error::BadParameter);
let nanos = u64::MAX;
let standard = std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos);
let result = Time::try_from(standard).unwrap_err();
assert_eq!(result, crate::Error::BadParameter);
}
#[test]
fn test_time_from_millis() {
let time = Time::from_millis(1_000);
assert_eq!(time.inner, 1_000_000_000);
}
#[test]
fn test_time_from_secs() {
let time = Time::from_secs(1);
assert_eq!(time.inner, 1_000_000_000);
}
#[test]
fn test_time_as_nanos() {
let time = Time::from_nanos(1_000_000_000);
assert_eq!(time.as_nanos(), 1_000_000_000);
}
#[test]
fn test_time_checked_add() {
let result = Time::from_secs(1).checked_add(Duration::from_secs(2));
assert_eq!(result, Some(Time::from_secs(3)));
}
#[test]
fn test_time_checked_add_overflow() {
let result = Time::from_nanos(i64::MAX).checked_add(Duration::from_nanos(1));
assert_eq!(result, None);
}
#[test]
fn test_time_checked_sub() {
let result = Time::from_secs(3).checked_sub(Duration::from_secs(1));
assert_eq!(result, Some(Time::from_secs(2)));
}
#[test]
fn test_time_checked_sub_underflow() {
let result = Time::from_nanos(i64::MIN).checked_sub(Duration::from_nanos(1));
assert_eq!(result, None);
}
#[test]
fn test_time_elapsed_increases() {
let before = Time::try_from(std::time::SystemTime::now()).unwrap();
std::thread::sleep(std::time::Duration::from_millis(10));
let elapsed = before.elapsed().unwrap();
assert!(elapsed.as_nanos() >= 10_000_000);
}
#[test]
fn test_time_elapsed_future_returns_error() {
let future = Time::from_nanos(i64::MAX);
assert_eq!(future.elapsed().unwrap_err(), crate::Error::BadParameter);
}
#[test]
fn test_to_std_system_time() {
let nanos = 11_222_33;
let time = Time::from_nanos(nanos);
let expected =
std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos as u64);
let actual: std::time::SystemTime = time.into();
assert_eq!(actual, expected);
}
}