moqtap_codec/draft09/error_codes.rs
1//! The error, status and termination code registries that MoQ Transport
2//! draft-09 publishes as `Code`/`Reason` tables.
3//!
4//! Draft-09 predates the IANA registries introduced in later drafts. Every
5//! registry here is an inline two-column table with a `Code` column and a
6//! `Reason` column, and the draft assigns no ALLCAPS symbolic names to the code
7//! points. Each variant's doc comment therefore quotes the draft's `Reason`
8//! text verbatim so the mapping from the table to the Rust name is checkable by
9//! eye; where the draft explains a code in prose, that explanation follows.
10//!
11//! # Scope
12//!
13//! Draft-09 has six such tables and all six are transcribed here: Section 3.5
14//! (Termination), Section 7.10 (ANNOUNCE_ERROR), Section 7.16
15//! (SUBSCRIBE_ERROR), Section 7.18 (FETCH_ERROR), Section 7.19 (SUBSCRIBE_DONE)
16//! and Section 7.26 (SUBSCRIBE_ANNOUNCES_ERROR). Draft-09 assigns code points
17//! in two further places, neither of which is a `Code`/`Reason` table:
18//!
19//! - Section 7.24 assigns TRACK_STATUS Status Codes 0x00 through 0x04 in a
20//! prose list. That registry is closed — the draft says the field "MUST hold
21//! one of the following values. Any other value is a malformed message" —
22//! which is the opposite of the open registries below, so it is modelled
23//! apart from them, as [`TrackStatusCode`]. [`super::message::TrackStatus`]
24//! carries the field as a raw `VarInt`.
25//! - Section 8.1.1.1 assigns Object Status 0x0, 0x1, 0x3, 0x4 and 0x5 (0x2 is
26//! not assigned). That registry is [`super::types::ObjectStatus`], next to
27//! the data-stream code that reads it.
28//!
29//! ANNOUNCE_CANCEL carries an Error Code field with no table of its own, and
30//! Section 7.11 says why: "ANNOUNCE_CANCEL uses the same error codes as
31//! ANNOUNCE_ERROR". So its codes are [`AnnounceErrorCode`], and there is no
32//! separate registry to transcribe.
33//!
34//! # Unrecognised codes
35//!
36//! All six registries here are open: the draft says only that an application
37//! "SHOULD use a relevant error code", so a peer may send a code this draft
38//! does not define. `from_u64` returns `None` for an unrecognised value rather
39//! than failing or widening the enum.
40//!
41//! # These are six separate number spaces
42//!
43//! The registries are deliberately six distinct types because the same number
44//! means different things in each, and the differences are not intuitive:
45//!
46//! - The termination registry is offset by one against the other five.
47//! `Internal Error` is 0x1 in Section 3.5 but 0x0 in all five message-scoped
48//! registries, and `Unauthorized` is 0x2 in Section 3.5 but 0x1 in all five.
49//! Reusing a session code as a message code therefore shifts its meaning by a
50//! whole row rather than producing an obviously wrong value.
51//! - 0x2 is `Timeout` in ANNOUNCE_ERROR, SUBSCRIBE_ERROR, FETCH_ERROR and
52//! SUBSCRIBE_ANNOUNCES_ERROR, but `Track Ended` in SUBSCRIBE_DONE and
53//! `Unauthorized` in the termination registry.
54//! - 0x4 carries five distinct meanings across the six registries:
55//! `Duplicate Track Alias`, `Uninterested`, `Track Does Not Exist` (in both
56//! SUBSCRIBE_ERROR and FETCH_ERROR), `Going Away` and
57//! `Namespace Prefix Unknown`.
58//!
59//! `Unauthorized` also differs in meaning as well as in number: in
60//! [`SessionErrorCode`] it reports a breached agreement, which the
61//! message-scoped registries do not say.
62
63/// Session termination codes, from draft-09 Section 3.5 (Termination).
64///
65/// The draft introduces the table with "The application MAY use any error
66/// message and SHOULD use a relevant code, as defined below". These codes
67/// travel in the QUIC `CONNECTION_CLOSE` frame or the WebTransport
68/// `CLOSE_WEBTRANSPORT_SESSION` capsule.
69///
70/// Section 3.5 assigns 0x0 through 0x6 and then jumps to 0x10 through 0x12 for
71/// the three timeouts; 0x7 through 0xF are unassigned. The gap is the draft's
72/// own, so `GOAWAY Timeout` is 0x10 and not 0x7.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74#[repr(u64)]
75pub enum SessionErrorCode {
76 /// `No Error` — The session is being terminated without an error.
77 NoError = 0x0,
78 /// `Internal Error` — An implementation specific error occurred.
79 InternalError = 0x1,
80 /// `Unauthorized` — The endpoint breached an agreement, which MAY have been
81 /// pre-negotiated by the application.
82 Unauthorized = 0x2,
83 /// `Protocol Violation` — The remote endpoint performed an action that was
84 /// disallowed by the specification.
85 ProtocolViolation = 0x3,
86 /// `Duplicate Track Alias` — The endpoint attempted to use a Track Alias
87 /// that was already in use.
88 DuplicateTrackAlias = 0x4,
89 /// `Parameter Length Mismatch` — the Section 3.5 table assigns this code but
90 /// the list of descriptions that follows the table skips it. Section 7.1
91 /// supplies the meaning: if a receiver understands a parameter type, and the
92 /// parameter length implied by that type does not match the Parameter Length
93 /// field, the receiver MUST terminate the session with this code.
94 ParameterLengthMismatch = 0x5,
95 /// `Too Many Subscribes` — The session was closed because the subscriber
96 /// used a Subscribe ID equal or larger than the current Maximum Subscribe
97 /// ID.
98 TooManySubscribes = 0x6,
99 /// `GOAWAY Timeout` — The session was closed because the peer took too long
100 /// to close the session in response to a GOAWAY (Section 7.3) message. See
101 /// session migration (Section 3.6).
102 GoawayTimeout = 0x10,
103 /// `Control Message Timeout` — The session was closed because the peer took
104 /// too long to respond to a control message.
105 ControlMessageTimeout = 0x11,
106 /// `Data Stream Timeout` — The session was closed because the peer took too
107 /// long to send data expected on an open Data Stream (Section 8). This
108 /// includes fields of a stream header or an object header within a data
109 /// stream. If an endpoint times out waiting for a new object header on an
110 /// open subgroup stream, it MAY send a STOP_SENDING on that stream,
111 /// terminate the subscription, or close the session with an error.
112 DataStreamTimeout = 0x12,
113}
114
115/// ANNOUNCE_ERROR codes, from draft-09 Section 7.10 (ANNOUNCE_ERROR).
116///
117/// The draft introduces the table with "The application SHOULD use a relevant
118/// error code in ANNOUNCE_ERROR, as defined below" and gives no prose beyond the
119/// `Reason` column for any of these codes.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121#[repr(u64)]
122pub enum AnnounceErrorCode {
123 /// `Internal Error`.
124 InternalError = 0x0,
125 /// `Unauthorized`.
126 Unauthorized = 0x1,
127 /// `Timeout`.
128 Timeout = 0x2,
129 /// `Not Supported`.
130 NotSupported = 0x3,
131 /// `Uninterested`.
132 Uninterested = 0x4,
133}
134
135/// SUBSCRIBE_ERROR codes, from draft-09 Section 7.16 (SUBSCRIBE_ERROR).
136///
137/// The draft introduces the table with "The application SHOULD use a relevant
138/// error code in SUBSCRIBE_ERROR, as defined below".
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140#[repr(u64)]
141pub enum SubscribeErrorCode {
142 /// `Internal Error`.
143 InternalError = 0x0,
144 /// `Unauthorized`.
145 Unauthorized = 0x1,
146 /// `Timeout`.
147 Timeout = 0x2,
148 /// `Not Supported`.
149 NotSupported = 0x3,
150 /// `Track Does Not Exist`.
151 TrackDoesNotExist = 0x4,
152 /// `Invalid Range` — Section 7.4 adds that if a publisher cannot satisfy
153 /// the requested start or end, or if the end has already been published, it
154 /// SHOULD send a SUBSCRIBE_ERROR with this code.
155 InvalidRange = 0x5,
156 /// `Retry Track Alias` — Section 7.16 adds that the subscriber SHOULD
157 /// re-issue the SUBSCRIBE with the Track Alias carried in the
158 /// SUBSCRIBE_ERROR message instead. If that Track Alias is already in use,
159 /// the subscriber MUST close the connection with a Duplicate Track Alias
160 /// error (Section 3.5).
161 RetryTrackAlias = 0x6,
162}
163
164/// FETCH_ERROR codes, from draft-09 Section 7.18 (FETCH_ERROR).
165///
166/// The draft introduces the table with "The application SHOULD use a relevant
167/// error code in FETCH_ERROR, as defined below" and gives no prose beyond the
168/// `Reason` column for any of these codes. The table stops at `Invalid Range`:
169/// unlike SUBSCRIBE_ERROR, FETCH_ERROR has no `Retry Track Alias` code in this
170/// draft.
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172#[repr(u64)]
173pub enum FetchErrorCode {
174 /// `Internal Error`.
175 InternalError = 0x0,
176 /// `Unauthorized`.
177 Unauthorized = 0x1,
178 /// `Timeout`.
179 Timeout = 0x2,
180 /// `Not Supported`.
181 NotSupported = 0x3,
182 /// `Track Does Not Exist`.
183 TrackDoesNotExist = 0x4,
184 /// `Invalid Range`.
185 InvalidRange = 0x5,
186}
187
188/// SUBSCRIBE_DONE status codes, from draft-09 Section 7.19 (SUBSCRIBE_DONE).
189///
190/// The draft introduces the table with "The application SHOULD use a relevant
191/// status code in SUBSCRIBE_DONE, as defined below"; the table's second column
192/// is still headed `Reason`. The Status Code indicates why the subscription
193/// ended, and whether it was an error.
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195#[repr(u64)]
196pub enum SubscribeDoneStatusCode {
197 /// `Internal Error`.
198 InternalError = 0x0,
199 /// `Unauthorized`.
200 Unauthorized = 0x1,
201 /// `Track Ended`.
202 TrackEnded = 0x2,
203 /// `Subscription Ended`.
204 SubscriptionEnded = 0x3,
205 /// `Going Away`.
206 GoingAway = 0x4,
207 /// `Expired`.
208 Expired = 0x5,
209 /// `Too Far Behind` — Section 7.1.1.2 adds that if a subscriber exceeds the
210 /// publisher's resource limits by failing to consume objects at a sufficient
211 /// rate, the publisher MAY terminate the subscription with this code.
212 TooFarBehind = 0x6,
213}
214
215/// SUBSCRIBE_ANNOUNCES_ERROR codes, from draft-09 Section 7.26
216/// (SUBSCRIBE_ANNOUNCES_ERROR).
217///
218/// The draft introduces the table with "The application SHOULD use a relevant
219/// error code in SUBSCRIBE_ANNOUNCES_ERROR, as defined below" and gives no prose
220/// beyond the `Reason` column for any of these codes.
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222#[repr(u64)]
223pub enum SubscribeAnnouncesErrorCode {
224 /// `Internal Error`.
225 InternalError = 0x0,
226 /// `Unauthorized`.
227 Unauthorized = 0x1,
228 /// `Timeout`.
229 Timeout = 0x2,
230 /// `Not Supported`.
231 NotSupported = 0x3,
232 /// `Namespace Prefix Unknown`.
233 NamespacePrefixUnknown = 0x4,
234}
235
236impl SessionErrorCode {
237 /// Every session termination code draft-09 assigns, in ascending wire order.
238 ///
239 /// This is the set [`Self::from_u64`] accepts, written out so that it can
240 /// be enumerated: nothing can iterate an enum's variants, so a caller that
241 /// wants the registry has to be handed it. Writing it down is also what
242 /// lets a test state its claims about the registry itself rather than about
243 /// the range some sweep happens to reach.
244 pub const ALL: &[SessionErrorCode] = &[
245 SessionErrorCode::NoError,
246 SessionErrorCode::InternalError,
247 SessionErrorCode::Unauthorized,
248 SessionErrorCode::ProtocolViolation,
249 SessionErrorCode::DuplicateTrackAlias,
250 SessionErrorCode::ParameterLengthMismatch,
251 SessionErrorCode::TooManySubscribes,
252 SessionErrorCode::GoawayTimeout,
253 SessionErrorCode::ControlMessageTimeout,
254 SessionErrorCode::DataStreamTimeout,
255 ];
256
257 /// Convert a raw u64 to a `SessionErrorCode`, if draft-09 defines it.
258 pub fn from_u64(v: u64) -> Option<Self> {
259 match v {
260 0x0 => Some(SessionErrorCode::NoError),
261 0x1 => Some(SessionErrorCode::InternalError),
262 0x2 => Some(SessionErrorCode::Unauthorized),
263 0x3 => Some(SessionErrorCode::ProtocolViolation),
264 0x4 => Some(SessionErrorCode::DuplicateTrackAlias),
265 0x5 => Some(SessionErrorCode::ParameterLengthMismatch),
266 0x6 => Some(SessionErrorCode::TooManySubscribes),
267 0x10 => Some(SessionErrorCode::GoawayTimeout),
268 0x11 => Some(SessionErrorCode::ControlMessageTimeout),
269 0x12 => Some(SessionErrorCode::DataStreamTimeout),
270 _ => None,
271 }
272 }
273
274 /// Return the raw u64 value of this error code.
275 pub fn as_u64(self) -> u64 {
276 self as u64
277 }
278}
279
280impl AnnounceErrorCode {
281 /// Every ANNOUNCE_ERROR code draft-09 assigns, in ascending wire order.
282 ///
283 /// This is the set [`Self::from_u64`] accepts, written out so that it can
284 /// be enumerated: nothing can iterate an enum's variants, so a caller that
285 /// wants the registry has to be handed it. Writing it down is also what
286 /// lets a test state its claims about the registry itself rather than about
287 /// the range some sweep happens to reach.
288 pub const ALL: &[AnnounceErrorCode] = &[
289 AnnounceErrorCode::InternalError,
290 AnnounceErrorCode::Unauthorized,
291 AnnounceErrorCode::Timeout,
292 AnnounceErrorCode::NotSupported,
293 AnnounceErrorCode::Uninterested,
294 ];
295
296 /// Convert a raw u64 to an `AnnounceErrorCode`, if draft-09 defines it.
297 pub fn from_u64(v: u64) -> Option<Self> {
298 match v {
299 0x0 => Some(AnnounceErrorCode::InternalError),
300 0x1 => Some(AnnounceErrorCode::Unauthorized),
301 0x2 => Some(AnnounceErrorCode::Timeout),
302 0x3 => Some(AnnounceErrorCode::NotSupported),
303 0x4 => Some(AnnounceErrorCode::Uninterested),
304 _ => None,
305 }
306 }
307
308 /// Return the raw u64 value of this error code.
309 pub fn as_u64(self) -> u64 {
310 self as u64
311 }
312}
313
314impl SubscribeErrorCode {
315 /// Every SUBSCRIBE_ERROR code draft-09 assigns, in ascending wire order.
316 ///
317 /// This is the set [`Self::from_u64`] accepts, written out so that it can
318 /// be enumerated: nothing can iterate an enum's variants, so a caller that
319 /// wants the registry has to be handed it. Writing it down is also what
320 /// lets a test state its claims about the registry itself rather than about
321 /// the range some sweep happens to reach.
322 pub const ALL: &[SubscribeErrorCode] = &[
323 SubscribeErrorCode::InternalError,
324 SubscribeErrorCode::Unauthorized,
325 SubscribeErrorCode::Timeout,
326 SubscribeErrorCode::NotSupported,
327 SubscribeErrorCode::TrackDoesNotExist,
328 SubscribeErrorCode::InvalidRange,
329 SubscribeErrorCode::RetryTrackAlias,
330 ];
331
332 /// Convert a raw u64 to a `SubscribeErrorCode`, if draft-09 defines it.
333 pub fn from_u64(v: u64) -> Option<Self> {
334 match v {
335 0x0 => Some(SubscribeErrorCode::InternalError),
336 0x1 => Some(SubscribeErrorCode::Unauthorized),
337 0x2 => Some(SubscribeErrorCode::Timeout),
338 0x3 => Some(SubscribeErrorCode::NotSupported),
339 0x4 => Some(SubscribeErrorCode::TrackDoesNotExist),
340 0x5 => Some(SubscribeErrorCode::InvalidRange),
341 0x6 => Some(SubscribeErrorCode::RetryTrackAlias),
342 _ => None,
343 }
344 }
345
346 /// Return the raw u64 value of this error code.
347 pub fn as_u64(self) -> u64 {
348 self as u64
349 }
350}
351
352impl FetchErrorCode {
353 /// Every FETCH_ERROR code draft-09 assigns, in ascending wire order.
354 ///
355 /// This is the set [`Self::from_u64`] accepts, written out so that it can
356 /// be enumerated: nothing can iterate an enum's variants, so a caller that
357 /// wants the registry has to be handed it. Writing it down is also what
358 /// lets a test state its claims about the registry itself rather than about
359 /// the range some sweep happens to reach.
360 pub const ALL: &[FetchErrorCode] = &[
361 FetchErrorCode::InternalError,
362 FetchErrorCode::Unauthorized,
363 FetchErrorCode::Timeout,
364 FetchErrorCode::NotSupported,
365 FetchErrorCode::TrackDoesNotExist,
366 FetchErrorCode::InvalidRange,
367 ];
368
369 /// Convert a raw u64 to a `FetchErrorCode`, if draft-09 defines it.
370 pub fn from_u64(v: u64) -> Option<Self> {
371 match v {
372 0x0 => Some(FetchErrorCode::InternalError),
373 0x1 => Some(FetchErrorCode::Unauthorized),
374 0x2 => Some(FetchErrorCode::Timeout),
375 0x3 => Some(FetchErrorCode::NotSupported),
376 0x4 => Some(FetchErrorCode::TrackDoesNotExist),
377 0x5 => Some(FetchErrorCode::InvalidRange),
378 _ => None,
379 }
380 }
381
382 /// Return the raw u64 value of this error code.
383 pub fn as_u64(self) -> u64 {
384 self as u64
385 }
386}
387
388impl SubscribeDoneStatusCode {
389 /// Every SUBSCRIBE_DONE status code draft-09 assigns, in ascending wire order.
390 ///
391 /// This is the set [`Self::from_u64`] accepts, written out so that it can
392 /// be enumerated: nothing can iterate an enum's variants, so a caller that
393 /// wants the registry has to be handed it. Writing it down is also what
394 /// lets a test state its claims about the registry itself rather than about
395 /// the range some sweep happens to reach.
396 pub const ALL: &[SubscribeDoneStatusCode] = &[
397 SubscribeDoneStatusCode::InternalError,
398 SubscribeDoneStatusCode::Unauthorized,
399 SubscribeDoneStatusCode::TrackEnded,
400 SubscribeDoneStatusCode::SubscriptionEnded,
401 SubscribeDoneStatusCode::GoingAway,
402 SubscribeDoneStatusCode::Expired,
403 SubscribeDoneStatusCode::TooFarBehind,
404 ];
405
406 /// Convert a raw u64 to a `SubscribeDoneStatusCode`, if draft-09 defines it.
407 pub fn from_u64(v: u64) -> Option<Self> {
408 match v {
409 0x0 => Some(SubscribeDoneStatusCode::InternalError),
410 0x1 => Some(SubscribeDoneStatusCode::Unauthorized),
411 0x2 => Some(SubscribeDoneStatusCode::TrackEnded),
412 0x3 => Some(SubscribeDoneStatusCode::SubscriptionEnded),
413 0x4 => Some(SubscribeDoneStatusCode::GoingAway),
414 0x5 => Some(SubscribeDoneStatusCode::Expired),
415 0x6 => Some(SubscribeDoneStatusCode::TooFarBehind),
416 _ => None,
417 }
418 }
419
420 /// Return the raw u64 value of this status code.
421 pub fn as_u64(self) -> u64 {
422 self as u64
423 }
424}
425
426impl SubscribeAnnouncesErrorCode {
427 /// Every SUBSCRIBE_ANNOUNCES_ERROR code draft-09 assigns, in ascending wire order.
428 ///
429 /// This is the set [`Self::from_u64`] accepts, written out so that it can
430 /// be enumerated: nothing can iterate an enum's variants, so a caller that
431 /// wants the registry has to be handed it. Writing it down is also what
432 /// lets a test state its claims about the registry itself rather than about
433 /// the range some sweep happens to reach.
434 pub const ALL: &[SubscribeAnnouncesErrorCode] = &[
435 SubscribeAnnouncesErrorCode::InternalError,
436 SubscribeAnnouncesErrorCode::Unauthorized,
437 SubscribeAnnouncesErrorCode::Timeout,
438 SubscribeAnnouncesErrorCode::NotSupported,
439 SubscribeAnnouncesErrorCode::NamespacePrefixUnknown,
440 ];
441
442 /// Convert a raw u64 to a `SubscribeAnnouncesErrorCode`, if draft-09 defines
443 /// it.
444 pub fn from_u64(v: u64) -> Option<Self> {
445 match v {
446 0x0 => Some(SubscribeAnnouncesErrorCode::InternalError),
447 0x1 => Some(SubscribeAnnouncesErrorCode::Unauthorized),
448 0x2 => Some(SubscribeAnnouncesErrorCode::Timeout),
449 0x3 => Some(SubscribeAnnouncesErrorCode::NotSupported),
450 0x4 => Some(SubscribeAnnouncesErrorCode::NamespacePrefixUnknown),
451 _ => None,
452 }
453 }
454
455 /// Return the raw u64 value of this error code.
456 pub fn as_u64(self) -> u64 {
457 self as u64
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464
465 #[test]
466 fn session_error_code_roundtrip() {
467 for code in [
468 SessionErrorCode::NoError,
469 SessionErrorCode::InternalError,
470 SessionErrorCode::Unauthorized,
471 SessionErrorCode::ProtocolViolation,
472 SessionErrorCode::DuplicateTrackAlias,
473 SessionErrorCode::ParameterLengthMismatch,
474 SessionErrorCode::TooManySubscribes,
475 SessionErrorCode::GoawayTimeout,
476 SessionErrorCode::ControlMessageTimeout,
477 SessionErrorCode::DataStreamTimeout,
478 ] {
479 assert_eq!(SessionErrorCode::from_u64(code.as_u64()), Some(code));
480 }
481 }
482
483 #[test]
484 fn unknown_codes_are_none() {
485 // 0x7..=0xF are unassigned in draft-09's termination table, and the
486 // table stops at 0x12. Assert over the whole gap rather than a sample.
487 for code in 0x7..=0xF {
488 assert_eq!(SessionErrorCode::from_u64(code), None, "0x{code:x}");
489 }
490 assert_eq!(SessionErrorCode::from_u64(0x13), None);
491 assert_eq!(AnnounceErrorCode::from_u64(0x5), None);
492 assert_eq!(SubscribeErrorCode::from_u64(0x7), None);
493 assert_eq!(FetchErrorCode::from_u64(0x6), None);
494 assert_eq!(SubscribeDoneStatusCode::from_u64(0x7), None);
495 assert_eq!(SubscribeAnnouncesErrorCode::from_u64(0x5), None);
496 assert_eq!(SessionErrorCode::from_u64(u64::MAX), None);
497 }
498
499 #[test]
500 fn fetch_error_has_no_retry_track_alias() {
501 // draft-09 gives SUBSCRIBE_ERROR 0x6 but stops FETCH_ERROR at 0x5.
502 assert_eq!(SubscribeErrorCode::from_u64(0x6), Some(SubscribeErrorCode::RetryTrackAlias));
503 assert_eq!(FetchErrorCode::from_u64(0x6), None);
504 }
505}
506
507/// TRACK_STATUS Status Code values (draft-09, Section 7.24).
508///
509/// The draft defines these as a prose list rather than as a `Code`/`Reason`
510/// table, so they are named from the prose. It is stricter about this field
511/// than about the error registries above: the Status Code "MUST hold one of the
512/// following values" and "Any other value in the Status Code field is a
513/// malformed message", so [`TrackStatusCode::from_u64`] answering `None` is a
514/// decode failure rather than a merely unrecognised code.
515#[derive(Debug, Clone, Copy, PartialEq, Eq)]
516#[repr(u64)]
517pub enum TrackStatusCode {
518 /// The track is in progress, and subsequent fields contain the highest
519 /// group and object ID for that track.
520 InProgress = 0x00,
521 /// The track does not exist. Subsequent fields MUST be zero, and any other
522 /// value is a malformed message.
523 TrackDoesNotExist = 0x01,
524 /// The track has not yet begun. Subsequent fields MUST be zero, and any
525 /// other value is a malformed message.
526 NotYetBegun = 0x02,
527 /// The track has finished, so there is no live edge. Subsequent fields
528 /// contain the highest group and object ID known.
529 Finished = 0x03,
530 /// The publisher is a relay that cannot obtain the current track status
531 /// from upstream. Subsequent fields contain the largest group and object
532 /// ID known.
533 RelayStatusUnavailable = 0x04,
534}
535
536impl TrackStatusCode {
537 /// Convert a raw u64 to a `TrackStatusCode`, if this draft assigns it.
538 pub fn from_u64(v: u64) -> Option<Self> {
539 match v {
540 0x00 => Some(TrackStatusCode::InProgress),
541 0x01 => Some(TrackStatusCode::TrackDoesNotExist),
542 0x02 => Some(TrackStatusCode::NotYetBegun),
543 0x03 => Some(TrackStatusCode::Finished),
544 0x04 => Some(TrackStatusCode::RelayStatusUnavailable),
545 _ => None,
546 }
547 }
548
549 /// Whether this code requires the fields after it to be zero.
550 ///
551 /// Section 7.24 says of 0x01 "Subsequent fields MUST be zero, and any other
552 /// value is a malformed message", and the same of 0x02. The other three
553 /// codes describe those fields as carrying a real location, so they place
554 /// no requirement on them.
555 pub fn requires_zero_location(self) -> bool {
556 matches!(self, TrackStatusCode::TrackDoesNotExist | TrackStatusCode::NotYetBegun)
557 }
558
559 /// Return the raw u64 value of this status code.
560 pub fn as_u64(self) -> u64 {
561 self as u64
562 }
563}