use thiserror::Error;
#[macro_use]
mod macros;
mod clock_time;
pub use clock_time::*;
#[cfg(feature = "serde")]
mod clock_time_serde;
mod compatible;
pub use compatible::*;
#[cfg(feature = "serde")]
mod format_serde;
mod generic;
pub use generic::*;
mod signed;
pub use signed::*;
mod specific;
pub use specific::*;
mod undefined;
pub use undefined::*;
pub mod prelude {
pub use super::{
BuffersFormatConstructor, BytesFormatConstructor, DefaultFormatConstructor, FormattedValue,
FormattedValueNoneBuilder, NoneSignedBuilder, OtherFormatConstructor,
PercentFormatFloatConstructor, PercentFormatIntegerConstructor, TimeFormatConstructor,
UndefinedFormatConstructor, UnsignedIntoSigned,
};
}
use crate::Format;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)]
#[error("invalid formatted value format {:?}", .0)]
pub struct FormattedValueError(Format);
pub trait FormattedValue: Copy + Clone + Sized + Into<GenericFormattedValue> + 'static {
type FullRange: FormattedValueFullRange + From<Self>;
#[doc(alias = "get_default_format")]
fn default_format() -> Format;
#[doc(alias = "get_format")]
fn format(&self) -> Format;
fn is_some(&self) -> bool;
fn is_none(&self) -> bool {
!self.is_some()
}
unsafe fn into_raw_value(self) -> i64;
}
pub trait FormattedValueFullRange: FormattedValue + TryFrom<GenericFormattedValue> {
unsafe fn from_raw(format: Format, value: i64) -> Self;
}
pub trait FormattedValueIntrinsic: FormattedValue {}
pub trait FormattedValueNoneBuilder: FormattedValueFullRange {
fn none() -> Self;
#[track_caller]
fn none_for_format(format: Format) -> Self {
skip_assert_initialized!();
if Self::default_format() != format {
panic!(
"Expected: {:?}, requested {format:?}",
Self::default_format()
);
}
Self::none()
}
}
use std::fmt;
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Undefined => f.write_str("undefined"),
Self::Default => f.write_str("default"),
Self::Bytes => f.write_str("bytes"),
Self::Time => f.write_str("time"),
Self::Buffers => f.write_str("buffers"),
Self::Percent => f.write_str("%"),
Self::__Unknown(format) => write!(f, "(format: {})", format),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::Displayable;
fn with_compatible_formats<V1, V2>(
arg1: V1,
arg2: V2,
) -> Result<V2::Original, FormattedValueError>
where
V1: FormattedValue,
V2: CompatibleFormattedValue<V1>,
{
skip_assert_initialized!();
arg2.try_into_checked(arg1)
}
#[test]
fn compatible() {
assert_eq!(
with_compatible_formats(ClockTime::ZERO, ClockTime::ZERO),
Ok(ClockTime::ZERO),
);
assert_eq!(
with_compatible_formats(ClockTime::ZERO, ClockTime::NONE),
Ok(ClockTime::NONE),
);
assert_eq!(
with_compatible_formats(ClockTime::NONE, ClockTime::ZERO),
Ok(ClockTime::ZERO),
);
assert_eq!(
with_compatible_formats(
ClockTime::ZERO,
GenericFormattedValue::Time(Some(ClockTime::ZERO)),
),
Ok(GenericFormattedValue::Time(Some(ClockTime::ZERO))),
);
assert_eq!(
with_compatible_formats(
GenericFormattedValue::Time(Some(ClockTime::ZERO)),
ClockTime::NONE,
),
Ok(ClockTime::NONE),
);
}
#[test]
fn incompatible() {
with_compatible_formats(
ClockTime::ZERO,
GenericFormattedValue::Buffers(Some(42.buffers())),
)
.unwrap_err();
with_compatible_formats(
GenericFormattedValue::Buffers(Some(42.buffers())),
ClockTime::NONE,
)
.unwrap_err();
}
fn with_compatible_explicit<T, V>(arg: V, f: Format) -> Result<V::Original, FormattedValueError>
where
T: FormattedValue,
V: CompatibleFormattedValue<T>,
{
skip_assert_initialized!();
arg.try_into_checked_explicit(f)
}
#[test]
fn compatible_explicit() {
assert_eq!(
with_compatible_explicit::<ClockTime, _>(ClockTime::ZERO, Format::Time),
Ok(ClockTime::ZERO),
);
assert_eq!(
with_compatible_explicit::<ClockTime, _>(ClockTime::NONE, Format::Time),
Ok(ClockTime::NONE),
);
assert_eq!(
with_compatible_explicit::<ClockTime, _>(ClockTime::ZERO, Format::Time),
Ok(ClockTime::ZERO),
);
assert_eq!(
with_compatible_explicit::<ClockTime, _>(
GenericFormattedValue::Time(None),
Format::Time
),
Ok(GenericFormattedValue::Time(None)),
);
assert_eq!(
with_compatible_explicit::<GenericFormattedValue, _>(ClockTime::NONE, Format::Time),
Ok(ClockTime::NONE),
);
}
#[test]
fn incompatible_explicit() {
with_compatible_explicit::<Buffers, _>(GenericFormattedValue::Time(None), Format::Buffers)
.unwrap_err();
with_compatible_explicit::<GenericFormattedValue, _>(Buffers::ZERO, Format::Time)
.unwrap_err();
with_compatible_explicit::<GenericFormattedValue, _>(
GenericFormattedValue::Time(None),
Format::Buffers,
)
.unwrap_err();
}
#[test]
fn none_builder() {
let ct_none: Option<ClockTime> = Option::<ClockTime>::none();
assert!(ct_none.is_none());
let ct_none: Option<ClockTime> = Option::<ClockTime>::none_for_format(Format::Time);
assert!(ct_none.is_none());
let gen_ct_none: GenericFormattedValue =
GenericFormattedValue::none_for_format(Format::Time);
assert!(gen_ct_none.is_none());
assert!(ClockTime::ZERO.is_some());
assert!(!ClockTime::ZERO.is_none());
}
#[test]
#[should_panic]
fn none_for_inconsistent_format() {
let _ = Option::<ClockTime>::none_for_format(Format::Percent);
}
#[test]
#[should_panic]
fn none_for_unsupported_format() {
let _ = GenericFormattedValue::none_for_format(Format::Undefined);
}
#[test]
fn none_signed_builder() {
let ct_none: Option<Signed<ClockTime>> = Option::<ClockTime>::none_signed();
assert!(ct_none.is_none());
let ct_none: Option<Signed<ClockTime>> =
Option::<ClockTime>::none_signed_for_format(Format::Time);
assert!(ct_none.is_none());
let gen_ct_none: GenericSignedFormattedValue =
GenericFormattedValue::none_signed_for_format(Format::Time);
assert!(gen_ct_none.abs().is_none());
}
#[test]
fn signed_optional() {
let ct_1 = Some(ClockTime::SECOND);
let signed = ct_1.into_positive().unwrap();
assert_eq!(signed, Signed::Positive(ClockTime::SECOND));
assert!(signed.is_positive());
assert_eq!(signed.positive_or(()).unwrap(), ClockTime::SECOND);
assert_eq!(signed.positive_or_else(|_| ()).unwrap(), ClockTime::SECOND);
signed.negative_or(()).unwrap_err();
assert_eq!(
signed.negative_or_else(|val| val).unwrap_err(),
ClockTime::SECOND
);
let signed = ct_1.into_negative().unwrap();
assert_eq!(signed, Signed::Negative(ClockTime::SECOND));
assert!(signed.is_negative());
assert_eq!(signed.negative_or(()).unwrap(), ClockTime::SECOND);
assert_eq!(signed.negative_or_else(|_| ()).unwrap(), ClockTime::SECOND);
signed.positive_or(()).unwrap_err();
assert_eq!(
signed.positive_or_else(|val| val).unwrap_err(),
ClockTime::SECOND
);
let ct_none = ClockTime::NONE;
assert!(ct_none.into_positive().is_none());
assert!(ct_none.into_negative().is_none());
}
#[test]
fn signed_mandatory() {
let ct_1 = ClockTime::SECOND;
let signed = ct_1.into_positive();
assert_eq!(signed, Signed::Positive(ct_1));
assert!(signed.is_positive());
assert_eq!(signed.positive(), Some(ct_1));
assert!(!signed.is_negative());
assert!(signed.negative().is_none());
assert_eq!(signed.signum(), 1);
let signed = ct_1.into_negative();
assert_eq!(signed, Signed::Negative(ct_1));
assert!(signed.is_negative());
assert_eq!(signed.negative(), Some(ct_1));
assert!(!signed.is_positive());
assert!(signed.positive().is_none());
assert_eq!(signed.signum(), -1);
let signed = Default::ONE.into_positive();
assert_eq!(signed, Signed::Positive(Default::ONE));
assert!(signed.is_positive());
assert_eq!(signed.positive(), Some(Default::ONE));
assert!(!signed.is_negative());
assert!(signed.negative().is_none());
assert_eq!(signed.signum(), 1);
let signed = Default::ONE.into_negative();
assert_eq!(signed, Signed::Negative(Default::ONE));
assert!(signed.is_negative());
assert_eq!(signed.negative(), Some(Default::ONE));
assert!(!signed.is_positive());
assert!(signed.positive().is_none());
assert_eq!(signed.signum(), -1);
let ct_zero = ClockTime::ZERO;
let p_ct_zero = ct_zero.into_positive();
assert!(p_ct_zero.is_positive());
assert!(!p_ct_zero.is_negative());
assert_eq!(p_ct_zero.signum(), 0);
let n_ct_zero = ct_zero.into_negative();
assert!(n_ct_zero.is_negative());
assert!(!n_ct_zero.is_positive());
assert_eq!(n_ct_zero.signum(), 0);
}
#[test]
fn signed_generic() {
let ct_1 = GenericFormattedValue::Time(Some(ClockTime::SECOND));
assert!(ct_1.is_some());
let signed = ct_1.into_positive();
assert_eq!(
signed,
GenericSignedFormattedValue::Time(Some(Signed::Positive(ClockTime::SECOND))),
);
assert_eq!(signed.is_positive(), Some(true));
assert_eq!(signed.is_negative(), Some(false));
assert_eq!(signed.signum(), Some(1));
let signed = ct_1.into_negative();
assert_eq!(
signed,
GenericSignedFormattedValue::Time(Some(Signed::Negative(ClockTime::SECOND))),
);
assert_eq!(signed.is_negative(), Some(true));
assert_eq!(signed.is_positive(), Some(false));
assert_eq!(signed.signum(), Some(-1));
let ct_none = GenericFormattedValue::Time(ClockTime::NONE);
assert!(ct_none.is_none());
let signed = ct_none.into_positive();
assert_eq!(signed, GenericSignedFormattedValue::Time(None),);
assert!(signed.is_positive().is_none());
assert!(signed.is_negative().is_none());
assert!(signed.signum().is_none());
let signed = ct_none.into_negative();
assert_eq!(signed, GenericSignedFormattedValue::Time(None),);
assert!(signed.is_negative().is_none());
assert!(signed.is_positive().is_none());
assert!(signed.signum().is_none());
let ct_zero = GenericFormattedValue::Time(Some(ClockTime::ZERO));
assert!(ct_zero.is_some());
let signed = ct_zero.into_positive();
assert_eq!(
signed,
GenericSignedFormattedValue::Time(Some(Signed::Positive(ClockTime::ZERO))),
);
assert_eq!(signed.is_positive(), Some(true));
assert_eq!(signed.is_negative(), Some(false));
assert_eq!(signed.signum(), Some(0));
}
#[test]
fn signed_roundtrip() {
let ct_1 = Some(ClockTime::SECOND);
let raw_ct_1 = unsafe { ct_1.into_raw_value() };
let signed = unsafe { Option::<ClockTime>::from_raw(Format::Time, raw_ct_1) }
.into_signed(1)
.unwrap();
assert_eq!(signed, Signed::Positive(ClockTime::SECOND));
assert!(signed.is_positive());
let signed = unsafe { Option::<ClockTime>::from_raw(Format::Time, raw_ct_1) }
.into_signed(-1)
.unwrap();
assert_eq!(signed, Signed::Negative(ClockTime::SECOND));
assert!(signed.is_negative());
let ct_none = ClockTime::NONE;
let raw_ct_none = unsafe { ct_none.into_raw_value() };
let signed =
unsafe { Option::<ClockTime>::from_raw(Format::Time, raw_ct_none) }.into_signed(1);
assert!(signed.is_none());
let signed =
unsafe { Option::<ClockTime>::from_raw(Format::Time, raw_ct_none) }.into_signed(-1);
assert!(signed.is_none());
}
#[test]
fn display_new_types() {
let bytes = 42.bytes();
assert_eq!(&format!("{bytes}"), "42 bytes");
assert_eq!(&format!("{}", bytes.display()), "42 bytes");
assert_eq!(&format!("{}", Some(bytes).display()), "42 bytes");
assert_eq!(&format!("{}", Bytes::NONE.display()), "undef. bytes");
let gv_1 = GenericFormattedValue::Percent(Some(42.percent()));
assert_eq!(&format!("{gv_1}"), "42 %");
assert_eq!(
&format!("{}", GenericFormattedValue::Percent(None)),
"undef. %"
);
let percent = Percent::try_from(0.1234).unwrap();
assert_eq!(&format!("{percent}"), "12.34 %");
assert_eq!(&format!("{percent:5.1}"), " 12.3 %");
let other: Other = 42.try_into().unwrap();
assert_eq!(&format!("{other}"), "42");
let g_other = GenericFormattedValue::new(Format::__Unknown(128), 42);
assert_eq!(&format!("{g_other}"), "42 (format: 128)");
assert_eq!(&format!("{}", g_other.display()), "42 (format: 128)");
let g_other_none = GenericFormattedValue::Other(Format::__Unknown(128), None);
assert_eq!(&format!("{g_other_none}"), "undef. (format: 128)");
assert_eq!(
&format!("{}", g_other_none.display()),
"undef. (format: 128)"
);
}
#[test]
fn display_signed() {
let bytes_42 = 42.bytes();
let p_bytes = bytes_42.into_positive();
assert_eq!(&format!("{p_bytes}"), "+42 bytes");
assert_eq!(&format!("{}", p_bytes.display()), "+42 bytes");
let some_p_bytes = Some(p_bytes);
assert_eq!(&format!("{}", some_p_bytes.display()), "+42 bytes");
let p_some_bytes = Signed::Positive(Some(bytes_42));
assert_eq!(&format!("{}", p_some_bytes.display()), "+42 bytes");
let n_bytes = bytes_42.into_negative();
assert_eq!(&format!("{n_bytes}"), "-42 bytes");
assert_eq!(&format!("{}", n_bytes.display()), "-42 bytes");
let some_n_bytes = Some(n_bytes);
assert_eq!(&format!("{}", some_n_bytes.display()), "-42 bytes");
let n_some_bytes = Signed::Negative(Some(bytes_42));
assert_eq!(&format!("{}", n_some_bytes.display()), "-42 bytes");
let p_none_bytes = Signed::Positive(Bytes::NONE);
assert_eq!(&format!("{}", p_none_bytes.display()), "undef. bytes");
let n_none_bytes = Signed::Negative(Bytes::NONE);
assert_eq!(&format!("{}", n_none_bytes.display()), "undef. bytes");
let none_s_bytes = Option::<Signed<Bytes>>::None;
assert_eq!(&format!("{}", none_s_bytes.display()), "undef. bytes");
let ct_1 = 45_834_908_569_837 * ClockTime::NSECOND;
assert_eq!(&format!("{ct_1}"), "12:43:54.908569837");
assert_eq!(&format!("{}", ct_1.display()), "12:43:54.908569837");
let g_ct_1 = GenericFormattedValue::Time(Some(ct_1));
assert_eq!(&format!("{g_ct_1}"), "12:43:54.908569837");
assert_eq!(&format!("{}", g_ct_1.display()), "12:43:54.908569837");
let p_g_ct1 = g_ct_1.into_positive();
assert_eq!(&format!("{p_g_ct1}"), "+12:43:54.908569837");
assert_eq!(&format!("{}", p_g_ct1.display()), "+12:43:54.908569837");
let n_g_ct1 = g_ct_1.into_negative();
assert_eq!(&format!("{n_g_ct1}"), "-12:43:54.908569837");
assert_eq!(&format!("{}", n_g_ct1.display()), "-12:43:54.908569837");
}
}