Skip to main content

moq_transport/message/
track_status.rs

1use crate::coding::{
2    Decode, DecodeError, Encode, EncodeError, KeyValuePairs, Location, TrackNamespace,
3};
4use crate::message::FilterType;
5use crate::message::GroupOrder;
6
7/// A potential subscriber sends a TrackStatus message to obtain information about
8/// the current status of a given track.
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct TrackStatus {
11    /// The subscription request ID
12    pub id: u64,
13
14    /// Track properties
15    pub track_namespace: TrackNamespace,
16    pub track_name: String, // TODO SLG - consider making a FullTrackName base struct (total size limit of 4096)
17
18    /// Subscriber Priority
19    pub subscriber_priority: u8,
20    pub group_order: GroupOrder,
21
22    /// Forward Flag
23    pub forward: bool,
24
25    /// Filter type
26    pub filter_type: FilterType,
27
28    /// The starting location for this subscription. Only present for "AbsoluteStart" and "AbsoluteRange" filter types.
29    pub start_location: Option<Location>,
30    /// End group id, inclusive, for the subscription, if applicable. Only present for "AbsoluteRange" filter type.
31    pub end_group_id: Option<u64>,
32
33    /// Optional parameters
34    pub params: KeyValuePairs,
35}
36
37impl Decode for TrackStatus {
38    fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
39        let id = u64::decode(r)?;
40
41        let track_namespace = TrackNamespace::decode(r)?;
42        let track_name = String::decode(r)?;
43
44        let subscriber_priority = u8::decode(r)?;
45        let group_order = GroupOrder::decode(r)?;
46
47        let forward = bool::decode(r)?;
48
49        let filter_type = FilterType::decode(r)?;
50        let start_location: Option<Location>;
51        let end_group_id: Option<u64>;
52        match filter_type {
53            FilterType::AbsoluteStart => {
54                start_location = Some(Location::decode(r)?);
55                end_group_id = None;
56            }
57            FilterType::AbsoluteRange => {
58                start_location = Some(Location::decode(r)?);
59                end_group_id = Some(u64::decode(r)?);
60            }
61            _ => {
62                start_location = None;
63                end_group_id = None;
64            }
65        }
66
67        let params = KeyValuePairs::decode(r)?;
68
69        Ok(Self {
70            id,
71            track_namespace,
72            track_name,
73            subscriber_priority,
74            group_order,
75            forward,
76            filter_type,
77            start_location,
78            end_group_id,
79            params,
80        })
81    }
82}
83
84impl Encode for TrackStatus {
85    fn encode<W: bytes::BufMut>(&self, w: &mut W) -> Result<(), EncodeError> {
86        self.id.encode(w)?;
87
88        self.track_namespace.encode(w)?;
89        self.track_name.encode(w)?;
90
91        self.subscriber_priority.encode(w)?;
92        self.group_order.encode(w)?;
93
94        self.forward.encode(w)?;
95
96        self.filter_type.encode(w)?;
97        match self.filter_type {
98            FilterType::AbsoluteStart => {
99                if let Some(start) = &self.start_location {
100                    start.encode(w)?;
101                } else {
102                    return Err(EncodeError::MissingField("LargestLocation".to_string()));
103                }
104                // Just ignore end_group_id if it happens to be set
105            }
106            FilterType::AbsoluteRange => {
107                if let Some(start) = &self.start_location {
108                    start.encode(w)?;
109                } else {
110                    return Err(EncodeError::MissingField("LargestLocation".to_string()));
111                }
112                if let Some(end) = self.end_group_id {
113                    end.encode(w)?;
114                } else {
115                    return Err(EncodeError::MissingField("EndGroupId".to_string()));
116                }
117            }
118            _ => {}
119        }
120
121        self.params.encode(w)?;
122
123        Ok(())
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use bytes::BytesMut;
131
132    #[test]
133    fn encode_decode() {
134        let mut buf = BytesMut::new();
135
136        // One parameter for testing
137        let mut kvps = KeyValuePairs::new();
138        kvps.set_bytesvalue(123, vec![0x00, 0x01, 0x02, 0x03]);
139
140        // FilterType = NextGroupStart
141        let msg = TrackStatus {
142            id: 12345,
143            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
144            track_name: "audiotrack".to_string(),
145            subscriber_priority: 127,
146            group_order: GroupOrder::Publisher,
147            forward: true,
148            filter_type: FilterType::NextGroupStart,
149            start_location: None,
150            end_group_id: None,
151            params: kvps.clone(),
152        };
153        msg.encode(&mut buf).unwrap();
154        let decoded = TrackStatus::decode(&mut buf).unwrap();
155        assert_eq!(decoded, msg);
156
157        // FilterType = AbsoluteStart
158        let msg = TrackStatus {
159            id: 12345,
160            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
161            track_name: "audiotrack".to_string(),
162            subscriber_priority: 127,
163            group_order: GroupOrder::Publisher,
164            forward: true,
165            filter_type: FilterType::AbsoluteStart,
166            start_location: Some(Location::new(12345, 67890)),
167            end_group_id: None,
168            params: kvps.clone(),
169        };
170        msg.encode(&mut buf).unwrap();
171        let decoded = TrackStatus::decode(&mut buf).unwrap();
172        assert_eq!(decoded, msg);
173
174        // FilterType = AbsoluteRange
175        let msg = TrackStatus {
176            id: 12345,
177            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
178            track_name: "audiotrack".to_string(),
179            subscriber_priority: 127,
180            group_order: GroupOrder::Publisher,
181            forward: true,
182            filter_type: FilterType::AbsoluteRange,
183            start_location: Some(Location::new(12345, 67890)),
184            end_group_id: Some(23456),
185            params: kvps.clone(),
186        };
187        msg.encode(&mut buf).unwrap();
188        let decoded = TrackStatus::decode(&mut buf).unwrap();
189        assert_eq!(decoded, msg);
190    }
191
192    #[test]
193    fn encode_missing_fields() {
194        let mut buf = BytesMut::new();
195
196        // FilterType = AbsoluteStart - missing start_location
197        let msg = TrackStatus {
198            id: 12345,
199            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
200            track_name: "audiotrack".to_string(),
201            subscriber_priority: 127,
202            group_order: GroupOrder::Publisher,
203            forward: true,
204            filter_type: FilterType::AbsoluteStart,
205            start_location: None,
206            end_group_id: None,
207            params: Default::default(),
208        };
209        let encoded = msg.encode(&mut buf);
210        assert!(matches!(encoded.unwrap_err(), EncodeError::MissingField(_)));
211
212        // FilterType = AbsoluteRange - missing start_location
213        let msg = TrackStatus {
214            id: 12345,
215            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
216            track_name: "audiotrack".to_string(),
217            subscriber_priority: 127,
218            group_order: GroupOrder::Publisher,
219            forward: true,
220            filter_type: FilterType::AbsoluteRange,
221            start_location: None,
222            end_group_id: None,
223            params: Default::default(),
224        };
225        let encoded = msg.encode(&mut buf);
226        assert!(matches!(encoded.unwrap_err(), EncodeError::MissingField(_)));
227
228        // FilterType = AbsoluteRange - missing end_group_id
229        let msg = TrackStatus {
230            id: 12345,
231            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
232            track_name: "audiotrack".to_string(),
233            subscriber_priority: 127,
234            group_order: GroupOrder::Publisher,
235            forward: true,
236            filter_type: FilterType::AbsoluteRange,
237            start_location: Some(Location::new(12345, 67890)),
238            end_group_id: None,
239            params: Default::default(),
240        };
241        let encoded = msg.encode(&mut buf);
242        assert!(matches!(encoded.unwrap_err(), EncodeError::MissingField(_)));
243    }
244}