use crate::up::{Uptime,Htop};
use crate::str::Str;
use crate::macros::{
return_bad_float,impl_common,
impl_const,impl_impl_math,impl_math,
impl_usize,impl_traits,handle_over_u32,
};
use crate::itoa;
#[cfg(feature = "time")]
use crate::time::TimeUnit;
#[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 UptimeFull(pub(super) u32, pub(super) Str<{ UptimeFull::MAX_LEN }>);
impl_math!(UptimeFull, u32);
impl_traits!(UptimeFull, u32);
impl UptimeFull {
pub const MAX_LEN: usize = 63;
pub const UNKNOWN: Self = Self(0, Str::from_static_str("(unknown)"));
pub const ZERO: Self = Self(0, Str::from_static_str("0 seconds"));
pub const SECOND: Self = Self(1, Str::from_static_str("1 second"));
pub const MINUTE: Self = Self(60, Str::from_static_str("1 minute"));
pub const HOUR: Self = Self(3600, Str::from_static_str("1 hour"));
pub const DAY: Self = Self(86400, Str::from_static_str("1 day"));
pub const MONTH: Self = Self(86400, Str::from_static_str("1 month"));
pub const YEAR: Self = Self(31_536_000, Str::from_static_str("1 year"));
pub const MAX: Self = Self(u32::MAX, Str::from_static_str("136 years, 2 months, 8 days, 6 hours, 28 minutes, 15 seconds"));
}
impl UptimeFull {
impl_common!(u32);
impl_const!();
impl_usize!();
#[inline]
#[must_use]
pub const fn is_unknown(&self) -> bool {
matches!(*self, Self::UNKNOWN)
}
}
impl UptimeFull {
#[inline]
fn plural(
s: &mut Str<{ Self::MAX_LEN }>,
name: &'static str,
value: u32,
started: &mut bool,
) {
if value > 0 {
if *started {
s.push_str_panic(", ");
}
s.push_str_panic(itoa!(value));
s.push_str_panic(" ");
s.push_str_panic(name);
if value > 1 {
s.push_str_panic("s");
}
*started = true;
}
}
fn from_priv(secs: u32) -> Self {
if secs == 0 {
return Self::ZERO;
}
let years = secs / 31_536_000; let ydays = secs % 31_536_000;
let months = ydays / 2_678_400; let mdays = ydays % 2_678_400;
let days = mdays / 86400;
let day_secs = mdays % 86400;
let hours = day_secs / 3600;
let minutes = day_secs % 3600 / 60;
let seconds = day_secs % 60;
let started = &mut false;
let mut string = Str::new();
let s = &mut string;
Self::plural(s, "year", years, started);
Self::plural(s, "month", months, started);
Self::plural(s, "day", days, started);
Self::plural(s, "hour", hours, started);
Self::plural(s, "minute", minutes, started);
Self::plural(s, "second", seconds, started);
Self(secs, string)
}
}
macro_rules! impl_u {
($($u:ty),* $(,)?) => { $(
impl From<$u> for UptimeFull {
#[inline]
fn from(u: $u) -> Self {
Self::from_priv(u as u32)
}
}
impl From<&$u> for UptimeFull {
#[inline]
fn from(u: &$u) -> Self {
Self::from_priv(*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 UptimeFull {
#[inline]
fn from(u: $u) -> Self {
handle_over_u32!(u, $u);
Self::from_priv(u as u32)
}
}
impl From<&$u> for UptimeFull {
#[inline]
fn from(u: &$u) -> Self {
handle_over_u32!(*u, $u);
Self::from_priv(*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 UptimeFull {
#[inline]
fn from(int: $int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
Self::from_priv(int as u32)
}
}
impl From<&$int> for UptimeFull {
#[inline]
fn from(int: &$int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
Self::from_priv(*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 UptimeFull {
#[inline]
fn from(int: $int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(int, $int);
Self::from_priv(int as u32)
}
}
impl From<&$int> for UptimeFull {
#[inline]
fn from(int: &$int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
handle_over_u32!(*int, $int);
Self::from_priv(*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 UptimeFull {
#[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::from_priv(float as u32)
}
}
impl From<&$float> for UptimeFull {
#[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::from_priv(*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::from_priv(from.inner())
}
}
}
impl From<&$other> for $this {
#[inline]
fn from(from: &$other) -> Self {
if from.is_unknown() {
Self::UNKNOWN
} else {
Self::from_priv(from.inner())
}
}
}
)*}
}
impl_from_time!(UptimeFull => Uptime, Htop);
#[cfg(feature = "time")]
impl_from_time!(UptimeFull => TimeUnit);
impl From<std::time::Duration> for UptimeFull {
#[inline]
fn from(duration: std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::from_priv(u as u32)
}
}
impl From<&std::time::Duration> for UptimeFull {
#[inline]
fn from(duration: &std::time::Duration) -> Self {
let u = duration.as_secs();
handle_over_u32!(u, u64);
Self::from_priv(u as u32)
}
}
impl From<std::time::Instant> for UptimeFull {
#[inline]
fn from(instant: std::time::Instant) -> Self {
let u = instant.elapsed().as_secs();
handle_over_u32!(u, u64);
Self::from_priv(u as u32)
}
}
impl From<&std::time::Instant> for UptimeFull {
#[inline]
fn from(instant: &std::time::Instant) -> Self {
let u = instant.elapsed().as_secs();
handle_over_u32!(u, u64);
Self::from_priv(u as u32)
}
}
impl From<UptimeFull> for std::time::Duration {
#[inline]
fn from(value: UptimeFull) -> Self {
Self::from_secs(value.inner().into())
}
}
impl From<&UptimeFull> for std::time::Duration {
#[inline]
fn from(value: &UptimeFull) -> Self {
Self::from_secs(value.inner().into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_ints() {
let mut f = 1_u64;
while f < (u64::from(UptimeFull::MAX.0)) {
let t = UptimeFull::from(f);
println!("t: {t}, f: {f}");
assert_eq!(t, f as u32);
f *= 10;
}
}
#[test]
fn over() {
assert_ne!(UptimeFull::from(u32::MAX), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(u64::from(u32::MAX) + 1), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(u64::MAX), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f64::MAX), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f32::MAX), UptimeFull::UNKNOWN);
}
#[test]
fn special() {
assert_eq!(UptimeFull::from(f32::NAN), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f32::INFINITY), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f32::NEG_INFINITY), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f64::NAN), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f64::INFINITY), UptimeFull::UNKNOWN);
assert_eq!(UptimeFull::from(f64::NEG_INFINITY), UptimeFull::UNKNOWN);
}
#[test]
#[cfg(feature = "serde")]
fn serde() {
let this: UptimeFull = UptimeFull::from(3283199_u32);
let json = serde_json::to_string(&this).unwrap();
assert_eq!(json, r#"[3283199,"1 month, 6 days, 23 hours, 59 minutes, 59 seconds"]"#);
let this: UptimeFull = serde_json::from_str(&json).unwrap();
assert_eq!(this, 3283199_u32);
assert_eq!(this, "1 month, 6 days, 23 hours, 59 minutes, 59 seconds");
assert!(serde_json::from_str::<UptimeFull>(&"---").is_err());
let json = serde_json::to_string(&UptimeFull::UNKNOWN).unwrap();
assert_eq!(json, r#"[0,"(unknown)"]"#);
assert!(serde_json::from_str::<UptimeFull>(&json).unwrap().is_unknown());
}
#[test]
#[cfg(feature = "bincode")]
fn bincode() {
let this: UptimeFull = UptimeFull::from(3283199_u32);
let config = bincode::config::standard();
let bytes = bincode::encode_to_vec(&this, config).unwrap();
let this: UptimeFull = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert_eq!(this, 3283199_u32);
assert_eq!(this, "1 month, 6 days, 23 hours, 59 minutes, 59 seconds");
let bytes = bincode::encode_to_vec(&UptimeFull::UNKNOWN, config).unwrap();
let this: UptimeFull = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert!(this.is_unknown());
}
#[test]
#[cfg(feature = "borsh")]
fn borsh() {
let this: UptimeFull = UptimeFull::from(3283199_u32);
let bytes = borsh::to_vec(&this).unwrap();
let this: UptimeFull = borsh::from_slice(&bytes).unwrap();
assert_eq!(this, 3283199_u32);
assert_eq!(this, "1 month, 6 days, 23 hours, 59 minutes, 59 seconds");
assert!(borsh::from_slice::<UptimeFull>(b"bad .-;[]124/ bytes").is_err());
let bytes = borsh::to_vec(&UptimeFull::UNKNOWN).unwrap();
let this: UptimeFull = borsh::from_slice(&bytes).unwrap();
assert!(this.is_unknown());
}
}