Skip to main content

moq_lite/
version.rs

1use std::fmt;
2use std::str::FromStr;
3
4use crate::{coding, ietf, lite};
5
6/// The versions of MoQ that are negotiated via SETUP.
7///
8/// Ordered by preference, with the client's preference taking priority.
9/// This intentionally includes only SETUP-negotiated versions (Lite02, Lite01, Draft14);
10/// Lite03 and newer IETF drafts negotiate via dedicated ALPNs instead.
11pub(crate) const NEGOTIATED: [Version; 3] = [
12	Version::Lite(lite::Version::Lite02),
13	Version::Lite(lite::Version::Lite01),
14	Version::Ietf(ietf::Version::Draft14),
15];
16
17/// ALPN strings for supported versions.
18pub const ALPNS: &[&str] = &[ALPN_LITE_03, ALPN_LITE, ALPN_17, ALPN_16, ALPN_15, ALPN_14];
19
20// ALPN constants
21pub const ALPN_LITE: &str = "moql";
22pub const ALPN_LITE_03: &str = "moq-lite-03";
23pub const ALPN_14: &str = "moq-00";
24pub const ALPN_15: &str = "moqt-15";
25pub const ALPN_16: &str = "moqt-16";
26pub const ALPN_17: &str = "moqt-17";
27
28/// A MoQ protocol version.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum Version {
32	Lite(lite::Version),
33	Ietf(ietf::Version),
34}
35
36impl Version {
37	/// Parse from wire version code (used during SETUP negotiation).
38	pub fn from_code(code: u64) -> Option<Self> {
39		match code {
40			0xff0dad01 => Some(Self::Lite(lite::Version::Lite01)),
41			0xff0dad02 => Some(Self::Lite(lite::Version::Lite02)),
42			0xff0dad03 => Some(Self::Lite(lite::Version::Lite03)),
43			0xff00000e => Some(Self::Ietf(ietf::Version::Draft14)),
44			0xff00000f => Some(Self::Ietf(ietf::Version::Draft15)),
45			0xff000010 => Some(Self::Ietf(ietf::Version::Draft16)),
46			0xff000011 => Some(Self::Ietf(ietf::Version::Draft17)),
47			_ => None,
48		}
49	}
50
51	/// Get the wire version code.
52	pub fn code(&self) -> u64 {
53		match self {
54			Self::Lite(lite::Version::Lite01) => 0xff0dad01,
55			Self::Lite(lite::Version::Lite02) => 0xff0dad02,
56			Self::Lite(lite::Version::Lite03) => 0xff0dad03,
57			Self::Ietf(ietf::Version::Draft14) => 0xff00000e,
58			Self::Ietf(ietf::Version::Draft15) => 0xff00000f,
59			Self::Ietf(ietf::Version::Draft16) => 0xff000010,
60			Self::Ietf(ietf::Version::Draft17) => 0xff000011,
61		}
62	}
63
64	/// Parse from ALPN string.
65	///
66	/// Returns `None` for `ALPN_LITE` since multiple versions share
67	/// that ALPN, requiring SETUP negotiation to determine the version.
68	pub fn from_alpn(alpn: &str) -> Option<Self> {
69		match alpn {
70			ALPN_LITE => None, // Multiple versions share this ALPN, need SETUP negotiation
71			ALPN_LITE_03 => Some(Self::Lite(lite::Version::Lite03)),
72			ALPN_14 => Some(Self::Ietf(ietf::Version::Draft14)),
73			ALPN_15 => Some(Self::Ietf(ietf::Version::Draft15)),
74			ALPN_16 => Some(Self::Ietf(ietf::Version::Draft16)),
75			ALPN_17 => Some(Self::Ietf(ietf::Version::Draft17)),
76			_ => None,
77		}
78	}
79
80	/// Returns the ALPN string for this version.
81	pub fn alpn(&self) -> &'static str {
82		match self {
83			Self::Lite(lite::Version::Lite03) => ALPN_LITE_03,
84			Self::Lite(lite::Version::Lite01 | lite::Version::Lite02) => ALPN_LITE,
85			Self::Ietf(ietf::Version::Draft14) => ALPN_14,
86			Self::Ietf(ietf::Version::Draft15) => ALPN_15,
87			Self::Ietf(ietf::Version::Draft16) => ALPN_16,
88			Self::Ietf(ietf::Version::Draft17) => ALPN_17,
89		}
90	}
91
92	/// Whether this version uses SETUP version-code negotiation
93	/// (as opposed to ALPN-only).
94	pub fn uses_setup_negotiation(&self) -> bool {
95		matches!(
96			self,
97			Self::Lite(lite::Version::Lite01 | lite::Version::Lite02) | Self::Ietf(ietf::Version::Draft14)
98		)
99	}
100
101	/// Whether this is a lite protocol version.
102	pub fn is_lite(&self) -> bool {
103		match self {
104			Self::Lite(_) => true,
105			Self::Ietf(_) => false,
106		}
107	}
108
109	/// Whether this is an IETF protocol version.
110	pub fn is_ietf(&self) -> bool {
111		match self {
112			Self::Ietf(_) => true,
113			Self::Lite(_) => false,
114		}
115	}
116}
117
118impl fmt::Display for Version {
119	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120		match self {
121			Self::Lite(v) => v.fmt(f),
122			Self::Ietf(v) => v.fmt(f),
123		}
124	}
125}
126
127impl FromStr for Version {
128	type Err = String;
129
130	fn from_str(s: &str) -> Result<Self, Self::Err> {
131		match s {
132			"moq-lite-01" => Ok(Self::Lite(lite::Version::Lite01)),
133			"moq-lite-02" => Ok(Self::Lite(lite::Version::Lite02)),
134			"moq-lite-03" => Ok(Self::Lite(lite::Version::Lite03)),
135			"moq-transport-14" => Ok(Self::Ietf(ietf::Version::Draft14)),
136			"moq-transport-15" => Ok(Self::Ietf(ietf::Version::Draft15)),
137			"moq-transport-16" => Ok(Self::Ietf(ietf::Version::Draft16)),
138			"moq-transport-17" => Ok(Self::Ietf(ietf::Version::Draft17)),
139			_ => Err(format!("unknown version: {s}")),
140		}
141	}
142}
143
144#[cfg(feature = "serde")]
145impl serde::Serialize for Version {
146	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
147		serializer.serialize_str(&self.to_string())
148	}
149}
150
151#[cfg(feature = "serde")]
152impl<'de> serde::Deserialize<'de> for Version {
153	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
154		let s = String::deserialize(deserializer)?;
155		s.parse().map_err(serde::de::Error::custom)
156	}
157}
158
159impl TryFrom<coding::Version> for Version {
160	type Error = ();
161
162	fn try_from(value: coding::Version) -> Result<Self, Self::Error> {
163		Self::from_code(value.0).ok_or(())
164	}
165}
166
167impl coding::Decode<Version> for Version {
168	fn decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, coding::DecodeError> {
169		coding::Version::decode(r, version).and_then(|v| v.try_into().map_err(|_| coding::DecodeError::InvalidValue))
170	}
171}
172
173impl coding::Encode<Version> for Version {
174	fn encode<W: bytes::BufMut>(&self, w: &mut W, v: Version) -> Result<(), coding::EncodeError> {
175		coding::Version::from(*self).encode(w, v)
176	}
177}
178
179impl From<Version> for coding::Version {
180	fn from(value: Version) -> Self {
181		Self(value.code())
182	}
183}
184
185impl From<Vec<Version>> for coding::Versions {
186	fn from(value: Vec<Version>) -> Self {
187		let inner: Vec<coding::Version> = value.into_iter().map(|v| v.into()).collect();
188		coding::Versions::from(inner)
189	}
190}
191
192/// A set of supported MoQ versions.
193#[derive(Debug, Clone)]
194pub struct Versions(Vec<Version>);
195
196impl Versions {
197	/// All supported versions exposed by default.
198	pub fn all() -> Self {
199		Self(vec![
200			Version::Lite(lite::Version::Lite03),
201			Version::Lite(lite::Version::Lite02),
202			Version::Lite(lite::Version::Lite01),
203			Version::Ietf(ietf::Version::Draft17),
204			Version::Ietf(ietf::Version::Draft16),
205			Version::Ietf(ietf::Version::Draft15),
206			Version::Ietf(ietf::Version::Draft14),
207		])
208	}
209
210	/// Compute the unique ALPN strings needed for these versions.
211	pub fn alpns(&self) -> Vec<&'static str> {
212		let mut alpns = Vec::new();
213		for v in &self.0 {
214			let alpn = v.alpn();
215			if !alpns.contains(&alpn) {
216				alpns.push(alpn);
217			}
218		}
219		alpns
220	}
221
222	/// Return only versions present in both self and other, or `None` if the intersection is empty.
223	pub fn filter(&self, other: &Versions) -> Option<Versions> {
224		let filtered: Vec<Version> = self.0.iter().filter(|v| other.0.contains(v)).copied().collect();
225		if filtered.is_empty() {
226			None
227		} else {
228			Some(Versions(filtered))
229		}
230	}
231
232	/// Check if a specific version is in this set.
233	pub fn select(&self, version: Version) -> Option<Version> {
234		self.0.contains(&version).then_some(version)
235	}
236
237	pub fn contains(&self, version: &Version) -> bool {
238		self.0.contains(version)
239	}
240
241	pub fn iter(&self) -> impl Iterator<Item = &Version> {
242		self.0.iter()
243	}
244}
245
246impl Default for Versions {
247	fn default() -> Self {
248		Self::all()
249	}
250}
251
252impl From<Version> for Versions {
253	fn from(value: Version) -> Self {
254		Self(vec![value])
255	}
256}
257
258impl From<Vec<Version>> for Versions {
259	fn from(value: Vec<Version>) -> Self {
260		Self(value)
261	}
262}
263
264impl<const N: usize> From<[Version; N]> for Versions {
265	fn from(value: [Version; N]) -> Self {
266		Self(value.to_vec())
267	}
268}
269
270impl From<Versions> for coding::Versions {
271	fn from(value: Versions) -> Self {
272		let inner: Vec<coding::Version> = value.0.into_iter().map(|v| v.into()).collect();
273		coding::Versions::from(inner)
274	}
275}