use crate::time::{Time,Military};
use crate::macros::{
return_bad_float,impl_impl_math,impl_math,
};
#[cfg(feature = "num")]
use crate::num::Unsigned;
#[cfg(feature = "up")]
use crate::up::{Uptime,Htop,UptimeFull};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TimeUnit {
unknown: bool,
inner: u32,
years: u8,
months: u8,
weeks: u8,
days: u8,
hours: u8,
minutes: u8,
seconds: u8,
}
impl_math!(TimeUnit, u32);
impl TimeUnit {
pub const UNKNOWN: Self = Self { inner: 0, unknown: true, years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0 };
pub const ZERO: Self = Self { inner: 0, unknown: false, years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0 };
pub const SECOND: Self = Self { inner: 1, unknown: false, years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 1 };
pub const MINUTE: Self = Self { inner: 60, unknown: false, years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 1, seconds: 0 };
pub const HOUR: Self = Self { inner: 3600, unknown: false, years: 0, months: 0, weeks: 0, days: 0, hours: 1, minutes: 0, seconds: 0 };
pub const DAY: Self = Self { inner: 86400, unknown: false, years: 0, months: 0, weeks: 0, days: 1, hours: 0, minutes: 0, seconds: 0 };
pub const WEEK: Self = Self { inner: 604800, unknown: false, years: 0, months: 0, weeks: 1, days: 0, hours: 0, minutes: 0, seconds: 0 };
pub const MONTH: Self = Self { inner: 2678400, unknown: false, years: 0, months: 1, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0 };
pub const YEAR: Self = Self { inner: 31536000, unknown: false, years: 1, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0 };
pub const MAX: Self = Self { inner: u32::MAX, unknown: false, years: 136, months: 2, weeks: 1, days: 1, hours: 6, minutes: 28, seconds: 15 };
}
impl TimeUnit {
}
impl TimeUnit {
#[inline]
#[must_use]
pub const fn new(secs: u32) -> Self {
if secs == 0 {
return Self::ZERO;
}
let years = secs / 31_536_000; let years_rem = secs % 31_536_000;
let months = years_rem / 2_678_400; let months_rem = years_rem % 2_678_400;
let weeks = months_rem / 604_800; let weeks_rem = months_rem % 604_800;
let days = weeks_rem / 86_400; let days_rem = weeks_rem % 86_400;
let hours = days_rem / 3_600; let hours_rem = days_rem % 3_600;
let minutes = hours_rem / 60; let seconds = hours_rem % 60;
Self {
unknown: false,
inner: secs,
years: years as u8,
months: months as u8,
weeks: weeks as u8,
days: days as u8,
hours: hours as u8,
minutes: minutes as u8,
seconds: seconds as u8,
}
}
#[inline]
#[must_use]
pub const fn from_minutes(minutes: u32) -> Self { Self::new(minutes.saturating_mul(60)) }
#[inline]
#[must_use]
pub const fn from_hours(hours: u32) -> Self { Self::new(hours.saturating_mul(3_600)) }
#[inline]
#[must_use]
pub const fn from_days(days: u16) -> Self { Self::new((days as u32).saturating_mul(86_400)) }
#[inline]
#[must_use]
pub const fn from_weeks(weeks: u16) -> Self { Self::new((weeks as u32).saturating_mul(604_800)) }
#[inline]
#[must_use]
pub const fn from_months(months: u16) -> Self { Self::new((months as u32).saturating_mul(2_678_400)) }
#[inline]
#[must_use]
pub const fn from_years(years: u8) -> Self { Self::new((years as u32).saturating_mul(31_536_000)) }
#[inline]
#[must_use]
pub const fn new_variety(
years: u8,
months: u16,
weeks: u16,
days: u16,
hours: u32,
minutes: u32,
seconds: u32,
) -> Self {
let mut inner: u32 = 0;
inner = inner.saturating_add((years as u32).saturating_mul(31_536_000));
inner = inner.saturating_add((months as u32).saturating_mul(2_678_400));
inner = inner.saturating_add((weeks as u32).saturating_mul(604_800));
inner = inner.saturating_add((days as u32).saturating_mul(86_400));
inner = inner.saturating_add(hours.saturating_mul(3_600));
inner = inner.saturating_add(minutes.saturating_mul(60));
inner = inner.saturating_add(seconds);
Self::new(inner)
}
#[inline]
#[must_use]
pub const fn into_raw(self) -> (bool, u32, u8, u8, u8, u8, u8, u8, u8) {
(
self.unknown,
self.inner,
self.years,
self.months,
self.weeks,
self.days,
self.hours,
self.minutes,
self.seconds,
)
}
#[inline]
#[must_use]
pub const fn to_raw(&self) -> (bool, u32, u8, u8, u8, u8, u8, u8, u8) {
(
self.unknown,
self.inner,
self.years,
self.months,
self.weeks,
self.days,
self.hours,
self.minutes,
self.seconds,
)
}
#[inline]
#[must_use]
pub const fn is_unknown(&self) -> bool { self.unknown }
#[inline]
#[must_use]
pub const fn inner(&self) -> u32 { self.inner }
#[inline]
#[must_use]
pub const fn years(&self) -> u8 { self.years }
#[inline]
#[must_use]
pub const fn months(&self) -> u8 { self.months }
#[inline]
#[must_use]
pub const fn weeks(&self) -> u8 { self.weeks }
#[inline]
#[must_use]
pub const fn days(&self) -> u8 { self.days }
#[inline]
#[must_use]
pub const fn hours(&self) -> u8 { self.hours }
#[inline]
#[must_use]
pub const fn minutes(&self) -> u8 { self.minutes }
#[inline]
#[must_use]
pub const fn seconds(&self) -> u8 { self.seconds }
}
macro_rules! handle_over_u32 {
($value:expr, $type:ty) => {
if $value > (u32::MAX as $type) {
return Self::UNKNOWN;
}
};
}
macro_rules! impl_u {
($($u:ty),* $(,)?) => { $(
impl From<$u> for TimeUnit {
#[inline]
fn from(u: $u) -> Self {
Self::new(u as u32)
}
}
impl From<&$u> for TimeUnit {
#[inline]
fn from(u: &$u) -> Self {
Self::new(*u as u32)
}
}
)*}
}
impl_u!(u8,u16,u32);
#[cfg(not(target_pointer_width = "64"))]
impl_u!(usize);
macro_rules! impl_u_over {
($($u:ty),* $(,)?) => { $(
impl From<$u> for TimeUnit {
#[inline]
fn from(u: $u) -> Self {
handle_over_u32!(u, $u);
Self::new(u as u32)
}
}
impl From<&$u> for TimeUnit {
#[inline]
fn from(u: &$u) -> Self {
handle_over_u32!(*u, $u);
Self::new(*u as u32)
}
}
)*}
}
impl_u_over!(u64,u128);
#[cfg(target_pointer_width = "64")]
impl_u_over!(usize);
macro_rules! impl_int {
($($int:ty),* $(,)?) => { $(
impl From<$int> for TimeUnit {
#[inline]
fn from(int: $int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
Self::new(int as u32)
}
}
impl From<&$int> for TimeUnit {
#[inline]
fn from(int: &$int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
Self::new(*int as u32)
}
}
)*}
}
impl_int!(i8,i16,i32);
#[cfg(not(target_pointer_width = "64"))]
impl_u!(isize);
macro_rules! impl_int_over {
($($int:ty),* $(,)?) => { $(
impl From<$int> for TimeUnit {
#[inline]
fn from(int: $int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(int, $int);
Self::new(int as u32)
}
}
impl From<&$int> for TimeUnit {
#[inline]
fn from(int: &$int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(*int, $int);
Self::new(*int as u32)
}
}
)*}
}
impl_int_over!(i64,i128);
#[cfg(target_pointer_width = "64")]
impl_u_over!(isize);
macro_rules! impl_f {
($float:ty) => {
impl From<$float> for TimeUnit {
#[inline]
fn from(float: $float) -> Self {
return_bad_float!(float, Self::UNKNOWN, Self::UNKNOWN);
if float.is_sign_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(float, $float);
Self::new(float as u32)
}
}
impl From<&$float> for TimeUnit {
#[inline]
fn from(float: &$float) -> Self {
return_bad_float!(float, Self::UNKNOWN, Self::UNKNOWN);
if float.is_sign_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(*float, $float);
Self::new(*float as u32)
}
}
}
}
impl_f!(f32);
impl_f!(f64);
macro_rules! impl_from_time {
($this:ty => $($other:ty),* $(,)?) => { $(
impl From<$other> for $this {
#[inline]
fn from(from: $other) -> Self {
if from.is_unknown() {
Self::UNKNOWN
} else {
Self::new(from.inner() as u32)
}
}
}
impl From<&$other> for $this {
#[inline]
fn from(from: &$other) -> Self {
if from.is_unknown() {
Self::UNKNOWN
} else {
Self::new(from.inner() as u32)
}
}
}
)*}
}
impl_from_time!(TimeUnit => Time, Military);
#[cfg(feature = "up")]
impl_from_time!(TimeUnit => UptimeFull, Htop, Uptime);
#[cfg(feature = "num")]
impl_from_time!(TimeUnit => Unsigned);
impl From<std::time::Duration> for TimeUnit {
#[inline]
fn from(duration: std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<&std::time::Duration> for TimeUnit {
#[inline]
fn from(duration: &std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<std::time::Instant> for TimeUnit {
#[inline]
fn from(instant: std::time::Instant) -> Self {
let u = instant.elapsed().as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<&std::time::Instant> for TimeUnit {
#[inline]
fn from(instant: &std::time::Instant) -> Self {
let u = instant.elapsed().as_secs();
handle_over_u32!(u, u64);
Self::new(u as u32)
}
}
impl From<TimeUnit> for std::time::Duration {
#[inline]
fn from(value: TimeUnit) -> Self {
Self::from_secs(value.inner().into())
}
}
impl From<&TimeUnit> for std::time::Duration {
#[inline]
fn from(value: &TimeUnit) -> Self {
Self::from_secs(value.inner().into())
}
}
impl std::default::Default for TimeUnit {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "serde")]
fn serde() {
let this: TimeUnit = TimeUnit::from(39071167);
let json = serde_json::to_string(&this).unwrap();
assert_eq!(
json,
r#"{"unknown":false,"inner":39071167,"years":1,"months":2,"weeks":3,"days":4,"hours":5,"minutes":6,"seconds":7}"#
);
let this: TimeUnit = serde_json::from_str(&json).unwrap();
assert_eq!(this.inner(), 39071167);
assert!(serde_json::from_str::<TimeUnit>(&"---").is_err());
}
#[test]
#[cfg(feature = "bincode")]
fn bincode() {
let this: TimeUnit = TimeUnit::from(39071167);
let config = bincode::config::standard();
let bytes = bincode::encode_to_vec(&this, config).unwrap();
let this: TimeUnit = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert_eq!(this.inner(), 39071167);
}
#[test]
#[cfg(feature = "borsh")]
fn borsh() {
let this: TimeUnit = TimeUnit::from(39071167);
let bytes = borsh::to_vec(&this).unwrap();
let this: TimeUnit = borsh::from_slice(&bytes).unwrap();
assert_eq!(this.inner(), 39071167);
assert!(borsh::from_slice::<TimeUnit>(b"bad .-;[]124/ bytes").is_err());
}
}