Skip to main content

moq_transport/message/
publish.rs

1use crate::coding::{
2    Decode, DecodeError, Encode, EncodeError, KeyValuePairs, Location, TrackNamespace,
3};
4use crate::message::GroupOrder;
5
6/// Sent by publisher to initiate a subscription to a track.
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub struct Publish {
9    /// The publish request ID
10    pub id: u64,
11
12    /// Track properties
13    pub track_namespace: TrackNamespace,
14    pub track_name: String, // TODO SLG - consider making a FullTrackName base struct (total size limit of 4096)
15    pub track_alias: u64,
16
17    pub group_order: GroupOrder,
18    pub content_exists: bool,
19    // The largest object available for this track, if content exists.
20    pub largest_location: Option<Location>,
21    pub forward: bool,
22
23    /// Optional parameters
24    pub params: KeyValuePairs,
25}
26
27impl Decode for Publish {
28    fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
29        let id = u64::decode(r)?;
30
31        let track_namespace = TrackNamespace::decode(r)?;
32        let track_name = String::decode(r)?;
33        let track_alias = u64::decode(r)?;
34
35        let group_order = GroupOrder::decode(r)?;
36        // GroupOrder enum has Publisher in it, but it's not allowed to be used in this
37        // publish message, so validate it now so we can return a protocol error.
38        if group_order == GroupOrder::Publisher {
39            return Err(DecodeError::InvalidGroupOrder);
40        }
41        let content_exists = bool::decode(r)?;
42        let largest_location = match content_exists {
43            true => Some(Location::decode(r)?),
44            false => None,
45        };
46        let forward = bool::decode(r)?;
47
48        let params = KeyValuePairs::decode(r)?;
49
50        Ok(Self {
51            id,
52            track_namespace,
53            track_name,
54            track_alias,
55            group_order,
56            content_exists,
57            largest_location,
58            forward,
59            params,
60        })
61    }
62}
63
64impl Encode for Publish {
65    fn encode<W: bytes::BufMut>(&self, w: &mut W) -> Result<(), EncodeError> {
66        self.id.encode(w)?;
67
68        self.track_namespace.encode(w)?;
69        self.track_name.encode(w)?;
70        self.track_alias.encode(w)?;
71
72        // GroupOrder enum has Publisher in it, but it's not allowed to be used in this
73        // publish message.
74        if self.group_order == GroupOrder::Publisher {
75            return Err(EncodeError::InvalidValue);
76        }
77        self.group_order.encode(w)?;
78        self.content_exists.encode(w)?;
79        if self.content_exists {
80            if let Some(largest) = &self.largest_location {
81                largest.encode(w)?;
82            } else {
83                return Err(EncodeError::MissingField("LargestLocation".to_string()));
84            }
85        }
86        self.forward.encode(w)?;
87        self.params.encode(w)?;
88
89        Ok(())
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use bytes::BytesMut;
97
98    #[test]
99    fn encode_decode() {
100        let mut buf = BytesMut::new();
101
102        // One parameter for testing
103        let mut kvps = KeyValuePairs::new();
104        kvps.set_bytesvalue(123, vec![0x00, 0x01, 0x02, 0x03]);
105
106        // Content exists = true
107        let msg = Publish {
108            id: 12345,
109            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
110            track_name: "audiotrack".to_string(),
111            track_alias: 212,
112            group_order: GroupOrder::Ascending,
113            content_exists: true,
114            largest_location: Some(Location::new(2, 3)),
115            forward: true,
116            params: kvps.clone(),
117        };
118        msg.encode(&mut buf).unwrap();
119        let decoded = Publish::decode(&mut buf).unwrap();
120        assert_eq!(decoded, msg);
121
122        // Content exists = false
123        let msg = Publish {
124            id: 12345,
125            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
126            track_name: "audiotrack".to_string(),
127            track_alias: 212,
128            group_order: GroupOrder::Ascending,
129            content_exists: false,
130            largest_location: None,
131            forward: true,
132            params: kvps.clone(),
133        };
134        msg.encode(&mut buf).unwrap();
135        let decoded = Publish::decode(&mut buf).unwrap();
136        assert_eq!(decoded, msg);
137    }
138
139    #[test]
140    fn encode_missing_fields() {
141        let mut buf = BytesMut::new();
142
143        let msg = Publish {
144            id: 12345,
145            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
146            track_name: "audiotrack".to_string(),
147            track_alias: 212,
148            group_order: GroupOrder::Ascending,
149            content_exists: true,
150            largest_location: None,
151            forward: true,
152            params: Default::default(),
153        };
154        let encoded = msg.encode(&mut buf);
155        assert!(matches!(encoded.unwrap_err(), EncodeError::MissingField(_)));
156    }
157
158    #[test]
159    fn encode_bad_group_order() {
160        let mut buf = BytesMut::new();
161
162        let msg = Publish {
163            id: 12345,
164            track_namespace: TrackNamespace::from_utf8_path("test/path/to/resource"),
165            track_name: "audiotrack".to_string(),
166            track_alias: 212,
167            group_order: GroupOrder::Publisher,
168            content_exists: false,
169            largest_location: None,
170            forward: true,
171            params: Default::default(),
172        };
173        let encoded = msg.encode(&mut buf);
174        assert!(matches!(encoded.unwrap_err(), EncodeError::InvalidValue));
175    }
176}