pub(crate) const IROH: &str = "iroh";
pub(crate) const IROH_QUIC: &str = "iroh-quic";
pub(crate) const IROH_RELAY: &str = "iroh-relay";
pub(crate) const IROH_LAN: &str = "iroh-lan";
pub(crate) const BLE: &str = "ble";
pub(crate) const WEBRTC: &str = "webrtc";
pub(crate) const WEBRTC_LAN: &str = "webrtc-lan";
pub(crate) const MOQ: &str = "moq";
pub(crate) fn normalize(value: &str) -> Option<&'static str> {
match value.trim().to_ascii_lowercase().as_str() {
IROH => Some(IROH),
IROH_QUIC => Some(IROH_QUIC),
IROH_RELAY => Some(IROH_RELAY),
IROH_LAN => Some(IROH_LAN),
BLE => Some(BLE),
WEBRTC => Some(WEBRTC),
WEBRTC_LAN => Some(WEBRTC_LAN),
MOQ => Some(MOQ),
_ => None,
}
}
#[allow(dead_code)]
pub(crate) fn is_iroh_base(value: &str) -> bool {
matches!(
normalize(value),
Some(IROH | IROH_QUIC | IROH_LAN | IROH_RELAY | BLE)
)
}
#[allow(dead_code)]
pub(crate) fn is_direct_iroh(value: &str) -> bool {
matches!(normalize(value), Some(IROH_QUIC | IROH_LAN | BLE))
}
#[allow(dead_code)]
pub(crate) fn is_optional(value: &str) -> bool {
matches!(normalize(value), Some(WEBRTC | WEBRTC_LAN | MOQ))
}
#[allow(dead_code)]
pub(crate) fn is_webrtc(value: &str) -> bool {
matches!(normalize(value), Some(WEBRTC | WEBRTC_LAN))
}
pub(crate) fn is_independent_transport(value: &str) -> bool {
matches!(normalize(value), Some(WEBRTC | MOQ))
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct TransportPolicyVectors {
labels: Vec<LabelVector>,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct LabelVector {
input: String,
normalized: Option<String>,
iroh_base: bool,
direct_iroh: bool,
optional: bool,
web_rtc: bool,
independent: bool,
}
#[test]
fn normalizes_official_labels_case_insensitively() {
for (input, expected) in [
("iroh", IROH),
(" IROH-QUIC ", IROH_QUIC),
("iroh-relay", IROH_RELAY),
("iroh-lan", IROH_LAN),
("ble", BLE),
("webrtc", WEBRTC),
("webrtc-lan", WEBRTC_LAN),
("moq", MOQ),
] {
assert_eq!(normalize(input), Some(expected));
}
assert_eq!(normalize("lan"), None);
assert_eq!(normalize("webtransport"), None);
}
#[test]
fn classifies_transport_families() {
assert!(is_iroh_base(IROH));
assert!(is_iroh_base(IROH_QUIC));
assert!(is_iroh_base(IROH_LAN));
assert!(is_iroh_base(IROH_RELAY));
assert!(is_iroh_base(BLE));
assert!(!is_iroh_base(WEBRTC));
assert!(is_direct_iroh(IROH_QUIC));
assert!(is_direct_iroh(IROH_LAN));
assert!(is_direct_iroh(BLE));
assert!(!is_direct_iroh(IROH_RELAY));
assert!(is_optional(WEBRTC));
assert!(is_optional(WEBRTC_LAN));
assert!(is_optional(MOQ));
assert!(!is_optional(IROH_QUIC));
assert!(is_webrtc(WEBRTC));
assert!(is_webrtc(WEBRTC_LAN));
assert!(!is_webrtc(MOQ));
assert!(is_independent_transport(WEBRTC));
assert!(is_independent_transport(MOQ));
assert!(!is_independent_transport(WEBRTC_LAN));
}
#[test]
fn matches_shared_transport_policy_vectors() {
let vectors: TransportPolicyVectors =
serde_json::from_str(include_str!("../testdata/transport_policy_vectors.json"))
.expect("transport policy vectors");
for vector in vectors.labels {
assert_eq!(
normalize(&vector.input),
vector.normalized.as_deref(),
"normalize({:?})",
vector.input
);
assert_eq!(
is_iroh_base(&vector.input),
vector.iroh_base,
"is_iroh_base({:?})",
vector.input
);
assert_eq!(
is_direct_iroh(&vector.input),
vector.direct_iroh,
"is_direct_iroh({:?})",
vector.input
);
assert_eq!(
is_optional(&vector.input),
vector.optional,
"is_optional({:?})",
vector.input
);
assert_eq!(
is_webrtc(&vector.input),
vector.web_rtc,
"is_webrtc({:?})",
vector.input
);
assert_eq!(
is_independent_transport(&vector.input),
vector.independent,
"is_independent_transport({:?})",
vector.input
);
}
}
}