livesplit_core/timing/formatter/
timer.rs1use super::{
6 format_padded, format_unpadded, Accuracy, DigitsFormat, TimeFormatter, DASH, MINUS,
7 SECONDS_PER_HOUR, SECONDS_PER_MINUTE,
8};
9use crate::TimeSpan;
10use core::fmt::{Display, Formatter, Result};
11
12pub struct TimeInner {
14 time: Option<TimeSpan>,
15 digits_format: DigitsFormat,
16}
17
18pub struct Time {
30 digits_format: DigitsFormat,
31}
32
33impl Time {
34 pub const fn new() -> Self {
36 Time {
37 digits_format: DigitsFormat::SingleDigitSeconds,
38 }
39 }
40
41 pub const fn with_digits_format(digits_format: DigitsFormat) -> Self {
45 Time { digits_format }
46 }
47}
48
49impl Default for Time {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl TimeFormatter<'_> for Time {
56 type Inner = TimeInner;
57
58 fn format<T>(&self, time: T) -> Self::Inner
59 where
60 T: Into<Option<TimeSpan>>,
61 {
62 TimeInner {
63 time: time.into(),
64 digits_format: self.digits_format,
65 }
66 }
67}
68
69impl Display for TimeInner {
70 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
71 if let Some(time) = self.time {
72 let (total_seconds, nanoseconds) = time.to_seconds_and_subsec_nanoseconds();
73 let total_seconds = if (total_seconds | nanoseconds as i64) < 0 {
74 f.write_str(MINUS)?;
75 (-total_seconds) as u64
76 } else {
77 total_seconds as u64
78 };
79 let seconds = (total_seconds % SECONDS_PER_MINUTE) as u8;
84 let minutes = ((total_seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE) as u8;
85 let hours = total_seconds / SECONDS_PER_HOUR;
86
87 if self.digits_format == DigitsFormat::DoubleDigitHours {
88 let mut buffer = itoa::Buffer::new();
89 let hours = buffer.format(hours);
90 if hours.len() < 2 {
91 f.write_str("0")?;
92 }
93 f.write_str(hours)?;
94 f.write_str(":")?;
95 f.write_str(format_padded(minutes))?;
96 f.write_str(":")?;
97 f.write_str(format_padded(seconds))
98 } else if hours > 0 || self.digits_format == DigitsFormat::SingleDigitHours {
99 f.write_str(itoa::Buffer::new().format(hours))?;
100 f.write_str(":")?;
101 f.write_str(format_padded(minutes))?;
102 f.write_str(":")?;
103 f.write_str(format_padded(seconds))
104 } else if self.digits_format == DigitsFormat::DoubleDigitMinutes {
105 f.write_str(format_padded(minutes))?;
106 f.write_str(":")?;
107 f.write_str(format_padded(seconds))
108 } else if minutes > 0 || self.digits_format == DigitsFormat::SingleDigitMinutes {
109 f.write_str(format_unpadded(minutes))?;
110 f.write_str(":")?;
111 f.write_str(format_padded(seconds))
112 } else if self.digits_format == DigitsFormat::DoubleDigitSeconds {
113 f.write_str(format_padded(seconds))
114 } else {
115 f.write_str(format_unpadded(seconds))
116 }
117 } else {
118 f.write_str(DASH)
119 }
120 }
121}
122
123pub struct FractionInner {
126 time: Option<TimeSpan>,
127 accuracy: Accuracy,
128}
129
130pub struct Fraction {
142 accuracy: Accuracy,
143}
144
145impl Fraction {
146 pub const fn new() -> Self {
149 Fraction {
150 accuracy: Accuracy::Hundredths,
151 }
152 }
153
154 pub const fn with_accuracy(accuracy: Accuracy) -> Self {
157 Fraction { accuracy }
158 }
159}
160
161impl Default for Fraction {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167impl TimeFormatter<'_> for Fraction {
168 type Inner = FractionInner;
169
170 fn format<T>(&self, time: T) -> Self::Inner
171 where
172 T: Into<Option<TimeSpan>>,
173 {
174 FractionInner {
175 time: time.into(),
176 accuracy: self.accuracy,
177 }
178 }
179}
180
181impl Display for FractionInner {
182 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
183 if let Some(time) = self.time {
184 let nanoseconds = time.to_duration().subsec_nanoseconds().unsigned_abs();
185 self.accuracy.format_nanoseconds(nanoseconds).fmt(f)
186 } else {
187 Ok(())
188 }
189 }
190}
191
192#[test]
193fn test() {
194 let time = "4:20.999999".parse::<TimeSpan>().unwrap();
195 assert_eq!(Fraction::new().format(Some(time)).to_string(), ".99");
196}