Skip to main content

moqtap_codec/draft23/
types.rs

1//! Draft-23 object status values.
2//!
3//! - 0x0 = Normal, may carry a payload
4//! - 0x3 = End of Group, may not
5//! - 0x4 = End of Track, may not
6//!
7//! The Object Status registry (Section 16.9) has a "Payload" column, carried
8//! here as a `PayloadPermission` on the status itself.
9//!
10//! Object Status is present only on subscription-delivered Objects: a fetch
11//! stream's objects carry no status field (Section 11.1.1).
12
13/// Whether an Object carrying a given status is permitted a non-empty payload:
14/// the "Payload" column of the Object Status registry, MoQ Transport draft-23
15/// Section 16.9, Table 17.
16///
17/// Draft-23 Section 11.1.1 phrases the rule as "An Object MUST have an empty
18/// payload unless its Object Status value is registered as permitting a
19/// payload", and Section 16.9 adds that each new registration "MUST indicate
20/// whether the status permits a payload". Modelling the column as a value keeps
21/// that obligation visible: a status cannot be added to [`ObjectStatus`]
22/// without [`ObjectStatus::payload_permission`] refusing to compile until its
23/// column is filled in.
24///
25/// Draft-23 Section 16.9 gives the registry this column.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum PayloadPermission {
28    /// Registry column "Yes". The status permits a payload but does not
29    /// require one: a zero-length Object with such a status is well formed.
30    Permitted,
31    /// Registry column "No". An Object with such a status has an empty
32    /// payload, and one carrying bytes is malformed.
33    Forbidden,
34}
35
36impl PayloadPermission {
37    /// `true` for [`PayloadPermission::Permitted`].
38    ///
39    /// The permission answers on its own, without a payload length in hand,
40    /// which is the point of moving the rule onto the status.
41    pub fn permits(self) -> bool {
42        matches!(self, PayloadPermission::Permitted)
43    }
44}
45
46/// Object status values, from MoQ Transport draft-23 Section 11.1.1
47/// "Object Status", with the same three code points listed in the IANA Object
48/// Status registry at Section 16.9, Table 17.
49///
50/// The draft assigns 0x0, 0x3 and 0x4. Of every other value the section says:
51/// "If an endpoint receives any other value, it SHOULD close the session with
52/// INVALID_OBJECT_STATUS (Section 12.2)." [`ObjectStatus::from_u64`]
53/// answers `None` for everything the draft leaves unassigned, 0x1 and 0x2
54/// included. The section also states plainly that there is no status meaning
55/// end of Subgroup: a subgroup ends when its stream is closed with a FIN.
56///
57/// Each status carries the registry's payload rule with it, as
58/// [`ObjectStatus::payload_permission`].
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60#[repr(u8)]
61pub enum ObjectStatus {
62    /// Normal object. The one status Table 17 marks "Payload: Yes", and the
63    /// status of every Object that carries bytes — the encodings elide it and
64    /// spell it out only when the payload is empty.
65    Normal = 0x0,
66    /// End of Group. No object with the given Group ID and an Object ID greater
67    /// than or equal to the one specified exists in that group. Table 17 marks
68    /// it "Payload: No".
69    EndOfGroup = 0x3,
70    /// End of Track. No object at a location equal to or greater than the one
71    /// specified exists. Table 17 marks it "Payload: No".
72    EndOfTrack = 0x4,
73}
74
75impl ObjectStatus {
76    /// Every status draft-23 assigns, in ascending wire order.
77    ///
78    /// This is exactly the set [`ObjectStatus::from_u64`] accepts, and exactly
79    /// the three rows of the draft's Object Status registry. Any other value is
80    /// one the draft does not assign.
81    pub const ALL: &[ObjectStatus] =
82        &[ObjectStatus::Normal, ObjectStatus::EndOfGroup, ObjectStatus::EndOfTrack];
83
84    /// Convert a raw u64 to an `ObjectStatus`, or `None` if draft-23 does not
85    /// assign that value.
86    pub fn from_u64(v: u64) -> Option<Self> {
87        match v {
88            0x0 => Some(ObjectStatus::Normal),
89            0x3 => Some(ObjectStatus::EndOfGroup),
90            0x4 => Some(ObjectStatus::EndOfTrack),
91            _ => None,
92        }
93    }
94
95    /// The registry's "Payload" column for this status, from draft-23
96    /// Section 16.9, Table 17: Normal is "Yes", End of Group and End of Track
97    /// are "No".
98    ///
99    /// This is the whole of the rule draft-23 Section 11.1.1 states — an
100    /// Object has an empty payload unless its status is registered as
101    /// permitting one — so no caller has to restate it, and none has to reach
102    /// for a payload length to guess at it. The three rows currently agree with
103    /// the blanket *any status other than Normal means an empty payload* that
104    /// drafts up to 18 used; they agree by coincidence of the current
105    /// assignments, not by construction, and a status registered later with
106    /// "Payload: Yes" would part them.
107    pub fn payload_permission(self) -> PayloadPermission {
108        match self {
109            ObjectStatus::Normal => PayloadPermission::Permitted,
110            ObjectStatus::EndOfGroup => PayloadPermission::Forbidden,
111            ObjectStatus::EndOfTrack => PayloadPermission::Forbidden,
112        }
113    }
114
115    /// `true` when the registry permits an Object with this status to carry a
116    /// non-empty payload. Shorthand for
117    /// `self.payload_permission().permits()`.
118    ///
119    /// Permitting is not requiring: a Normal Object with no payload is well
120    /// formed, and draft-23's encodings have a way to spell it.
121    pub fn permits_payload(self) -> bool {
122        self.payload_permission().permits()
123    }
124
125    /// Return the wire value.
126    pub fn as_u64(self) -> u64 {
127        self as u64
128    }
129
130    /// Return the wire value as a single byte.
131    ///
132    /// A draft-23 status datagram carries its status as one bare byte rather
133    /// than a varint, so the datagram encoder needs the code in that width;
134    /// every assigned code is well under 0xff, so this is the same number
135    /// [`ObjectStatus::as_u64`] returns.
136    pub fn as_u8(self) -> u8 {
137        self as u8
138    }
139}