use super::{
format_padded, format_unpadded, Accuracy, DigitsFormat, TimeFormatter, DASH, MINUS,
SECONDS_PER_HOUR, SECONDS_PER_MINUTE,
};
use crate::TimeSpan;
use core::fmt::{Display, Formatter, Result};
pub struct TimeInner {
time: Option<TimeSpan>,
digits_format: DigitsFormat,
}
pub struct Time {
digits_format: DigitsFormat,
}
impl Time {
pub const fn new() -> Self {
Time {
digits_format: DigitsFormat::SingleDigitSeconds,
}
}
pub const fn with_digits_format(digits_format: DigitsFormat) -> Self {
Time { digits_format }
}
}
impl Default for Time {
fn default() -> Self {
Self::new()
}
}
impl TimeFormatter<'_> for Time {
type Inner = TimeInner;
fn format<T>(&self, time: T) -> Self::Inner
where
T: Into<Option<TimeSpan>>,
{
TimeInner {
time: time.into(),
digits_format: self.digits_format,
}
}
}
impl Display for TimeInner {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if let Some(time) = self.time {
let (total_seconds, nanoseconds) = time.to_seconds_and_subsec_nanoseconds();
let total_seconds = if (total_seconds | nanoseconds as i64) < 0 {
f.write_str(MINUS)?;
(-total_seconds) as u64
} else {
total_seconds as u64
};
let seconds = (total_seconds % SECONDS_PER_MINUTE) as u8;
let minutes = ((total_seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE) as u8;
let hours = total_seconds / SECONDS_PER_HOUR;
if self.digits_format == DigitsFormat::DoubleDigitHours {
let mut buffer = itoa::Buffer::new();
let hours = buffer.format(hours);
if hours.len() < 2 {
f.write_str("0")?;
}
f.write_str(hours)?;
f.write_str(":")?;
f.write_str(format_padded(minutes))?;
f.write_str(":")?;
f.write_str(format_padded(seconds))
} else if hours > 0 || self.digits_format == DigitsFormat::SingleDigitHours {
f.write_str(itoa::Buffer::new().format(hours))?;
f.write_str(":")?;
f.write_str(format_padded(minutes))?;
f.write_str(":")?;
f.write_str(format_padded(seconds))
} else if self.digits_format == DigitsFormat::DoubleDigitMinutes {
f.write_str(format_padded(minutes))?;
f.write_str(":")?;
f.write_str(format_padded(seconds))
} else if minutes > 0 || self.digits_format == DigitsFormat::SingleDigitMinutes {
f.write_str(format_unpadded(minutes))?;
f.write_str(":")?;
f.write_str(format_padded(seconds))
} else if self.digits_format == DigitsFormat::DoubleDigitSeconds {
f.write_str(format_padded(seconds))
} else {
f.write_str(format_unpadded(seconds))
}
} else {
f.write_str(DASH)
}
}
}
pub struct FractionInner {
time: Option<TimeSpan>,
accuracy: Accuracy,
}
pub struct Fraction {
accuracy: Accuracy,
}
impl Fraction {
pub const fn new() -> Self {
Fraction {
accuracy: Accuracy::Hundredths,
}
}
pub const fn with_accuracy(accuracy: Accuracy) -> Self {
Fraction { accuracy }
}
}
impl Default for Fraction {
fn default() -> Self {
Self::new()
}
}
impl TimeFormatter<'_> for Fraction {
type Inner = FractionInner;
fn format<T>(&self, time: T) -> Self::Inner
where
T: Into<Option<TimeSpan>>,
{
FractionInner {
time: time.into(),
accuracy: self.accuracy,
}
}
}
impl Display for FractionInner {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if let Some(time) = self.time {
let nanoseconds = time.to_duration().subsec_nanoseconds().unsigned_abs();
self.accuracy.format_nanoseconds(nanoseconds).fmt(f)
} else {
Ok(())
}
}
}
#[test]
fn test() {
let time = "4:20.999999".parse::<TimeSpan>().unwrap();
assert_eq!(Fraction::new().format(Some(time)).to_string(), ".99");
}