use glib::translate::*;
use glib::StaticType;
use num_integer::div_rem;
use std::io::{self, prelude::*};
use std::time::Duration;
use std::{cmp, convert, fmt, str};
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Default)]
pub struct ClockTime(pub Option<u64>);
impl ClockTime {
pub fn hours(&self) -> Option<u64> {
(*self / crate::SECOND / 60 / 60).0
}
pub fn minutes(&self) -> Option<u64> {
(*self / crate::SECOND / 60).0
}
pub fn seconds(&self) -> Option<u64> {
(*self / crate::SECOND).0
}
pub fn mseconds(&self) -> Option<u64> {
(*self / crate::MSECOND).0
}
pub fn useconds(&self) -> Option<u64> {
(*self / crate::USECOND).0
}
pub fn nseconds(&self) -> Option<u64> {
(*self / crate::NSECOND).0
}
pub fn nanoseconds(&self) -> Option<u64> {
self.0
}
pub fn from_seconds(seconds: u64) -> ClockTime {
skip_assert_initialized!();
seconds * crate::SECOND
}
pub fn from_mseconds(mseconds: u64) -> ClockTime {
skip_assert_initialized!();
mseconds * crate::MSECOND
}
pub fn from_useconds(useconds: u64) -> ClockTime {
skip_assert_initialized!();
useconds * crate::USECOND
}
pub fn from_nseconds(nseconds: u64) -> ClockTime {
skip_assert_initialized!();
nseconds * crate::NSECOND
}
}
macro_rules! impl_common_ops_for_opt_int(
($name:ident) => {
impl $name {
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn saturating_add(self, rhs: Self) -> Option<Self> {
match (self.0, rhs.0) {
(Some(this), Some(rhs)) => Some(Self(Some(this.saturating_add(rhs)))),
_ => None,
}
}
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn saturating_sub(self, rhs: Self) -> Option<Self> {
match (self.0, rhs.0) {
(Some(this), Some(rhs)) => Some(Self(Some(this.saturating_sub(rhs)))),
_ => None,
}
}
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn min(self, rhs: Self) -> Option<Self> {
match (self.0, rhs.0) {
(Some(this), Some(rhs)) => Some(Self(Some(this.min(rhs)))),
_ => None,
}
}
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn max(self, rhs: Self) -> Option<Self> {
match (self.0, rhs.0) {
(Some(this), Some(rhs)) => Some(Self(Some(this.max(rhs)))),
_ => None,
}
}
pub const fn zero() -> Self {
Self(Some(0))
}
pub fn is_zero(&self) -> bool {
matches!(self.0, Some(0))
}
pub const fn none() -> Self {
Self(None)
}
}
impl cmp::PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
match (self.0, other.0) {
(Some(this), Some(other)) => this.partial_cmp(&other),
(None, None) => Some(cmp::Ordering::Equal),
_ => None,
}
}
}
};
);
impl_common_ops_for_opt_int!(ClockTime);
enum Sign {
Invalid,
NonNegative,
#[allow(dead_code)]
Negative,
}
fn pad_clocktime(f: &mut fmt::Formatter<'_>, sign: Sign, buf: &str) -> fmt::Result {
skip_assert_initialized!();
use self::Sign::*;
use std::fmt::{Alignment, Write};
let sign_aware_zero_pad = f.sign_aware_zero_pad();
let fill_char = match sign {
Invalid if sign_aware_zero_pad => '-',
_ if sign_aware_zero_pad => '0',
_ => f.fill(),
};
let sign_plus = f.sign_plus();
let sign_char = match sign {
Invalid if sign_plus => Some(fill_char),
NonNegative if sign_plus => Some('+'),
Negative => Some('-'),
_ => None,
};
let width = buf.len() + sign_char.map_or(0, |_| 1);
let padding = f.width().unwrap_or(0).saturating_sub(width);
let align = f.align().unwrap_or(Alignment::Right);
let (pre_padding, zero_padding, post_padding) = match align {
_ if sign_aware_zero_pad => (0, padding, 0),
Alignment::Left => (0, 0, padding),
Alignment::Right => (padding, 0, 0),
Alignment::Center => (padding / 2, 0, (padding + 1) / 2),
};
for _ in 0..pre_padding {
f.write_char(fill_char)?;
}
if let Some(c) = sign_char {
f.write_char(c)?;
}
for _ in 0..zero_padding {
f.write_char(fill_char)?;
}
f.write_str(buf)?;
for _ in 0..post_padding {
f.write_char(fill_char)?;
}
Ok(())
}
fn write_clocktime<W: io::Write>(
mut writer: W,
clocktime: Option<u64>,
precision: usize,
) -> io::Result<()> {
skip_assert_initialized!();
let precision = cmp::min(9, precision);
if let Some(ns) = clocktime {
let (s, ns) = div_rem(ns, 1_000_000_000);
let (m, s) = div_rem(s, 60);
let (h, m) = div_rem(m, 60);
write!(writer, "{}:{:02}:{:02}", h, m, s)?;
if precision > 0 {
let mut buf = [0u8; 9];
write!(&mut buf[..], "{:09}", ns).unwrap();
let buf_str = str::from_utf8(&buf[..]).unwrap();
write!(writer, ".{:.p$}", buf_str, p = precision)?;
}
} else {
write!(writer, "--:--:--")?;
if precision > 0 {
write!(writer, ".{:->p$}", "", p = precision)?;
}
}
Ok(())
}
impl fmt::Display for ClockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let precision = f.precision().unwrap_or(9);
const MAX_SIZE: usize = "5124095:34:33.709551614".len();
let mut buf = [0u8; MAX_SIZE];
let mut cursor = io::Cursor::new(&mut buf[..]);
write_clocktime(&mut cursor, self.0, precision).unwrap();
let pos = cursor.position() as usize;
let buf_str = str::from_utf8(&buf[..pos]).unwrap();
let sign = if self.0.is_some() {
Sign::NonNegative
} else {
Sign::Invalid
};
pad_clocktime(f, sign, buf_str)
}
}
#[doc(hidden)]
impl IntoGlib for ClockTime {
type GlibType = ffi::GstClockTime;
fn into_glib(self) -> ffi::GstClockTime {
match self.0 {
None => ffi::GST_CLOCK_TIME_NONE,
Some(v) => v,
}
}
}
#[doc(hidden)]
impl FromGlib<ffi::GstClockTime> for ClockTime {
unsafe fn from_glib(value: ffi::GstClockTime) -> Self {
skip_assert_initialized!();
match value {
ffi::GST_CLOCK_TIME_NONE => ClockTime(None),
value => ClockTime(Some(value)),
}
}
}
impl glib::value::ValueType for ClockTime {
type Type = Self;
}
#[doc(hidden)]
unsafe impl<'a> glib::value::FromValue<'a> for ClockTime {
type Checker = glib::value::GenericValueTypeChecker<Self>;
unsafe fn from_value(value: &glib::Value) -> Self {
skip_assert_initialized!();
from_glib(glib::gobject_ffi::g_value_get_uint64(
value.to_glib_none().0,
))
}
}
#[doc(hidden)]
impl glib::value::ToValue for ClockTime {
fn to_value(&self) -> glib::Value {
let mut value = glib::Value::for_value_type::<Self>();
unsafe {
glib::gobject_ffi::g_value_set_uint64(value.to_glib_none_mut().0, self.into_glib())
}
value
}
fn value_type(&self) -> glib::Type {
Self::static_type()
}
}
#[doc(hidden)]
impl glib::StaticType for ClockTime {
fn static_type() -> glib::Type {
<u64 as glib::StaticType>::static_type()
}
}
impl From<Duration> for ClockTime {
fn from(d: Duration) -> Self {
skip_assert_initialized!();
let nanos = d.as_nanos();
if nanos > std::u64::MAX as u128 {
crate::CLOCK_TIME_NONE
} else {
ClockTime::from_nseconds(nanos as u64)
}
}
}
impl convert::TryFrom<ClockTime> for Duration {
type Error = glib::BoolError;
fn try_from(t: ClockTime) -> Result<Self, Self::Error> {
skip_assert_initialized!();
t.nanoseconds()
.map(Duration::from_nanos)
.ok_or_else(|| glib::bool_error!("Can't convert ClockTime::NONE to Duration"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::eq_op)]
fn ops() {
let ct_10 = ClockTime::from_mseconds(10);
let ct_20 = ClockTime::from_mseconds(20);
let ct_30 = ClockTime::from_mseconds(30);
let ct_none = ClockTime::none();
assert_eq!(ct_10 + ct_20, ct_30);
assert_eq!(ct_10 + ct_none, ct_none);
assert_eq!(ct_none + ct_10, ct_none);
assert_eq!(ct_none + ct_none, ct_none);
assert_eq!(ct_30 - ct_20, ct_10);
assert_eq!(ct_30 - ct_30, ClockTime::zero());
assert_eq!(ct_30 - ct_none, ct_none);
assert_eq!(ct_none - ct_30, ct_none);
assert_eq!(ct_none - ct_none, ct_none);
}
#[test]
fn saturating_ops() {
let ct_1 = ClockTime::from_nseconds(1);
let ct_2 = ClockTime::from_nseconds(2);
let ct_max = ClockTime::from_nseconds(std::u64::MAX);
let ct_none = ClockTime::none();
assert_eq!(ct_max.saturating_add(ct_1), Some(ct_max));
assert!(ct_max.saturating_add(ct_none).is_none());
assert!(ct_none.saturating_add(ct_max).is_none());
assert!(ct_1.saturating_sub(ct_2).unwrap().is_zero());
assert!(ct_1.saturating_sub(ct_none).is_none());
assert!(ct_none.saturating_sub(ct_1).is_none());
}
#[test]
#[allow(clippy::eq_op)]
fn eq() {
let ct_10 = ClockTime::from_mseconds(10);
let ct_10_2 = ClockTime::from_mseconds(10);
let ct_10_3 = ClockTime::from_mseconds(10);
let ct_20 = ClockTime::from_mseconds(20);
let ct_none = ClockTime::none();
let ct_none_2 = ClockTime::none();
let ct_none_3 = ClockTime::none();
assert!(ct_10 == ct_10_2);
assert_ne!(ct_10 == ct_10_2, ct_10 != ct_10_2);
assert!(ct_10 != ct_20);
assert_ne!(ct_10 == ct_20, ct_10 != ct_20);
assert!(ct_none == ct_none_2);
assert_ne!(ct_none == ct_none_2, ct_none != ct_none_2);
assert!(ct_10 != ct_none);
assert_ne!(ct_10 == ct_none, ct_10 != ct_none);
assert!(ct_none != ct_10);
assert_ne!(ct_none == ct_10, ct_none != ct_10);
assert!(ct_10 == ct_10);
assert!(ct_none == ct_none);
assert!((ct_10 == ct_10_2) && (ct_10_2 == ct_10));
assert!((ct_none == ct_none_2) && (ct_none_2 == ct_none));
assert!((ct_10 == ct_10_2) && (ct_10_2 == ct_10_3) && (ct_10 == ct_10_3));
assert!((ct_none == ct_none_2) && (ct_none_2 == ct_none_3) && (ct_none == ct_none_3));
}
#[test]
#[allow(clippy::neg_cmp_op_on_partial_ord)]
fn partial_ord() {
let ct_10 = ClockTime::from_mseconds(10);
let ct_20 = ClockTime::from_mseconds(20);
let ct_30 = ClockTime::from_mseconds(30);
let ct_none = ClockTime::none();
assert_eq!(ct_10 < ct_none, false);
assert_eq!(ct_10 > ct_none, false);
assert_eq!(ct_none < ct_10, false);
assert_eq!(ct_none > ct_10, false);
assert!((ct_10 < ct_20) && !(ct_10 > ct_20));
assert!((ct_20 > ct_10) && !(ct_20 < ct_10));
assert!((ct_10 < ct_20) && (ct_20 < ct_30) && (ct_10 < ct_30));
assert!((ct_30 > ct_20) && (ct_20 > ct_10) && (ct_30 > ct_10));
}
#[test]
fn not_ord() {
let ct_10 = ClockTime::from_mseconds(10);
let ct_20 = ClockTime::from_mseconds(20);
let ct_none = ClockTime::none();
assert!((ct_10 < ct_20) ^ (ct_10 == ct_20) ^ (ct_10 > ct_20));
assert_eq!(
(ct_10 < ct_none) ^ (ct_10 == ct_none) ^ (ct_10 > ct_none),
false
);
assert_eq!(
(ct_none < ct_10) ^ (ct_none == ct_10) ^ (ct_none > ct_10),
false
);
}
#[test]
fn min_max() {
let ct_10 = ClockTime::from_nseconds(10);
let ct_20 = ClockTime::from_nseconds(20);
let ct_none = ClockTime::none();
assert_eq!(ct_10.min(ct_20).unwrap(), ct_10);
assert_eq!(ct_20.min(ct_10).unwrap(), ct_10);
assert!(ct_none.min(ct_10).is_none());
assert!(ct_20.min(ct_none).is_none());
assert_eq!(ct_10.max(ct_20).unwrap(), ct_20);
assert_eq!(ct_20.max(ct_10).unwrap(), ct_20);
assert!(ct_none.max(ct_10).is_none());
assert!(ct_20.max(ct_none).is_none());
}
#[test]
fn display() {
let none = ClockTime::none();
let some = ClockTime::from_nseconds(45834908569837);
let lots = ClockTime::from_nseconds(std::u64::MAX - 1);
assert_eq!(format!("{:.0}", none), "--:--:--");
assert_eq!(format!("{:.3}", none), "--:--:--.---");
assert_eq!(format!("{}", none), "--:--:--.---------");
assert_eq!(format!("{:.0}", some), "12:43:54");
assert_eq!(format!("{:.3}", some), "12:43:54.908");
assert_eq!(format!("{}", some), "12:43:54.908569837");
assert_eq!(format!("{:.0}", lots), "5124095:34:33");
assert_eq!(format!("{:.3}", lots), "5124095:34:33.709");
assert_eq!(format!("{}", lots), "5124095:34:33.709551614");
assert_eq!(format!("{:.10}", none), "--:--:--.---------");
assert_eq!(format!("{:.10}", some), "12:43:54.908569837");
assert_eq!(format!("{:.10}", lots), "5124095:34:33.709551614");
assert_eq!(format!("{:4.0}", none), "--:--:--");
assert_eq!(format!("{:4.3}", none), "--:--:--.---");
assert_eq!(format!("{:4}", none), "--:--:--.---------");
assert_eq!(format!("{:4.0}", some), "12:43:54");
assert_eq!(format!("{:4.3}", some), "12:43:54.908");
assert_eq!(format!("{:4}", some), "12:43:54.908569837");
assert_eq!(format!("{:4.0}", lots), "5124095:34:33");
assert_eq!(format!("{:4.3}", lots), "5124095:34:33.709");
assert_eq!(format!("{:4}", lots), "5124095:34:33.709551614");
assert_eq!(format!("{:>9.0}", none), " --:--:--");
assert_eq!(format!("{:<9.0}", none), "--:--:-- ");
assert_eq!(format!("{:^10.0}", none), " --:--:-- ");
assert_eq!(format!("{:>13.3}", none), " --:--:--.---");
assert_eq!(format!("{:<13.3}", none), "--:--:--.--- ");
assert_eq!(format!("{:^14.3}", none), " --:--:--.--- ");
assert_eq!(format!("{:>19}", none), " --:--:--.---------");
assert_eq!(format!("{:<19}", none), "--:--:--.--------- ");
assert_eq!(format!("{:^20}", none), " --:--:--.--------- ");
assert_eq!(format!("{:>9.0}", some), " 12:43:54");
assert_eq!(format!("{:<9.0}", some), "12:43:54 ");
assert_eq!(format!("{:^10.0}", some), " 12:43:54 ");
assert_eq!(format!("{:>13.3}", some), " 12:43:54.908");
assert_eq!(format!("{:<13.3}", some), "12:43:54.908 ");
assert_eq!(format!("{:^14.3}", some), " 12:43:54.908 ");
assert_eq!(format!("{:>19}", some), " 12:43:54.908569837");
assert_eq!(format!("{:<19}", some), "12:43:54.908569837 ");
assert_eq!(format!("{:^20}", some), " 12:43:54.908569837 ");
assert_eq!(format!("{:>14.0}", lots), " 5124095:34:33");
assert_eq!(format!("{:<14.0}", lots), "5124095:34:33 ");
assert_eq!(format!("{:^15.0}", lots), " 5124095:34:33 ");
assert_eq!(format!("{:>18.3}", lots), " 5124095:34:33.709");
assert_eq!(format!("{:<18.3}", lots), "5124095:34:33.709 ");
assert_eq!(format!("{:^19.3}", lots), " 5124095:34:33.709 ");
assert_eq!(format!("{:>24}", lots), " 5124095:34:33.709551614");
assert_eq!(format!("{:<24}", lots), "5124095:34:33.709551614 ");
assert_eq!(format!("{:^25}", lots), " 5124095:34:33.709551614 ");
assert_eq!(format!("{:+11.0}", none), " --:--:--");
assert_eq!(format!("{:011.0}", none), "-----:--:--");
assert_eq!(format!("{:+011.0}", none), "-----:--:--");
assert_eq!(format!("{:+15.3}", none), " --:--:--.---");
assert_eq!(format!("{:015.3}", none), "-----:--:--.---");
assert_eq!(format!("{:+015.3}", none), "-----:--:--.---");
assert_eq!(format!("{:+21}", none), " --:--:--.---------");
assert_eq!(format!("{:021}", none), "-----:--:--.---------");
assert_eq!(format!("{:+021}", none), "-----:--:--.---------");
assert_eq!(format!("{:+11.0}", some), " +12:43:54");
assert_eq!(format!("{:011.0}", some), "00012:43:54");
assert_eq!(format!("{:+011.0}", some), "+0012:43:54");
assert_eq!(format!("{:+15.3}", some), " +12:43:54.908");
assert_eq!(format!("{:015.3}", some), "00012:43:54.908");
assert_eq!(format!("{:+015.3}", some), "+0012:43:54.908");
assert_eq!(format!("{:+21}", some), " +12:43:54.908569837");
assert_eq!(format!("{:021}", some), "00012:43:54.908569837");
assert_eq!(format!("{:+021}", some), "+0012:43:54.908569837");
assert_eq!(format!("{:+16.0}", lots), " +5124095:34:33");
assert_eq!(format!("{:016.0}", lots), "0005124095:34:33");
assert_eq!(format!("{:+016.0}", lots), "+005124095:34:33");
assert_eq!(format!("{:+20.3}", lots), " +5124095:34:33.709");
assert_eq!(format!("{:020.3}", lots), "0005124095:34:33.709");
assert_eq!(format!("{:+020.3}", lots), "+005124095:34:33.709");
assert_eq!(format!("{:+26}", lots), " +5124095:34:33.709551614");
assert_eq!(format!("{:026}", lots), "0005124095:34:33.709551614");
assert_eq!(format!("{:+026}", lots), "+005124095:34:33.709551614");
}
}