use crate::Version;
pub(crate) const QMUX_VERSIONS: &[Version] = &[Version::QMux01, Version::QMux00];
pub(crate) const BARE_ALPNS: &[Version] =
&[Version::QMux01, Version::QMux00, Version::WebTransport];
pub(crate) fn expand_versions(versions: &[Version]) -> &[Version] {
if versions.is_empty() {
QMUX_VERSIONS
} else {
versions
}
}
pub(crate) fn build<'a>(
entries: impl IntoIterator<Item = (&'a str, &'a [Version])>,
require_protocol: bool,
) -> Vec<String> {
let mut out: Vec<String> = Vec::new();
for (alpn, versions) in entries {
for &version in expand_versions(versions) {
debug_assert!(
version.is_qmux(),
"webtransport doesn't use prefixed ALPN; pair entries are qmux only. Bare ALPNs are controlled by require_protocol."
);
out.push(format!("{}{}", version.prefix(), alpn));
}
}
if !require_protocol {
for &v in BARE_ALPNS {
out.push(v.alpn().to_string());
}
}
out
}
pub(crate) fn parse(alpn: Option<&str>) -> (Version, Option<String>) {
let alpn = match alpn {
Some(s) if !s.is_empty() => s,
_ => return (Version::WebTransport, None),
};
for &v in QMUX_VERSIONS {
if alpn == v.alpn() {
return (v, None);
}
if let Some(rest) = alpn.strip_prefix(v.prefix()) {
let app = (!rest.is_empty()).then(|| rest.to_string());
return (v, app);
}
}
(Version::WebTransport, None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_appends_bare_alpns_by_default() {
let out = build([("moq-lite-04", &[Version::QMux01][..])], false);
assert_eq!(
out,
vec!["qmux-01.moq-lite-04", "qmux-01", "qmux-00", "webtransport"]
);
}
#[test]
fn build_emits_only_prefixed_pairs_when_require_protocol() {
let out = build(
[
("moq-lite-04", &[Version::QMux01][..]),
("moq-transport-17", &[Version::QMux00][..]),
],
true,
);
assert_eq!(out, vec!["qmux-01.moq-lite-04", "qmux-00.moq-transport-17"]);
}
#[test]
fn build_expands_empty_versions_to_all_qmux_drafts() {
let out = build([("moq-lite-04", &[][..])], true);
assert_eq!(out, vec!["qmux-01.moq-lite-04", "qmux-00.moq-lite-04"]);
}
#[test]
fn build_emits_one_pair_per_listed_version() {
let out = build(
[("moq-lite-04", &[Version::QMux00, Version::QMux01][..])],
true,
);
assert_eq!(out, vec!["qmux-00.moq-lite-04", "qmux-01.moq-lite-04"]);
}
#[test]
fn build_empty_by_default_emits_only_bare_alpns() {
let entries: [(&str, &[Version]); 0] = [];
let out = build(entries, false);
assert_eq!(out, vec!["qmux-01", "qmux-00", "webtransport"]);
}
#[test]
fn build_empty_with_require_protocol_emits_nothing() {
let entries: [(&str, &[Version]); 0] = [];
let out = build(entries, true);
assert!(out.is_empty());
}
#[test]
fn parse_recognises_prefixed_pairs() {
assert_eq!(
parse(Some("qmux-01.moq-lite-04")),
(Version::QMux01, Some("moq-lite-04".to_string()))
);
assert_eq!(
parse(Some("qmux-00.moq-transport-17")),
(Version::QMux00, Some("moq-transport-17".to_string()))
);
}
#[test]
fn parse_recognises_bare_versions() {
assert_eq!(parse(Some("qmux-01")), (Version::QMux01, None));
assert_eq!(parse(Some("qmux-00")), (Version::QMux00, None));
assert_eq!(parse(Some("webtransport")), (Version::WebTransport, None));
}
#[test]
fn parse_falls_back_to_webtransport() {
assert_eq!(parse(None), (Version::WebTransport, None));
assert_eq!(parse(Some("")), (Version::WebTransport, None));
assert_eq!(parse(Some("h2")), (Version::WebTransport, None));
}
#[test]
fn parse_does_not_strip_webtransport_prefix() {
assert_eq!(
parse(Some("webtransport.moq-lite-04")),
(Version::WebTransport, None)
);
}
}