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.
9pub(crate) const NEGOTIATED: [Version; 3] = [
10	Version::Lite(lite::Version::Draft02),
11	Version::Lite(lite::Version::Draft01),
12	Version::Ietf(ietf::Version::Draft14),
13];
14
15/// ALPN strings for supported versions.
16pub const ALPNS: &[&str] = &[lite::ALPN_03, lite::ALPN, ietf::ALPN_16, ietf::ALPN_15, ietf::ALPN_14];
17
18/// A MoQ protocol version, combining both IETF and Lite variants.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[non_exhaustive]
21pub enum Version {
22	Ietf(ietf::Version),
23	Lite(lite::Version),
24}
25
26impl Version {
27	/// Returns the ALPN string for this version.
28	pub fn alpn(&self) -> &'static str {
29		match self {
30			Self::Lite(lite::Version::Draft03) => lite::ALPN_03,
31			Self::Lite(lite::Version::Draft01 | lite::Version::Draft02) => lite::ALPN,
32			Self::Ietf(ietf::Version::Draft14) => ietf::ALPN_14,
33			Self::Ietf(ietf::Version::Draft15) => ietf::ALPN_15,
34			Self::Ietf(ietf::Version::Draft16) => ietf::ALPN_16,
35		}
36	}
37}
38
39impl fmt::Display for Version {
40	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41		match self {
42			Self::Lite(lite::Version::Draft01) => write!(f, "moq-lite-01"),
43			Self::Lite(lite::Version::Draft02) => write!(f, "moq-lite-02"),
44			Self::Lite(lite::Version::Draft03) => write!(f, "moq-lite-03"),
45			Self::Ietf(ietf::Version::Draft14) => write!(f, "moq-transport-14"),
46			Self::Ietf(ietf::Version::Draft15) => write!(f, "moq-transport-15"),
47			Self::Ietf(ietf::Version::Draft16) => write!(f, "moq-transport-16"),
48		}
49	}
50}
51
52impl FromStr for Version {
53	type Err = String;
54
55	fn from_str(s: &str) -> Result<Self, Self::Err> {
56		match s {
57			"moq-lite-01" => Ok(Self::Lite(lite::Version::Draft01)),
58			"moq-lite-02" => Ok(Self::Lite(lite::Version::Draft02)),
59			"moq-lite-03" => Ok(Self::Lite(lite::Version::Draft03)),
60			"moq-transport-14" => Ok(Self::Ietf(ietf::Version::Draft14)),
61			"moq-transport-15" => Ok(Self::Ietf(ietf::Version::Draft15)),
62			"moq-transport-16" => Ok(Self::Ietf(ietf::Version::Draft16)),
63			_ => Err(format!("unknown version: {s}")),
64		}
65	}
66}
67
68#[cfg(feature = "serde")]
69impl serde::Serialize for Version {
70	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
71		serializer.serialize_str(&self.to_string())
72	}
73}
74
75#[cfg(feature = "serde")]
76impl<'de> serde::Deserialize<'de> for Version {
77	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
78		let s = String::deserialize(deserializer)?;
79		s.parse().map_err(serde::de::Error::custom)
80	}
81}
82
83impl From<ietf::Version> for Version {
84	fn from(value: ietf::Version) -> Self {
85		Self::Ietf(value)
86	}
87}
88
89impl From<lite::Version> for Version {
90	fn from(value: lite::Version) -> Self {
91		Self::Lite(value)
92	}
93}
94
95impl TryFrom<coding::Version> for Version {
96	type Error = ();
97
98	fn try_from(value: coding::Version) -> Result<Self, Self::Error> {
99		ietf::Version::try_from(value)
100			.map(Self::Ietf)
101			.or_else(|_| lite::Version::try_from(value).map(Self::Lite))
102	}
103}
104
105impl<V> coding::Decode<V> for Version {
106	fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, coding::DecodeError> {
107		coding::Version::decode(r, version).and_then(|v| v.try_into().map_err(|_| coding::DecodeError::InvalidValue))
108	}
109}
110
111impl<V> coding::Encode<V> for Version {
112	fn encode<W: bytes::BufMut>(&self, w: &mut W, v: V) -> Result<(), coding::EncodeError> {
113		match self {
114			Self::Ietf(version) => coding::Version::from(*version).encode(w, v)?,
115			Self::Lite(version) => coding::Version::from(*version).encode(w, v)?,
116		}
117		Ok(())
118	}
119}
120
121impl From<Version> for coding::Version {
122	fn from(value: Version) -> Self {
123		match value {
124			Version::Ietf(version) => version.into(),
125			Version::Lite(version) => version.into(),
126		}
127	}
128}
129
130impl From<Vec<Version>> for coding::Versions {
131	fn from(value: Vec<Version>) -> Self {
132		let inner: Vec<coding::Version> = value.into_iter().map(|v| v.into()).collect();
133		coding::Versions::from(inner)
134	}
135}
136
137/// A set of supported MoQ versions.
138#[derive(Debug, Clone)]
139pub struct Versions(Vec<Version>);
140
141impl Versions {
142	/// All supported versions.
143	pub fn all() -> Self {
144		Self(vec![
145			Version::Lite(lite::Version::Draft03),
146			Version::Lite(lite::Version::Draft02),
147			Version::Lite(lite::Version::Draft01),
148			Version::Ietf(ietf::Version::Draft16),
149			Version::Ietf(ietf::Version::Draft15),
150			Version::Ietf(ietf::Version::Draft14),
151		])
152	}
153
154	/// Compute the unique ALPN strings needed for these versions.
155	pub fn alpns(&self) -> Vec<&'static str> {
156		let mut alpns = Vec::new();
157		for v in &self.0 {
158			let alpn = v.alpn();
159			if !alpns.contains(&alpn) {
160				alpns.push(alpn);
161			}
162		}
163		alpns
164	}
165
166	/// Return only versions present in both self and other, or `None` if the intersection is empty.
167	pub fn filter(&self, other: &Versions) -> Option<Versions> {
168		let filtered: Vec<Version> = self.0.iter().filter(|v| other.0.contains(v)).copied().collect();
169		if filtered.is_empty() {
170			None
171		} else {
172			Some(Versions(filtered))
173		}
174	}
175
176	/// Check if a specific version is in this set.
177	pub fn select(&self, version: Version) -> Option<Version> {
178		self.0.contains(&version).then_some(version)
179	}
180
181	pub fn contains(&self, version: &Version) -> bool {
182		self.0.contains(version)
183	}
184
185	pub fn iter(&self) -> impl Iterator<Item = &Version> {
186		self.0.iter()
187	}
188}
189
190impl Default for Versions {
191	fn default() -> Self {
192		Self::all()
193	}
194}
195
196impl From<Version> for Versions {
197	fn from(value: Version) -> Self {
198		Self(vec![value])
199	}
200}
201
202impl From<Vec<Version>> for Versions {
203	fn from(value: Vec<Version>) -> Self {
204		Self(value)
205	}
206}
207
208impl<const N: usize> From<[Version; N]> for Versions {
209	fn from(value: [Version; N]) -> Self {
210		Self(value.to_vec())
211	}
212}
213
214impl From<Versions> for coding::Versions {
215	fn from(value: Versions) -> Self {
216		let inner: Vec<coding::Version> = value.0.into_iter().map(|v| v.into()).collect();
217		coding::Versions::from(inner)
218	}
219}