Skip to main content

moqtap_codec/fields/
mod.rs

1//! A decoded control message as a tree of named fields.
2//!
3//! [`AnyControlMessage::fields`](crate::dispatch::AnyControlMessage::fields)
4//! turns any draft's `ControlMessage` into a [`FieldMap`] whose keys are the
5//! field names that draft gives them. The names are the drafts' own, in
6//! snake_case, and the field order is the order the draft defines — so a
7//! reader that has never heard of a message can still show it, and two drafts
8//! that spell the same concept differently keep their own spelling. The
9//! per-draft `fields` modules are what it dispatches to.
10//!
11//! # Why a tree of the crate's own making
12//!
13//! The obvious return types are `serde_json::Value` and `ciborium::Value`, and
14//! this crate depends on neither. A codec that gained a serialization format's
15//! value type would make everything downstream carry it, to describe messages
16//! that have nothing to do with that format. [`FieldValue`] is `std` and a
17//! `Vec`, and each caller renders it into whatever it already writes: the
18//! vector tests into JSON, where a varint becomes a decimal string and a byte
19//! string becomes hex, and a trace writer into CBOR, where both have a type of
20//! their own.
21//!
22//! That split is also why [`FieldValue::Uint`] and [`FieldValue::Bytes`] are
23//! distinct from [`FieldValue::Text`] rather than pre-rendered into it. A
24//! converter that flattened them would force every consumer to guess which
25//! strings were numbers.
26
27/// Parameter tables more than one draft shares.
28///
29/// A draft whose parameter handling is its own keeps it in its own `fields.rs`,
30/// which is where all but four of them are. Only drafts 07 through 10 share:
31/// one message table across all four, and one setup table across the three that
32/// dropped ROLE. Anything reachable from a single draft belongs to that draft,
33/// or a build of that draft alone compiles code nothing can call.
34#[cfg(any(feature = "draft07", feature = "draft08", feature = "draft09", feature = "draft10"))]
35pub(crate) mod params;
36
37/// One field's value inside a decoded control message.
38///
39/// Absent optional fields are omitted from their [`FieldMap`] rather than
40/// given a zero: a field the wire never carried and a field carrying zero are
41/// different, and only omission can say so.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum FieldValue {
44    /// A varint or fixed-width integer, widened to `u64`.
45    Uint(u64),
46    /// A single-bit field.
47    Bool(bool),
48    /// A field the draft defines as text, or a name for something the draft
49    /// leaves opaque — an unknown parameter's key, rendered `0x21`.
50    Text(String),
51    /// A field the draft leaves as opaque bytes.
52    Bytes(Vec<u8>),
53    /// A repeated field, in wire order.
54    Array(Vec<FieldValue>),
55    /// A nested structure — a location, a parameter set, a fetch's payload.
56    Map(FieldMap),
57}
58
59/// A decoded message's fields, in the order the draft defines them.
60///
61/// Ordered rather than sorted because the order is information: it is the
62/// order the fields appear on the wire, which is what makes a rendering of
63/// one message comparable to a rendering of the same message from another
64/// implementation.
65#[derive(Debug, Clone, Default, PartialEq, Eq)]
66pub struct FieldMap {
67    entries: Vec<(String, FieldValue)>,
68}
69
70impl FieldMap {
71    /// An empty map.
72    pub fn new() -> Self {
73        Self { entries: Vec::new() }
74    }
75
76    /// Set `key` to `value`, replacing any value already under that key.
77    ///
78    /// Replacing rather than appending keeps a duplicate key impossible, which
79    /// is what lets a reader index the map. A replaced key keeps its original
80    /// position, so a later correction does not reorder the message.
81    ///
82    /// The key is a `String` rather than an `impl Into<String>` because the
83    /// callers write `"request_id".into()`, and a generic bound leaves that
84    /// `into` with nothing to infer from.
85    pub fn insert(&mut self, key: String, value: FieldValue) {
86        match self.entries.iter_mut().find(|(k, _)| *k == key) {
87            Some(entry) => entry.1 = value,
88            None => self.entries.push((key, value)),
89        }
90    }
91
92    /// The value under `key`, if the message carried that field.
93    pub fn get(&self, key: &str) -> Option<&FieldValue> {
94        self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
95    }
96
97    /// The fields, in the order the draft defines them.
98    pub fn iter(&self) -> impl Iterator<Item = (&str, &FieldValue)> {
99        self.entries.iter().map(|(k, v)| (k.as_str(), v))
100    }
101
102    /// Whether the message had no fields at all. True for the handful of
103    /// messages that are nothing but their type.
104    pub fn is_empty(&self) -> bool {
105        self.entries.is_empty()
106    }
107
108    /// How many fields the message carried.
109    pub fn len(&self) -> usize {
110        self.entries.len()
111    }
112}
113
114impl IntoIterator for FieldMap {
115    type Item = (String, FieldValue);
116    type IntoIter = std::vec::IntoIter<(String, FieldValue)>;
117
118    fn into_iter(self) -> Self::IntoIter {
119        self.entries.into_iter()
120    }
121}