1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use glib::translate::*;
4 use glib::StaticType;
5 use num_integer::div_rem;
6 use std::io::{self, prelude::*};
7 use std::time::Duration;
8 use std::{cmp, convert, fmt, str};
9 
10 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
11 pub struct ClockTime(pub(crate) u64);
12 
13 impl ClockTime {
14     pub const SECOND: ClockTime = ClockTime(1_000_000_000);
15     pub const MSECOND: ClockTime = ClockTime(1_000_000);
16     pub const USECOND: ClockTime = ClockTime(1_000);
17     pub const NSECOND: ClockTime = ClockTime(1);
18 
hours(self) -> u6419     pub const fn hours(self) -> u64 {
20         self.0 / Self::SECOND.0 / 60 / 60
21     }
22 
minutes(self) -> u6423     pub const fn minutes(self) -> u64 {
24         self.0 / Self::SECOND.0 / 60
25     }
26 
seconds(self) -> u6427     pub const fn seconds(self) -> u64 {
28         self.0 / Self::SECOND.0
29     }
30 
mseconds(self) -> u6431     pub const fn mseconds(self) -> u64 {
32         self.0 / Self::MSECOND.0
33     }
34 
useconds(self) -> u6435     pub const fn useconds(self) -> u64 {
36         self.0 / Self::USECOND.0
37     }
38 
nseconds(self) -> u6439     pub const fn nseconds(self) -> u64 {
40         self.0
41     }
42 
from_seconds(seconds: u64) -> Self43     pub const fn from_seconds(seconds: u64) -> Self {
44         skip_assert_initialized!();
45         ClockTime(seconds * Self::SECOND.0)
46     }
47 
from_mseconds(mseconds: u64) -> Self48     pub const fn from_mseconds(mseconds: u64) -> Self {
49         skip_assert_initialized!();
50         ClockTime(mseconds * Self::MSECOND.0)
51     }
52 
from_useconds(useconds: u64) -> Self53     pub const fn from_useconds(useconds: u64) -> Self {
54         skip_assert_initialized!();
55         ClockTime(useconds * Self::USECOND.0)
56     }
57 
from_nseconds(nseconds: u64) -> Self58     pub const fn from_nseconds(nseconds: u64) -> Self {
59         skip_assert_initialized!();
60         ClockTime(nseconds * Self::NSECOND.0)
61     }
62 }
63 
64 macro_rules! option_glib_newtype_from_to {
65     ($type_:ident, $none_value:expr) => {
66         #[doc(hidden)]
67         impl IntoGlib for $type_ {
68             type GlibType = u64;
69 
70             fn into_glib(self) -> u64 {
71                 self.0
72             }
73         }
74 
75         #[doc(hidden)]
76         impl OptionIntoGlib for $type_ {
77             const GLIB_NONE: u64 = $none_value;
78         }
79 
80         #[doc(hidden)]
81         impl TryFromGlib<u64> for $type_ {
82             type Error = GlibNoneError;
83             #[inline]
84             unsafe fn try_from_glib(val: u64) -> Result<Self, GlibNoneError> {
85                 skip_assert_initialized!();
86                 if val == $none_value {
87                     return Err(GlibNoneError);
88                 }
89 
90                 Ok($type_(val))
91             }
92         }
93     };
94 }
95 
96 option_glib_newtype_from_to!(ClockTime, ffi::GST_CLOCK_TIME_NONE);
97 
98 impl glib::value::ValueType for ClockTime {
99     type Type = Self;
100 }
101 
102 pub struct ClockTimeValueTypeOrNoneChecker<T>(std::marker::PhantomData<T>);
103 
104 unsafe impl<T: StaticType> glib::value::ValueTypeChecker for ClockTimeValueTypeOrNoneChecker<T> {
105     type Error = glib::value::ValueTypeMismatchOrNoneError;
106 
check(value: &glib::Value) -> Result<(), Self::Error>107     fn check(value: &glib::Value) -> Result<(), Self::Error> {
108         skip_assert_initialized!();
109         glib::value::GenericValueTypeChecker::<T>::check(value)?;
110 
111         let gct = unsafe { glib::gobject_ffi::g_value_get_uint64(value.to_glib_none().0) };
112         if gct == ffi::GST_CLOCK_TIME_NONE {
113             return Err(glib::value::ValueTypeMismatchOrNoneError::UnexpectedNone);
114         }
115 
116         Ok(())
117     }
118 }
119 
120 unsafe impl<'a> glib::value::FromValue<'a> for ClockTime {
121     type Checker = ClockTimeValueTypeOrNoneChecker<Self>;
122 
from_value(value: &glib::Value) -> ClockTime123     unsafe fn from_value(value: &glib::Value) -> ClockTime {
124         skip_assert_initialized!();
125         ClockTime(glib::gobject_ffi::g_value_get_uint64(
126             value.to_glib_none().0,
127         ))
128     }
129 }
130 
131 impl glib::value::ToValue for ClockTime {
to_value(&self) -> glib::Value132     fn to_value(&self) -> glib::Value {
133         let mut value = glib::Value::for_value_type::<ClockTime>();
134         let gct = self.into_glib();
135         if gct == ffi::GST_CLOCK_TIME_NONE {
136             crate::gst_warning!(
137                 crate::CAT_RUST,
138                 "converting a defined `ClockTime` with value `ffi::GST_CLOCK_TIME_NONE` to `Value`, this is probably not what you wanted.",
139             );
140         }
141         unsafe { glib::gobject_ffi::g_value_set_uint64(value.to_glib_none_mut().0, gct) }
142         value
143     }
144 
value_type(&self) -> glib::Type145     fn value_type(&self) -> glib::Type {
146         Self::static_type()
147     }
148 }
149 
150 impl glib::value::ToValueOptional for ClockTime {
to_value_optional(opt: Option<&Self>) -> glib::Value151     fn to_value_optional(opt: Option<&Self>) -> glib::Value {
152         skip_assert_initialized!();
153         let mut value = glib::Value::for_value_type::<ClockTime>();
154         let inner = opt.map(|inner| inner.0).unwrap_or(ffi::GST_CLOCK_TIME_NONE);
155         unsafe { glib::gobject_ffi::g_value_set_uint64(value.to_glib_none_mut().0, inner) };
156 
157         value
158     }
159 }
160 
161 #[doc(hidden)]
162 impl glib::StaticType for ClockTime {
static_type() -> glib::Type163     fn static_type() -> glib::Type {
164         <u64 as glib::StaticType>::static_type()
165     }
166 }
167 
168 #[derive(Debug)]
169 pub struct DurationError;
170 
171 impl fmt::Display for DurationError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result172     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
173         write!(fmt, "out of range conversion from Duration attempted")
174     }
175 }
176 
177 impl std::error::Error for DurationError {}
178 
179 impl convert::TryFrom<Duration> for ClockTime {
180     type Error = DurationError;
181 
try_from(d: Duration) -> Result<Self, Self::Error>182     fn try_from(d: Duration) -> Result<Self, Self::Error> {
183         skip_assert_initialized!();
184 
185         let nanos = d.as_nanos();
186 
187         // Note: `std::u64::MAX` is `ClockTime::None`.
188         if nanos >= std::u64::MAX as u128 {
189             return Err(DurationError);
190         }
191 
192         Ok(ClockTime::from_nseconds(nanos as u64))
193     }
194 }
195 
196 impl convert::From<ClockTime> for Duration {
from(t: ClockTime) -> Self197     fn from(t: ClockTime) -> Self {
198         skip_assert_initialized!();
199 
200         Duration::from_nanos(t.nseconds())
201     }
202 }
203 
204 macro_rules! impl_common_ops_for_newtype_u64(
205     ($name:ident) => {
206         impl $name {
207             pub const ZERO: Self = Self(0);
208             pub const NONE: Option<Self> = None;
209 
210             pub const fn is_zero(self) -> bool {
211                 self.0 == Self::ZERO.0
212             }
213 
214             #[must_use = "this returns the result of the operation, without modifying the original"]
215             #[inline]
216             // FIXME Can't use `map` in a `const fn` as of rustc 1.53.0-beta.2
217             #[allow(clippy::manual_map)]
218             pub const fn checked_add(self, rhs: Self) -> Option<Self> {
219                 match self.0.checked_add(rhs.0) {
220                     Some(res) => Some(Self(res)),
221                     None => None,
222                 }
223             }
224 
225             #[must_use = "this returns the result of the operation, without modifying the original"]
226             #[inline]
227             pub const fn saturating_add(self, rhs: Self) -> Self {
228                 Self(self.0.saturating_add(rhs.0))
229             }
230 
231             #[must_use = "this returns the result of the operation, without modifying the original"]
232             #[inline]
233             pub const fn wrapping_add(self, rhs: Self) -> Self {
234                 Self(self.0.wrapping_add(rhs.0))
235             }
236 
237             #[must_use = "this returns the result of the operation, without modifying the original"]
238             #[inline]
239             // FIXME Can't use `map` in a `const fn` as of rustc 1.53.0-beta.2
240             #[allow(clippy::manual_map)]
241             pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
242                 match self.0.checked_sub(rhs.0) {
243                     Some(res) => Some(Self(res)),
244                     None => None,
245                 }
246             }
247 
248             #[must_use = "this returns the result of the operation, without modifying the original"]
249             #[inline]
250             pub const fn saturating_sub(self, rhs: Self) -> Self {
251                 Self(self.0.saturating_sub(rhs.0))
252             }
253 
254             #[must_use = "this returns the result of the operation, without modifying the original"]
255             #[inline]
256             pub const fn wrapping_sub(self, rhs: Self) -> Self {
257                 Self(self.0.wrapping_sub(rhs.0))
258             }
259         }
260     };
261 );
262 
263 impl_common_ops_for_newtype_u64!(ClockTime);
264 
265 /// Tell [`pad_clocktime`] what kind of time we're formatting
266 enum Sign {
267     /// An undefined time (`None`)
268     Undefined,
269 
270     /// A non-negative time (zero or greater)
271     NonNegative,
272 
273     // For a future ClockTimeDiff formatting
274     #[allow(dead_code)]
275     /// A negative time (below zero)
276     Negative,
277 }
278 
279 // Derived from libcore `Formatter::pad_integral` (same APACHE v2 + MIT licenses)
280 //
281 // TODO: Would be useful for formatting ClockTimeDiff
282 // if it was a new type instead of an alias for i64
283 //
284 /// Performs the correct padding for a clock time which has already been
285 /// emitted into a str, as by [`write_clocktime`]. The str should *not*
286 /// contain the sign; that will be added by this method.
pad_clocktime(f: &mut fmt::Formatter<'_>, sign: Sign, buf: &str) -> fmt::Result287 fn pad_clocktime(f: &mut fmt::Formatter<'_>, sign: Sign, buf: &str) -> fmt::Result {
288     skip_assert_initialized!();
289     use self::Sign::*;
290     use std::fmt::{Alignment, Write};
291 
292     // Start by determining how we're padding, gathering
293     // settings from the Formatter and the Sign
294 
295     // Choose the fill character
296     let sign_aware_zero_pad = f.sign_aware_zero_pad();
297     let fill_char = match sign {
298         Undefined if sign_aware_zero_pad => '-', // Zero-padding an undefined time
299         _ if sign_aware_zero_pad => '0',         // Zero-padding a valid time
300         _ => f.fill(),                           // Otherwise, pad with the user-chosen character
301     };
302 
303     // Choose the sign character
304     let sign_plus = f.sign_plus();
305     let sign_char = match sign {
306         Undefined if sign_plus => Some(fill_char), // User requested sign, time is undefined
307         NonNegative if sign_plus => Some('+'),     // User requested sign, time is zero or above
308         Negative => Some('-'),                     // Time is below zero
309         _ => None,                                 // Otherwise, add no sign
310     };
311 
312     // Our minimum width is the value's width, plus 1 for the sign if present
313     let width = buf.len() + sign_char.map_or(0, |_| 1);
314 
315     // Subtract the minimum width from the requested width to get the padding,
316     // taking care not to allow wrapping due to underflow
317     let padding = f.width().unwrap_or(0).saturating_sub(width);
318 
319     // Split the required padding into the three possible parts
320     let align = f.align().unwrap_or(Alignment::Right);
321     let (pre_padding, zero_padding, post_padding) = match align {
322         _ if sign_aware_zero_pad => (0, padding, 0), // Zero-padding: Pad between sign and value
323         Alignment::Left => (0, 0, padding),          // Align left: Pad on the right side
324         Alignment::Right => (padding, 0, 0),         // Align right: Pad on the left side
325 
326         // Align center: Split equally between left and right side
327         // If the required padding is odd, the right side gets one more char
328         Alignment::Center => (padding / 2, 0, (padding + 1) / 2),
329     };
330 
331     // And now for the actual writing
332 
333     for _ in 0..pre_padding {
334         f.write_char(fill_char)?; // Left padding
335     }
336     if let Some(c) = sign_char {
337         f.write_char(c)?; // ------- Sign character
338     }
339     for _ in 0..zero_padding {
340         f.write_char(fill_char)?; // Padding between sign and value
341     }
342     f.write_str(buf)?; // ---------- Value
343     for _ in 0..post_padding {
344         f.write_char(fill_char)?; // Right padding
345     }
346 
347     Ok(())
348 }
349 
350 /// Writes an unpadded, signless clocktime string with the given precision
write_clocktime<W: io::Write>( mut writer: W, clocktime: Option<ClockTime>, precision: usize, ) -> io::Result<()>351 fn write_clocktime<W: io::Write>(
352     mut writer: W,
353     clocktime: Option<ClockTime>,
354     precision: usize,
355 ) -> io::Result<()> {
356     skip_assert_initialized!();
357     let precision = cmp::min(9, precision);
358 
359     if let Some(ns) = clocktime.map(ClockTime::nseconds) {
360         // Split the time into parts
361         let (s, ns) = div_rem(ns, 1_000_000_000);
362         let (m, s) = div_rem(s, 60);
363         let (h, m) = div_rem(m, 60);
364 
365         // Write HH:MM:SS
366         write!(writer, "{}:{:02}:{:02}", h, m, s)?;
367 
368         if precision > 0 {
369             // Format the nanoseconds into a stack-allocated string
370             // The value is zero-padded so always 9 digits long
371             let mut buf = [0u8; 9];
372             write!(&mut buf[..], "{:09}", ns).unwrap();
373             let buf_str = str::from_utf8(&buf[..]).unwrap();
374 
375             // Write decimal point and a prefix of the nanoseconds for more precision
376             write!(writer, ".{:.p$}", buf_str, p = precision)?;
377         }
378     } else {
379         // Undefined time
380 
381         // Write HH:MM:SS, but invalid
382         write!(writer, "--:--:--")?;
383 
384         if precision > 0 {
385             // Write decimal point and dashes for more precision
386             write!(writer, ".{:->p$}", "", p = precision)?;
387         }
388     }
389 
390     Ok(())
391 }
392 
fmt_opt_clock_time(ct: Option<ClockTime>, f: &mut fmt::Formatter) -> fmt::Result393 fn fmt_opt_clock_time(ct: Option<ClockTime>, f: &mut fmt::Formatter) -> fmt::Result {
394     skip_assert_initialized!();
395     let precision = f.precision().unwrap_or(9);
396 
397     // What the maximum time (u64::MAX - 1) would format to
398     const MAX_SIZE: usize = "5124095:34:33.709551614".len();
399 
400     // Write the unpadded clocktime value into a stack-allocated string
401     let mut buf = [0u8; MAX_SIZE];
402     let mut cursor = io::Cursor::new(&mut buf[..]);
403     write_clocktime(&mut cursor, ct, precision).unwrap();
404     let pos = cursor.position() as usize;
405     let buf_str = str::from_utf8(&buf[..pos]).unwrap();
406 
407     let sign = if ct.is_some() {
408         Sign::NonNegative
409     } else {
410         Sign::Undefined
411     };
412 
413     pad_clocktime(f, sign, buf_str)
414 }
415 
416 impl fmt::Display for ClockTime {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result417     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418         fmt_opt_clock_time(Some(*self), f)
419     }
420 }
421 
422 #[derive(Debug)]
423 pub struct DisplayableOptClockTime(Option<ClockTime>);
424 
425 impl fmt::Display for DisplayableOptClockTime {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result426     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427         fmt_opt_clock_time(self.0, f)
428     }
429 }
430 
431 impl crate::utils::Displayable for Option<ClockTime> {
432     type DisplayImpl = DisplayableOptClockTime;
433 
display(self) -> DisplayableOptClockTime434     fn display(self) -> DisplayableOptClockTime {
435         DisplayableOptClockTime(self)
436     }
437 }
438 
439 impl crate::utils::Displayable for ClockTime {
440     type DisplayImpl = ClockTime;
441 
display(self) -> ClockTime442     fn display(self) -> ClockTime {
443         self
444     }
445 }
446 
447 #[cfg(test)]
448 mod tests {
449     use super::*;
450 
451     #[test]
opt_time_clock()452     fn opt_time_clock() {
453         let ct_1 = ClockTime(1);
454         let opt_ct_none: Option<ClockTime> = None;
455 
456         assert_eq!(ct_1.into_glib(), 1);
457         assert_eq!(Some(ct_1).into_glib(), 1);
458         assert_eq!(opt_ct_none.into_glib(), ffi::GST_CLOCK_TIME_NONE);
459 
460         let ct_1_from: ClockTime = unsafe { try_from_glib(1u64) }.unwrap();
461         assert_eq!(ct_1_from, ct_1);
462 
463         let opt_ct_some: Option<ClockTime> = unsafe { from_glib(1u64) };
464         assert_eq!(opt_ct_some, Some(ct_1));
465 
466         let opt_ct_none: Option<ClockTime> = unsafe { from_glib(ffi::GST_CLOCK_TIME_NONE) };
467         assert_eq!(opt_ct_none, None);
468     }
469 
470     #[test]
471     #[allow(clippy::eq_op, clippy::op_ref)]
ops()472     fn ops() {
473         let ct_10 = 10 * ClockTime::MSECOND;
474         let ct_20 = 20 * ClockTime::MSECOND;
475         let ct_30 = 30 * ClockTime::MSECOND;
476 
477         assert_eq!(ct_10 + ct_20, ct_30);
478         assert_eq!(ct_10 + &ct_20, ct_30);
479         assert_eq!(&ct_10 + &ct_20, ct_30);
480         assert_eq!(ct_30 - ct_20, ct_10);
481         assert_eq!(ct_30 - ct_30, ClockTime::ZERO);
482         assert_eq!(ct_10 * 3, ct_30);
483         assert_eq!(3 * ct_10, ct_30);
484         assert_eq!(3 * &ct_10, ct_30);
485         assert_eq!(ct_30.nseconds(), 30_000_000);
486     }
487 
488     #[test]
checked_ops()489     fn checked_ops() {
490         let ct_1 = ClockTime::from_nseconds(1);
491         let ct_2 = ClockTime::from_nseconds(2);
492 
493         let ct_max = ClockTime::from_nseconds(std::u64::MAX);
494 
495         assert_eq!(ct_1.checked_add(ct_1), Some(ct_2));
496         assert_eq!(ct_1.checked_add(ct_1), Some(ct_2));
497         assert!(ct_max.checked_add(ct_1).is_none());
498 
499         assert_eq!(ct_2.checked_sub(ct_1), Some(ct_1));
500         assert_eq!(ct_2.checked_sub(ct_1), Some(ct_1));
501         assert!(ct_1.checked_sub(ct_2).is_none());
502     }
503 
504     #[test]
saturating_ops()505     fn saturating_ops() {
506         let ct_1 = ClockTime::from_nseconds(1);
507         let ct_2 = ClockTime::from_nseconds(2);
508         let ct_3 = ClockTime::from_nseconds(3);
509 
510         let ct_max = ClockTime::from_nseconds(std::u64::MAX);
511 
512         assert_eq!(ct_1.saturating_add(ct_2), ct_3);
513         assert_eq!(ct_1.saturating_add(ct_2), ct_3);
514         assert_eq!(ct_max.saturating_add(ct_1), ct_max);
515 
516         assert_eq!(ct_3.saturating_sub(ct_2), ct_1);
517         assert_eq!(ct_3.saturating_sub(ct_2), ct_1);
518         assert!(ct_1.saturating_sub(ct_2).is_zero());
519     }
520 
521     #[test]
wrapping_ops()522     fn wrapping_ops() {
523         let ct_1 = ClockTime::NSECOND;
524         let ct_2 = 2 * ClockTime::NSECOND;
525         let ct_3 = 3 * ClockTime::NSECOND;
526 
527         let ct_max = ClockTime::from_nseconds(std::u64::MAX);
528 
529         assert_eq!(ct_1.wrapping_add(ct_2), ct_3);
530         assert_eq!(ct_1.wrapping_add(ct_2), ct_3);
531         assert_eq!(ct_max.wrapping_add(ct_1), ClockTime::ZERO);
532 
533         assert_eq!(ct_3.wrapping_sub(ct_2), ct_1);
534         assert_eq!(ct_3.wrapping_sub(ct_2), ct_1);
535         assert_eq!(ct_1.wrapping_sub(ct_2), ct_max);
536     }
537 
538     #[test]
comp()539     fn comp() {
540         let ct_0 = ClockTime::ZERO;
541         let ct_2 = 2 * ClockTime::NSECOND;
542         let ct_3 = 3 * ClockTime::NSECOND;
543         let opt_ct_none: Option<ClockTime> = None;
544 
545         assert!(ct_0 < ct_2);
546         assert!(Some(ct_0) < Some(ct_2));
547         assert!(ct_2 < ct_3);
548         assert!(Some(ct_2) < Some(ct_3));
549         assert!(ct_0 < ct_3);
550         assert!(Some(ct_0) < Some(ct_3));
551 
552         assert!(ct_3 > ct_2);
553         assert!(Some(ct_3) > Some(ct_2));
554         assert!(ct_2 > ct_0);
555         assert!(Some(ct_2) > Some(ct_0));
556         assert!(ct_3 > ct_0);
557         assert!(Some(ct_3) > Some(ct_0));
558 
559         assert!(!(opt_ct_none < None));
560         assert!(!(opt_ct_none > None));
561         // This doesn't work due to the `PartialOrd` impl on `Option<T>`
562         //assert_eq!(Some(ct_0) > opt_ct_none, false);
563         assert!(!(Some(ct_0) < opt_ct_none));
564     }
565 
566     #[test]
display()567     fn display() {
568         let none = Option::<ClockTime>::None;
569         let some = Some(45_834_908_569_837 * ClockTime::NSECOND);
570         let lots = ClockTime::from_nseconds(std::u64::MAX - 1);
571 
572         // Simple
573 
574         assert_eq!(format!("{:.0}", DisplayableOptClockTime(none)), "--:--:--");
575         assert_eq!(
576             format!("{:.3}", DisplayableOptClockTime(none)),
577             "--:--:--.---"
578         );
579         assert_eq!(
580             format!("{}", DisplayableOptClockTime(none)),
581             "--:--:--.---------"
582         );
583 
584         assert_eq!(format!("{:.0}", DisplayableOptClockTime(some)), "12:43:54");
585         assert_eq!(
586             format!("{:.3}", DisplayableOptClockTime(some)),
587             "12:43:54.908"
588         );
589         assert_eq!(
590             format!("{}", DisplayableOptClockTime(some)),
591             "12:43:54.908569837"
592         );
593 
594         assert_eq!(format!("{:.0}", lots), "5124095:34:33");
595         assert_eq!(format!("{:.3}", lots), "5124095:34:33.709");
596         assert_eq!(format!("{}", lots), "5124095:34:33.709551614");
597 
598         // Precision caps at 9
599         assert_eq!(
600             format!("{:.10}", DisplayableOptClockTime(none)),
601             "--:--:--.---------"
602         );
603         assert_eq!(
604             format!("{:.10}", DisplayableOptClockTime(some)),
605             "12:43:54.908569837"
606         );
607         assert_eq!(format!("{:.10}", lots), "5124095:34:33.709551614");
608 
609         // Short width
610 
611         assert_eq!(format!("{:4.0}", DisplayableOptClockTime(none)), "--:--:--");
612         assert_eq!(
613             format!("{:4.3}", DisplayableOptClockTime(none)),
614             "--:--:--.---"
615         );
616         assert_eq!(
617             format!("{:4}", DisplayableOptClockTime(none)),
618             "--:--:--.---------"
619         );
620 
621         assert_eq!(format!("{:4.0}", DisplayableOptClockTime(some)), "12:43:54");
622         assert_eq!(
623             format!("{:4.3}", DisplayableOptClockTime(some)),
624             "12:43:54.908"
625         );
626         assert_eq!(
627             format!("{:4}", DisplayableOptClockTime(some)),
628             "12:43:54.908569837"
629         );
630 
631         assert_eq!(format!("{:4.0}", lots), "5124095:34:33");
632         assert_eq!(format!("{:4.3}", lots), "5124095:34:33.709");
633         assert_eq!(format!("{:4}", lots), "5124095:34:33.709551614");
634 
635         // Simple padding
636 
637         assert_eq!(
638             format!("{:>9.0}", DisplayableOptClockTime(none)),
639             " --:--:--"
640         );
641         assert_eq!(
642             format!("{:<9.0}", DisplayableOptClockTime(none)),
643             "--:--:-- "
644         );
645         assert_eq!(
646             format!("{:^10.0}", DisplayableOptClockTime(none)),
647             " --:--:-- "
648         );
649         assert_eq!(
650             format!("{:>13.3}", DisplayableOptClockTime(none)),
651             " --:--:--.---"
652         );
653         assert_eq!(
654             format!("{:<13.3}", DisplayableOptClockTime(none)),
655             "--:--:--.--- "
656         );
657         assert_eq!(
658             format!("{:^14.3}", DisplayableOptClockTime(none)),
659             " --:--:--.--- "
660         );
661         assert_eq!(
662             format!("{:>19}", DisplayableOptClockTime(none)),
663             " --:--:--.---------"
664         );
665         assert_eq!(
666             format!("{:<19}", DisplayableOptClockTime(none)),
667             "--:--:--.--------- "
668         );
669         assert_eq!(
670             format!("{:^20}", DisplayableOptClockTime(none)),
671             " --:--:--.--------- "
672         );
673 
674         assert_eq!(
675             format!("{:>9.0}", DisplayableOptClockTime(some)),
676             " 12:43:54"
677         );
678         assert_eq!(
679             format!("{:<9.0}", DisplayableOptClockTime(some)),
680             "12:43:54 "
681         );
682         assert_eq!(
683             format!("{:^10.0}", DisplayableOptClockTime(some)),
684             " 12:43:54 "
685         );
686         assert_eq!(
687             format!("{:>13.3}", DisplayableOptClockTime(some)),
688             " 12:43:54.908"
689         );
690         assert_eq!(
691             format!("{:<13.3}", DisplayableOptClockTime(some)),
692             "12:43:54.908 "
693         );
694         assert_eq!(
695             format!("{:^14.3}", DisplayableOptClockTime(some)),
696             " 12:43:54.908 "
697         );
698         assert_eq!(
699             format!("{:>19}", DisplayableOptClockTime(some)),
700             " 12:43:54.908569837"
701         );
702         assert_eq!(
703             format!("{:<19}", DisplayableOptClockTime(some)),
704             "12:43:54.908569837 "
705         );
706         assert_eq!(
707             format!("{:^20}", DisplayableOptClockTime(some)),
708             " 12:43:54.908569837 "
709         );
710 
711         assert_eq!(format!("{:>14.0}", lots), " 5124095:34:33");
712         assert_eq!(format!("{:<14.0}", lots), "5124095:34:33 ");
713         assert_eq!(format!("{:^15.0}", lots), " 5124095:34:33 ");
714         assert_eq!(format!("{:>18.3}", lots), " 5124095:34:33.709");
715         assert_eq!(format!("{:<18.3}", lots), "5124095:34:33.709 ");
716         assert_eq!(format!("{:^19.3}", lots), " 5124095:34:33.709 ");
717         assert_eq!(format!("{:>24}", lots), " 5124095:34:33.709551614");
718         assert_eq!(format!("{:<24}", lots), "5124095:34:33.709551614 ");
719         assert_eq!(format!("{:^25}", lots), " 5124095:34:33.709551614 ");
720 
721         // Padding with sign or zero-extension
722 
723         assert_eq!(
724             format!("{:+11.0}", DisplayableOptClockTime(none)),
725             "   --:--:--"
726         );
727         assert_eq!(
728             format!("{:011.0}", DisplayableOptClockTime(none)),
729             "-----:--:--"
730         );
731         assert_eq!(
732             format!("{:+011.0}", DisplayableOptClockTime(none)),
733             "-----:--:--"
734         );
735         assert_eq!(
736             format!("{:+15.3}", DisplayableOptClockTime(none)),
737             "   --:--:--.---"
738         );
739         assert_eq!(
740             format!("{:015.3}", DisplayableOptClockTime(none)),
741             "-----:--:--.---"
742         );
743         assert_eq!(
744             format!("{:+015.3}", DisplayableOptClockTime(none)),
745             "-----:--:--.---"
746         );
747         assert_eq!(
748             format!("{:+21}", DisplayableOptClockTime(none)),
749             "   --:--:--.---------"
750         );
751         assert_eq!(
752             format!("{:021}", DisplayableOptClockTime(none)),
753             "-----:--:--.---------"
754         );
755         assert_eq!(
756             format!("{:+021}", DisplayableOptClockTime(none)),
757             "-----:--:--.---------"
758         );
759 
760         assert_eq!(
761             format!("{:+11.0}", DisplayableOptClockTime(some)),
762             "  +12:43:54"
763         );
764         assert_eq!(
765             format!("{:011.0}", DisplayableOptClockTime(some)),
766             "00012:43:54"
767         );
768         assert_eq!(
769             format!("{:+011.0}", DisplayableOptClockTime(some)),
770             "+0012:43:54"
771         );
772         assert_eq!(
773             format!("{:+15.3}", DisplayableOptClockTime(some)),
774             "  +12:43:54.908"
775         );
776         assert_eq!(
777             format!("{:015.3}", DisplayableOptClockTime(some)),
778             "00012:43:54.908"
779         );
780         assert_eq!(
781             format!("{:+015.3}", DisplayableOptClockTime(some)),
782             "+0012:43:54.908"
783         );
784         assert_eq!(
785             format!("{:+21}", DisplayableOptClockTime(some)),
786             "  +12:43:54.908569837"
787         );
788         assert_eq!(
789             format!("{:021}", DisplayableOptClockTime(some)),
790             "00012:43:54.908569837"
791         );
792         assert_eq!(
793             format!("{:+021}", DisplayableOptClockTime(some)),
794             "+0012:43:54.908569837"
795         );
796 
797         assert_eq!(format!("{:+16.0}", lots), "  +5124095:34:33");
798         assert_eq!(format!("{:016.0}", lots), "0005124095:34:33");
799         assert_eq!(format!("{:+016.0}", lots), "+005124095:34:33");
800         assert_eq!(format!("{:+20.3}", lots), "  +5124095:34:33.709");
801         assert_eq!(format!("{:020.3}", lots), "0005124095:34:33.709");
802         assert_eq!(format!("{:+020.3}", lots), "+005124095:34:33.709");
803         assert_eq!(format!("{:+26}", lots), "  +5124095:34:33.709551614");
804         assert_eq!(format!("{:026}", lots), "0005124095:34:33.709551614");
805         assert_eq!(format!("{:+026}", lots), "+005124095:34:33.709551614");
806     }
807 }
808