macula_rust/record/
station_endpoint.rs1use crate::cbor::Value;
5
6use super::{entry, malformed, unsigned, Record, RecordError, RecordType};
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
12pub struct StationEndpointOptions {
13 pub host_advertised: Vec<String>,
14 pub alpn: String,
15 pub ttl_ms: u64,
16}
17
18pub fn new_station_endpoint(
21 quic_port: u16,
22 opts: &StationEndpointOptions,
23) -> Result<Record, RecordError> {
24 if quic_port == 0 {
25 return Err(RecordError::InvalidPort);
26 }
27 let mut entries = vec![entry("quic_port", Value::Int(i128::from(quic_port)))];
28 if !opts.host_advertised.is_empty() {
29 entries.push(entry(
30 "host_advertised",
31 Value::List(
32 opts.host_advertised
33 .iter()
34 .map(|h| Value::Bytes(h.as_bytes().to_vec()))
35 .collect(),
36 ),
37 ));
38 }
39 if !opts.alpn.is_empty() {
40 entries.push(entry("alpn", Value::text(opts.alpn.clone())));
41 }
42 Ok(unsigned(
43 RecordType::STATION_ENDPOINT,
44 Value::Map(entries),
45 opts.ttl_ms,
46 ))
47}
48
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
52pub struct StationEndpoint {
53 pub quic_port: u16,
54 pub host_advertised: Vec<String>,
55}
56
57pub fn read_station_endpoint(r: &Record) -> Result<StationEndpoint, RecordError> {
59 if r.record_type != RecordType::STATION_ENDPOINT {
60 return Err(malformed("not a station endpoint"));
61 }
62 let quic_port = match r.payload.get("quic_port") {
63 Some(Value::Int(n)) if (1..=65535).contains(n) => *n as u16,
64 _ => 0,
65 };
66 Ok(StationEndpoint {
67 quic_port,
68 host_advertised: host_list(r.payload.get("host_advertised")),
69 })
70}
71
72fn host_list(v: Option<&Value>) -> Vec<String> {
75 let items: Vec<&Value> = match v {
76 None => return Vec::new(),
77 Some(Value::List(items)) => items.iter().collect(),
78 Some(single) => vec![single],
79 };
80 items
81 .into_iter()
82 .filter_map(|item| match item {
83 Value::Bytes(b) => Some(String::from_utf8_lossy(b).into_owned()),
84 Value::Text(t) => Some(t.clone()),
85 _ => None,
86 })
87 .collect()
88}