1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
//! [`FromStr`] for the five time types, and the errors they reject with.
//!
//! Each impl is the inverse of the type's **exact** rendering — the one
//! `{:#}` writes, which is `{}` as well for [`Timebase`], [`Rate`] and
//! [`SignedDuration`], whose renderings have nothing to expand into. That is
//! the only rendering an inverse can exist for: [`Timestamp`]'s and
//! [`TimeRange`]'s default `{}` form is a clock truncated to milliseconds that
//! never names a timebase, so two different instants can share one rendering
//! and no parser can tell which was meant. Accepting it would mint a value
//! that does not compare equal to the one printed. See each impl for its
//! grammar.
//!
//! Each type rejects with **its own** error, named for the vocabulary it
//! wanted: a caller matching on a failed `Rate` parse should not have to read
//! a message about timebases, and the two rosters are disjoint on purpose.
use ;
use crate::;
/// The `num/den` half of the two rational grammars, scanned once so
/// [`Timebase`] and [`Rate`] cannot drift apart in what they accept.
///
/// Only the shape is decided here. The caller applies its own constructor as
/// the validator, because the invariants land in different types and are
/// reported under different errors.
/// Returned when a string is not a [`Timebase`] rendering.
///
/// Carries no detail: the grammar is two integers and a slash, or a name from
/// a fixed roster, so the input is its own diagnostic.
);
/// Returned when a string is not a [`Timestamp`] rendering.
///
/// Also returned for the readable `H:MM:SS.mmm` clock form, which is lossy
/// and therefore not parsed — see the [module docs](self).
);
/// Returned when a string is not a [`SignedDuration`] rendering.
///
/// Distinct from [`ParseTimestampError`] although the two grammars are the
/// same shape: a count and an instant are different vocabularies, and the
/// message says which one was expected.
);
/// Returned when a string is not a [`Rate`] rendering.
///
/// Carries no detail, as [`ParseTimebaseError`] does not: the grammar is two
/// integers and a slash, or a name from a fixed roster.
);
/// Returned when a string is not a [`TimeRange`] rendering.
///
/// Also returned when the endpoints parse but run backwards, which
/// [`TimeRange::try_new`] rejects.
);
/// Parses either a [well-known name](Timebase#the-well-known-roster) —
/// `MILLIS`, `MPEG_90K` — or `num/den`, the form [`Timebase`]'s `Display`
/// writes in both `{}` and `{:#}`.
///
/// The roster is tried first, via [`Timebase::from_name`], so the name arm
/// folds ASCII case as that door does — `millis` parses — and nothing else:
/// no alias, no separator guessing. Nothing in the roster contains a slash, so
/// the two arms cannot collide. It is an *input* convenience for
/// hand-written configuration
/// and command lines: `Display` still writes `num/den` for every value, so the
/// `Display` → `FromStr` round trip is unchanged and lossless. The reverse is
/// deliberately not injective — `"MILLIS"` and `"1/1000"` parse to the same
/// timebase, and [`Timebase::well_known_name`] is where the name goes to be
/// recovered.
///
/// Surrounding and interior whitespace is trimmed, so `1 / 1000` parses; on
/// the `num/den` arm the slash is required. The value is **not** reduced:
/// `2/4` parses to a numerator of 2 over a denominator of 4, which is what was
/// written, and what `Display` will write back.
///
/// [`Timestamp`], [`TimeRange`] and [`SignedDuration`] parse their timebase
/// half through this impl, so `12345 @ MPEG_90K` parses too. [`Rate`]'s roster
/// is **not** read here, nor this one there — see that impl for why.
///
/// # Errors
///
/// Returns [`ParseTimebaseError`] if the input is neither a roster name nor a
/// `num/den` pair: the slash is missing, either half is not an `i32`, or the
/// pair is one [`Timebase::try_new`] refuses — a negative numerator, or a
/// denominator that is zero or negative.
/// Parses either a [well-known rate name](Rate#the-well-known-roster) —
/// `FPS_29_97`, `FPS_24` — or `num/den`, the form [`Rate`]'s `Display` writes
/// in both `{}` and `{:#}`.
///
/// The two arms and their order are [`Timebase`]'s, over the *rate* roster:
/// the name arm is tried first, through [`Rate::from_name`], so it folds
/// ASCII case — `fps_29_97` parses — and nothing else. No rate name contains a
/// slash, so the arms cannot collide.
///
/// The rosters, though, are **disjoint on purpose**: `"MILLIS"` is not a rate
/// and `"FPS_24"` is not a timebase, and each door refuses the other's names.
/// A rate and a timebase are reciprocal readings of one rational, so a door
/// that read both would silently answer `1/24` where `24/1` was written.
/// [`Rate::to_timebase`] is the conversion, and it is asked for.
///
/// Whitespace is trimmed as it is on the timebase door, the value is not
/// reduced, and the name arm is an input convenience only: `Display` writes
/// `num/den` for every value, so the `Display` → `FromStr` round trip is
/// lossless and `"FPS_24"` and `"24/1"` land on the same rate.
///
/// # Errors
///
/// Returns [`ParseRateError`] if the input is neither a rate name nor a
/// `num/den` pair: the slash is missing, either half is not an `i32`, or the
/// pair is one [`Rate::try_fps`] refuses — a negative numerator, or a
/// denominator that is zero or negative.
/// Parses `pts @ num/den` — the form [`Timestamp`]'s `Display` writes under
/// `{:#}`.
///
/// Whitespace around each part is trimmed, so `12345@1/90000` parses as well
/// as `12345 @ 1/90000`. The default `{}` clock is **not** accepted: it is
/// truncated to milliseconds and names no timebase, so it cannot name back
/// the instant it was printed from.
///
/// # Errors
///
/// Returns [`ParseTimestampError`] if the `@` is missing, if the PTS is not
/// an `i64`, or if the timebase half is not one [`Timebase`] accepts.
/// Parses `ticks @ num/den` — the form [`SignedDuration`]'s `Display` writes,
/// under both `{}` and `{:#}`.
///
/// [`Timestamp`]'s grammar over a count: whitespace around each part is
/// trimmed, the timebase half goes through [`Timebase`]'s own impl, so
/// `-1500 @ MILLIS` parses, and the leading `-` is the count's, a timebase
/// having no sign to write.
///
/// The rendering is the same shape as [`Timestamp`]'s `{:#}`, so a string
/// alone does not say which type was printed; the type asked for decides, and
/// `"1500 @ 1/1000".parse::<SignedDuration>()` is a span however the string
/// was produced.
///
/// # Errors
///
/// Returns [`ParseSignedDurationError`] if the `@` is missing, if the count is
/// not an `i64`, or if the timebase half is not one [`Timebase`] accepts.
/// Parses `[start, end) @ num/den` — the form [`TimeRange`]'s `Display`
/// writes under `{:#}`.
///
/// The half-open brackets are required, in that asymmetry, because they are
/// what the rendering means. Whitespace around each part is trimmed. The
/// default `{}` form, a pair of clocks, is **not** accepted, for the reason
/// [`Timestamp`]'s is not.
///
/// # Errors
///
/// Returns [`ParseTimeRangeError`] if the bracket, comma, or `@` is missing,
/// if an endpoint is not an `i64`, if the timebase half is not one
/// [`Timebase`] accepts, or if the endpoints run backwards.