use crate::up::{Uptime,UptimeFull};
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;
use crate::run::RuntimePad;
#[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 Htop(pub(super) u32, pub(super) Str<{ Htop::MAX_LEN }>);
impl_math!(Htop, u32);
impl_traits!(Htop, u32);
impl Htop {
pub const MAX_LEN: usize = 23;
pub const UNKNOWN: Self = Self(0, Str::from_static_str("(unknown)"));
pub const ZERO: Self = Self(0, Str::from_static_str("00:00:00"));
pub const SECOND: Self = Self(1, Str::from_static_str("00:00:01"));
pub const MINUTE: Self = Self(60, Str::from_static_str("00:01:00"));
pub const HOUR: Self = Self(3600, Str::from_static_str("01:00:00"));
pub const DAY: Self = Self(86400, Str::from_static_str("1 day, 00:00:00"));
pub const MONTH: Self = Self(2678400, Str::from_static_str("31 days, 00:00:00"));
pub const YEAR: Self = Self(31536000, Str::from_static_str("365 days(!), 00:00:00"));
pub const MAX: Self = Self(u32::MAX, Str::from_static_str("49710 days(!), 06:28:15"));
}
impl Htop {
impl_common!(u32);
impl_const!();
impl_usize!();
#[inline]
#[must_use]
pub const fn is_unknown(&self) -> bool {
matches!(*self, Self::UNKNOWN)
}
}
impl Htop {
#[inline]
#[must_use]
fn from_priv(secs: u32) -> Self {
if secs == 0 {
return Self::ZERO;
}
let days = secs / 86400;
let mut string = Str::new();
if days > 0 {
string.push_str_panic(itoa!(days));
#[allow(clippy::else_if_without_else)]
if days > 100 {
string.push_str_panic(" days(!), ");
} else if days > 1 {
string.push_str_panic(" days, ");
} else if days == 1 {
string.push_str_panic(" day, ");
}
};
let runtime = RuntimePad::from(secs % 86400);
string.push_str_panic(runtime);
Self(secs, string)
}
}
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!(Htop => Uptime, UptimeFull);
#[cfg(feature = "time")]
impl_from_time!(Htop => TimeUnit);
macro_rules! impl_u {
($($u:ty),* $(,)?) => { $(
impl From<$u> for Htop {
#[inline]
fn from(u: $u) -> Self {
Self::from_priv(u as u32)
}
}
impl From<&$u> for Htop {
#[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 Htop {
#[inline]
fn from(u: $u) -> Self {
handle_over_u32!(u, $u);
Self::from_priv(u as u32)
}
}
impl From<&$u> for Htop {
#[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 Htop {
#[inline]
fn from(int: $int) -> Self {
if int.is_negative() {
return Self::UNKNOWN;
}
Self::from_priv(int as u32)
}
}
impl From<&$int> for Htop {
#[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 Htop {
#[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 Htop {
#[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 Htop {
#[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 Htop {
#[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);
impl From<std::time::Duration> for Htop {
#[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 Htop {
#[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 Htop {
#[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 Htop {
#[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<Htop> for std::time::Duration {
#[inline]
fn from(value: Htop) -> Self {
Self::from_secs(value.inner().into())
}
}
impl From<&Htop> for std::time::Duration {
#[inline]
fn from(value: &Htop) -> 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(Htop::MAX.0)) {
let t = Htop::from(f);
println!("t: {t}, f: {f}");
assert_eq!(t, f as u32);
f *= 10;
}
}
#[test]
fn over() {
assert_ne!(Htop::from(u32::MAX), Htop::UNKNOWN);
assert_eq!(Htop::from(u64::from(u32::MAX) + 1), Htop::UNKNOWN);
assert_eq!(Htop::from(u64::MAX), Htop::UNKNOWN);
assert_eq!(Htop::from(f64::MAX), Htop::UNKNOWN);
assert_eq!(Htop::from(f32::MAX), Htop::UNKNOWN);
}
#[test]
fn special() {
assert_eq!(Htop::from(f32::NAN), Htop::UNKNOWN);
assert_eq!(Htop::from(f32::INFINITY), Htop::UNKNOWN);
assert_eq!(Htop::from(f32::NEG_INFINITY), Htop::UNKNOWN);
assert_eq!(Htop::from(f64::NAN), Htop::UNKNOWN);
assert_eq!(Htop::from(f64::INFINITY), Htop::UNKNOWN);
assert_eq!(Htop::from(f64::NEG_INFINITY), Htop::UNKNOWN);
}
#[test]
#[cfg(feature = "serde")]
fn serde() {
let this: Htop = Htop::from(8726400_u32);
let json = serde_json::to_string(&this).unwrap();
assert_eq!(json, r#"[8726400,"101 days(!), 00:00:00"]"#);
let this: Htop = serde_json::from_str(&json).unwrap();
assert_eq!(this, 8726400_u32);
assert_eq!(this, "101 days(!), 00:00:00");
assert!(serde_json::from_str::<Htop>(&"---").is_err());
let json = serde_json::to_string(&Htop::UNKNOWN).unwrap();
assert_eq!(json, r#"[0,"(unknown)"]"#);
assert!(serde_json::from_str::<Htop>(&json).unwrap().is_unknown());
}
#[test]
#[cfg(feature = "bincode")]
fn bincode() {
let this: Htop = Htop::from(8726400_u32);
let config = bincode::config::standard();
let bytes = bincode::encode_to_vec(&this, config).unwrap();
let this: Htop = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert_eq!(this, 8726400_u32);
assert_eq!(this, "101 days(!), 00:00:00");
let bytes = bincode::encode_to_vec(&Htop::UNKNOWN, config).unwrap();
let this: Htop = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert!(this.is_unknown());
}
#[test]
#[cfg(feature = "borsh")]
fn borsh() {
let this: Htop = Htop::from(8726400_u32);
let bytes = borsh::to_vec(&this).unwrap();
let this: Htop = borsh::from_slice(&bytes).unwrap();
assert_eq!(this, 8726400_u32);
assert_eq!(this, "101 days(!), 00:00:00");
assert!(borsh::from_slice::<Htop>(b"bad .-;[]124/ bytes").is_err());
let bytes = borsh::to_vec(&Htop::UNKNOWN).unwrap();
let this: Htop = borsh::from_slice(&bytes).unwrap();
assert!(this.is_unknown());
}
}