Skip to main content

vox_types/
metadata.rs

1use std::borrow::Cow;
2
3use facet::Facet;
4
5// r[impl rpc.metadata]
6// r[impl rpc.metadata.value]
7/// Metadata value.
8///
9/// Uses `Cow` so values can be borrowed (from wire data) or owned (runtime-constructed).
10#[repr(u8)]
11#[derive(Debug, Clone, PartialEq, Eq, Facet)]
12pub enum MetadataValue<'a> {
13    String(Cow<'a, str>) = 0,
14    Bytes(Cow<'a, [u8]>) = 1,
15    U64(u64) = 2,
16}
17
18/// Metadata entry flags.
19///
20/// Flags control metadata handling behavior.
21// r[impl rpc.metadata.flags]
22// r[impl rpc.metadata.flags.sensitive]
23// r[impl rpc.metadata.flags.no-propagate]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
25#[repr(transparent)]
26#[facet(transparent)]
27pub struct MetadataFlags(u64);
28
29impl MetadataFlags {
30    /// No special handling.
31    pub const NONE: Self = Self(0);
32
33    /// Value MUST NOT be logged, traced, or included in error messages.
34    pub const SENSITIVE: Self = Self(1 << 0);
35
36    /// Value MUST NOT be forwarded to downstream calls.
37    pub const NO_PROPAGATE: Self = Self(1 << 1);
38
39    /// Returns `true` if all flags in `other` are set in `self`.
40    pub fn contains(self, other: Self) -> bool {
41        (self.0 & other.0) == other.0
42    }
43}
44
45impl std::ops::BitOr for MetadataFlags {
46    type Output = Self;
47    fn bitor(self, rhs: Self) -> Self {
48        Self(self.0 | rhs.0)
49    }
50}
51
52impl std::ops::BitOrAssign for MetadataFlags {
53    fn bitor_assign(&mut self, rhs: Self) {
54        self.0 |= rhs.0;
55    }
56}
57
58impl std::ops::BitAnd for MetadataFlags {
59    type Output = Self;
60    fn bitand(self, rhs: Self) -> Self {
61        Self(self.0 & rhs.0)
62    }
63}
64
65impl std::ops::BitAndAssign for MetadataFlags {
66    fn bitand_assign(&mut self, rhs: Self) {
67        self.0 &= rhs.0;
68    }
69}
70
71// r[impl rpc.metadata.keys]
72// r[impl rpc.metadata.duplicates]
73/// A single metadata entry with a key, value, and flags.
74///
75/// Uses `Cow` for the key so entries can be borrowed (from wire data) or owned.
76#[derive(Debug, Clone, PartialEq, Eq, Facet)]
77pub struct MetadataEntry<'a> {
78    pub key: Cow<'a, str>,
79    pub value: MetadataValue<'a>,
80    pub flags: MetadataFlags,
81}
82
83impl<'a> MetadataValue<'a> {
84    /// Convert to a `'static` lifetime by cloning any borrowed data.
85    pub fn into_owned(self) -> MetadataValue<'static> {
86        match self {
87            MetadataValue::String(s) => MetadataValue::String(Cow::Owned(s.into_owned())),
88            MetadataValue::Bytes(b) => MetadataValue::Bytes(Cow::Owned(b.into_owned())),
89            MetadataValue::U64(n) => MetadataValue::U64(n),
90        }
91    }
92}
93
94impl<'a> MetadataEntry<'a> {
95    /// Create a string metadata entry with no flags.
96    pub fn str(key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, str>>) -> Self {
97        MetadataEntry {
98            key: key.into(),
99            value: MetadataValue::String(value.into()),
100            flags: MetadataFlags::NONE,
101        }
102    }
103
104    /// Create a u64 metadata entry with no flags.
105    pub fn u64(key: impl Into<Cow<'a, str>>, value: u64) -> Self {
106        MetadataEntry {
107            key: key.into(),
108            value: MetadataValue::U64(value),
109            flags: MetadataFlags::NONE,
110        }
111    }
112
113    /// Create a bytes metadata entry with no flags.
114    pub fn bytes(key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, [u8]>>) -> Self {
115        MetadataEntry {
116            key: key.into(),
117            value: MetadataValue::Bytes(value.into()),
118            flags: MetadataFlags::NONE,
119        }
120    }
121
122    /// Set flags on this entry.
123    pub fn with_flags(mut self, flags: MetadataFlags) -> Self {
124        self.flags = flags;
125        self
126    }
127
128    /// Convert to a `'static` lifetime by cloning any borrowed data.
129    pub fn into_owned(self) -> MetadataEntry<'static> {
130        MetadataEntry {
131            key: Cow::Owned(self.key.into_owned()),
132            value: self.value.into_owned(),
133            flags: self.flags,
134        }
135    }
136}
137
138/// Builder for constructing a `Metadata` list.
139pub struct MetadataBuilder<'a> {
140    entries: Metadata<'a>,
141}
142
143impl<'a> MetadataBuilder<'a> {
144    /// Add a string entry.
145    pub fn str(mut self, key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, str>>) -> Self {
146        self.entries.push(MetadataEntry::str(key, value));
147        self
148    }
149
150    /// Add a u64 entry.
151    pub fn u64(mut self, key: impl Into<Cow<'a, str>>, value: u64) -> Self {
152        self.entries.push(MetadataEntry::u64(key, value));
153        self
154    }
155
156    /// Add a bytes entry.
157    pub fn bytes(mut self, key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, [u8]>>) -> Self {
158        self.entries.push(MetadataEntry::bytes(key, value));
159        self
160    }
161
162    /// Finish building and return the metadata list.
163    pub fn done(self) -> Metadata<'a> {
164        self.entries
165    }
166}
167
168/// Start building a metadata list.
169pub fn metadata_build<'a>() -> MetadataBuilder<'a> {
170    MetadataBuilder {
171        entries: Vec::new(),
172    }
173}
174
175// r[impl rpc.metadata.unknown]
176/// A list of metadata entries.
177pub type Metadata<'a> = Vec<MetadataEntry<'a>>;
178
179/// Convert a `Metadata<'a>` to `Metadata<'static>` by cloning any borrowed data.
180pub fn metadata_into_owned(metadata: Metadata<'_>) -> Metadata<'static> {
181    metadata
182        .into_iter()
183        .map(MetadataEntry::into_owned)
184        .collect()
185}
186
187/// Look up a string metadata value by key.
188pub fn metadata_get_str<'a>(metadata: &'a [MetadataEntry<'a>], key: &str) -> Option<&'a str> {
189    metadata.iter().find_map(|e| {
190        if e.key == key {
191            match &e.value {
192                MetadataValue::String(s) => Some(s.as_ref()),
193                _ => None,
194            }
195        } else {
196            None
197        }
198    })
199}
200
201/// Look up a u64 metadata value by key.
202pub fn metadata_get_u64(metadata: &[MetadataEntry], key: &str) -> Option<u64> {
203    metadata.iter().find_map(|e| {
204        if e.key == key {
205            match &e.value {
206                MetadataValue::U64(n) => Some(*n),
207                _ => None,
208            }
209        } else {
210            None
211        }
212    })
213}