use crate::cbor::Value;
use super::{entry, malformed, unsigned, Record, RecordError, RecordType};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StationEndpointOptions {
pub host_advertised: Vec<String>,
pub alpn: String,
pub ttl_ms: u64,
}
pub fn new_station_endpoint(
quic_port: u16,
opts: &StationEndpointOptions,
) -> Result<Record, RecordError> {
if quic_port == 0 {
return Err(RecordError::InvalidPort);
}
let mut entries = vec![entry("quic_port", Value::Int(i128::from(quic_port)))];
if !opts.host_advertised.is_empty() {
entries.push(entry(
"host_advertised",
Value::List(
opts.host_advertised
.iter()
.map(|h| Value::Bytes(h.as_bytes().to_vec()))
.collect(),
),
));
}
if !opts.alpn.is_empty() {
entries.push(entry("alpn", Value::text(opts.alpn.clone())));
}
Ok(unsigned(
RecordType::STATION_ENDPOINT,
Value::Map(entries),
opts.ttl_ms,
))
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StationEndpoint {
pub quic_port: u16,
pub host_advertised: Vec<String>,
}
pub fn read_station_endpoint(r: &Record) -> Result<StationEndpoint, RecordError> {
if r.record_type != RecordType::STATION_ENDPOINT {
return Err(malformed("not a station endpoint"));
}
let quic_port = match r.payload.get("quic_port") {
Some(Value::Int(n)) if (1..=65535).contains(n) => *n as u16,
_ => 0,
};
Ok(StationEndpoint {
quic_port,
host_advertised: host_list(r.payload.get("host_advertised")),
})
}
fn host_list(v: Option<&Value>) -> Vec<String> {
let items: Vec<&Value> = match v {
None => return Vec::new(),
Some(Value::List(items)) => items.iter().collect(),
Some(single) => vec![single],
};
items
.into_iter()
.filter_map(|item| match item {
Value::Bytes(b) => Some(String::from_utf8_lossy(b).into_owned()),
Value::Text(t) => Some(t.clone()),
_ => None,
})
.collect()
}