use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::{Add, Mul, Sub};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SimTime(u64);
impl SimTime {
pub const fn zero() -> Self {
SimTime(0)
}
pub const fn from_nanos(nanos: u64) -> Self {
SimTime(nanos)
}
pub const fn from_micros(micros: u64) -> Self {
SimTime(micros * 1_000)
}
pub const fn from_millis(millis: u64) -> Self {
SimTime(millis * 1_000_000)
}
pub const fn from_secs(secs: u64) -> Self {
SimTime(secs * 1_000_000_000)
}
pub fn from_duration(duration: Duration) -> Self {
let nanos = u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX);
SimTime(nanos)
}
pub fn try_from_duration(duration: Duration) -> Option<Self> {
u64::try_from(duration.as_nanos()).ok().map(SimTime)
}
pub fn as_duration(&self) -> Duration {
Duration::from_nanos(self.0)
}
pub const fn as_nanos(&self) -> u64 {
self.0
}
pub fn duration_since(&self, earlier: SimTime) -> Duration {
Duration::from_nanos(self.0.saturating_sub(earlier.0))
}
pub fn checked_duration_since(&self, earlier: SimTime) -> Option<Duration> {
self.0.checked_sub(earlier.0).map(Duration::from_nanos)
}
pub fn add_duration(&self, duration: Duration) -> Self {
let nanos = duration.as_nanos();
let nanos = u64::try_from(nanos).unwrap_or(u64::MAX);
SimTime(self.0.saturating_add(nanos))
}
pub fn checked_add_duration(&self, duration: Duration) -> Option<Self> {
let nanos = u64::try_from(duration.as_nanos()).ok()?;
self.0.checked_add(nanos).map(SimTime)
}
pub fn sub_duration(&self, duration: Duration) -> Self {
let nanos = duration.as_nanos();
let nanos = u64::try_from(nanos).unwrap_or(u64::MAX);
SimTime(self.0.saturating_sub(nanos))
}
pub fn checked_sub_duration(&self, duration: Duration) -> Option<Self> {
let nanos = u64::try_from(duration.as_nanos()).ok()?;
self.0.checked_sub(nanos).map(SimTime)
}
}
impl Add<SimTime> for SimTime {
type Output = SimTime;
fn add(self, rhs: SimTime) -> Self::Output {
SimTime(self.0.saturating_add(rhs.0))
}
}
impl Add<Duration> for SimTime {
type Output = SimTime;
fn add(self, rhs: Duration) -> Self::Output {
self.add_duration(rhs)
}
}
impl Sub<Duration> for SimTime {
type Output = SimTime;
fn sub(self, rhs: Duration) -> Self::Output {
self.sub_duration(rhs)
}
}
impl Sub<SimTime> for SimTime {
type Output = Duration;
fn sub(self, rhs: SimTime) -> Self::Output {
self.duration_since(rhs)
}
}
impl Mul<u64> for SimTime {
type Output = SimTime;
fn mul(self, rhs: u64) -> Self::Output {
SimTime(self.0.saturating_mul(rhs))
}
}
impl Default for SimTime {
fn default() -> Self {
SimTime::zero()
}
}
impl From<f64> for SimTime {
fn from(secs: f64) -> Self {
if !secs.is_finite() {
panic!("SimTime cannot be created from non-finite value: {secs}");
}
if secs < 0.0 {
panic!("SimTime cannot be negative: {secs}");
}
const MAX_SECS: f64 = (u64::MAX as f64) / 1_000_000_000.0;
if secs > MAX_SECS {
panic!("SimTime value too large: {secs} seconds (max: {MAX_SECS} seconds)");
}
SimTime::from_nanos((secs * 1_000_000_000.0) as u64)
}
}
impl fmt::Display for SimTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let duration = self.as_duration();
let secs = duration.as_secs();
let millis = duration.subsec_millis();
let micros = duration.subsec_micros() % 1000;
let nanos = duration.subsec_nanos() % 1000;
if secs > 0 {
write!(f, "{secs}.{millis:03}s")
} else if millis > 0 {
write!(f, "{millis}.{micros:03}ms")
} else if micros > 0 {
write!(f, "{micros}.{nanos:03}µs")
} else {
write!(f, "{nanos}ns")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simtime_creation() {
assert_eq!(SimTime::zero().as_nanos(), 0);
assert_eq!(SimTime::from_nanos(1000).as_nanos(), 1000);
assert_eq!(SimTime::from_micros(1).as_nanos(), 1_000);
assert_eq!(SimTime::from_millis(1).as_nanos(), 1_000_000);
assert_eq!(SimTime::from_secs(1).as_nanos(), 1_000_000_000);
}
#[test]
fn test_simtime_arithmetic() {
let t1 = SimTime::from_millis(100);
let t2 = SimTime::from_millis(50);
let duration = Duration::from_millis(25);
assert_eq!(t1 + duration, SimTime::from_millis(125));
assert_eq!(t1 - duration, SimTime::from_millis(75));
assert_eq!(t1 - t2, Duration::from_millis(50));
}
#[test]
fn test_simtime_ordering() {
let t1 = SimTime::from_millis(100);
let t2 = SimTime::from_millis(200);
assert!(t1 < t2);
assert!(t2 > t1);
assert_eq!(t1, t1);
}
#[test]
fn test_simtime_from_f64() {
let t1 = SimTime::from(1.0); assert_eq!(t1.as_nanos(), 1_000_000_000);
let t2 = SimTime::from(0.5); assert_eq!(t2.as_nanos(), 500_000_000);
let t3 = SimTime::from(1.5); assert_eq!(t3.as_nanos(), 1_500_000_000);
let t4 = SimTime::from(0.000001); assert_eq!(t4.as_nanos(), 1_000);
}
#[test]
#[should_panic(expected = "SimTime cannot be negative")]
fn test_simtime_from_negative_f64() {
let _ = SimTime::from(-1.0);
}
#[test]
#[should_panic(expected = "SimTime cannot be created from non-finite value")]
fn test_simtime_from_infinite_f64() {
let _ = SimTime::from(f64::INFINITY);
}
#[test]
#[should_panic(expected = "SimTime cannot be created from non-finite value")]
fn test_simtime_from_nan_f64() {
let _ = SimTime::from(f64::NAN);
}
#[test]
#[should_panic(expected = "SimTime value too large")]
fn test_simtime_from_too_large_f64() {
let max_secs = (u64::MAX as f64) / 1_000_000_000.0;
let _ = SimTime::from(max_secs + 1.0);
}
#[test]
fn test_simtime_from_duration() {
let duration = Duration::from_nanos(1_500_000_000); let time = SimTime::from_duration(duration);
assert_eq!(time.as_nanos(), 1_500_000_000);
let large_duration = Duration::from_secs(u64::MAX); let time = SimTime::from_duration(large_duration);
assert_eq!(time.as_nanos(), u64::MAX);
}
#[test]
fn test_simtime_try_from_duration() {
let duration = Duration::from_nanos(1_500_000_000); let time = SimTime::try_from_duration(duration).unwrap();
assert_eq!(time.as_nanos(), 1_500_000_000);
let large_duration = Duration::from_secs(u64::MAX); assert_eq!(SimTime::try_from_duration(large_duration), None);
}
}