blueprint_core/
metadata.rs1use core::convert::Infallible;
4use core::fmt;
5use core::str::FromStr;
6
7use alloc::borrow::Cow;
8use alloc::collections::BTreeMap;
9use alloc::collections::btree_map::{Entry, Iter, IterMut};
10use alloc::string::String;
11use alloc::vec::Vec;
12use bytes::{Bytes, BytesMut};
13
14#[derive(Clone, Debug, Default, PartialEq)]
20pub struct MetadataMap<T> {
21 map: BTreeMap<Cow<'static, str>, T>,
22}
23
24impl<T> MetadataMap<T> {
25 pub fn new() -> Self {
27 Self {
28 map: BTreeMap::new(),
29 }
30 }
31
32 pub fn len(&self) -> usize {
34 self.map.len()
35 }
36
37 pub fn is_empty(&self) -> bool {
39 self.map.is_empty()
40 }
41
42 pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<T>
50 where
51 K: Into<Cow<'static, str>>,
52 V: Into<T>,
53 {
54 self.map.insert(key.into(), value.into())
55 }
56
57 pub fn get<K>(&self, key: K) -> Option<&T>
59 where
60 K: AsRef<str>,
61 {
62 self.map.get(key.as_ref())
63 }
64
65 pub fn get_mut<K>(&mut self, key: &K) -> Option<&mut T>
67 where
68 K: AsRef<str>,
69 {
70 self.map.get_mut(key.as_ref())
71 }
72
73 pub fn remove<K>(&mut self, key: &K) -> Option<T>
76 where
77 K: AsRef<str>,
78 {
79 self.map.remove(key.as_ref())
80 }
81
82 pub fn clear(&mut self) {
84 self.map.clear();
85 }
86
87 #[allow(clippy::iter_without_into_iter)] pub fn iter(&self) -> Iter<'_, Cow<'static, str>, T> {
90 self.map.iter()
91 }
92
93 #[allow(clippy::iter_without_into_iter)] pub fn iter_mut(&mut self) -> IterMut<'_, Cow<'static, str>, T> {
96 self.map.iter_mut()
97 }
98
99 pub fn entry<K>(&mut self, key: K) -> Entry<'_, Cow<'static, str>, T>
101 where
102 K: Into<Cow<'static, str>>,
103 {
104 self.map.entry(key.into())
105 }
106
107 pub fn extend(&mut self, other: Self) {
109 self.map.extend(other.map);
110 }
111}
112
113#[derive(Clone, Default)]
119pub struct MetadataValue {
120 inner: Bytes,
121 is_sensitive: bool,
122}
123
124impl FromStr for MetadataValue {
125 type Err = Infallible;
126
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 Ok(Self {
129 inner: Bytes::copy_from_slice(s.as_bytes()),
130 is_sensitive: false,
131 })
132 }
133}
134
135impl MetadataValue {
136 pub fn from_bytes(value: Bytes) -> Self {
138 Self {
139 inner: value,
140 is_sensitive: false,
141 }
142 }
143
144 pub fn from_sensitive_str(value: &str) -> Self {
146 Self {
147 inner: Bytes::copy_from_slice(value.as_bytes()),
148 is_sensitive: true,
149 }
150 }
151
152 pub fn from_sensitive_bytes(value: Bytes) -> Self {
154 Self {
155 inner: value,
156 is_sensitive: true,
157 }
158 }
159
160 pub fn is_sensitive(&self) -> bool {
162 self.is_sensitive
163 }
164
165 pub fn len(&self) -> usize {
167 self.inner.len()
168 }
169
170 pub fn is_empty(&self) -> bool {
172 self.inner.is_empty()
173 }
174
175 pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
177 core::str::from_utf8(&self.inner)
178 }
179
180 pub fn into_bytes(self) -> Bytes {
182 self.inner
183 }
184
185 pub fn as_bytes(&self) -> &[u8] {
186 &self.inner[..]
187 }
188}
189
190impl AsRef<[u8]> for MetadataValue {
191 #[inline]
192 fn as_ref(&self) -> &[u8] {
193 self.inner.as_ref()
194 }
195}
196
197impl fmt::Debug for MetadataValue {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 if self.is_sensitive {
200 f.write_str("Sensitive")
201 } else {
202 f.write_str("\"")?;
203 let mut from = 0;
204 let bytes = self.as_bytes();
205 for (i, &b) in bytes.iter().enumerate() {
206 if !is_visible_ascii(b) || b == b'"' {
207 if from != i {
208 f.write_str(
209 core::str::from_utf8(&bytes[from..i]).map_err(|_| fmt::Error)?,
210 )?;
211 }
212 if b == b'"' {
213 f.write_str("\\\"")?;
214 } else {
215 write!(f, "\\x{b:x}")?;
216 }
217 from = i + 1;
218 }
219 }
220
221 if from != bytes.len() {
222 f.write_str(core::str::from_utf8(&bytes[from..]).map_err(|_| fmt::Error)?)?;
223 }
224 f.write_str("\"")
225 }
226 }
227}
228
229impl From<&str> for MetadataValue {
230 fn from(value: &str) -> Self {
231 Self::from_str(value).unwrap()
232 }
233}
234
235impl From<Bytes> for MetadataValue {
236 fn from(value: Bytes) -> Self {
237 Self::from_bytes(value)
238 }
239}
240
241impl From<&Bytes> for MetadataValue {
242 fn from(value: &Bytes) -> Self {
243 Self::from_bytes(value.clone())
244 }
245}
246
247impl From<BytesMut> for MetadataValue {
248 fn from(value: BytesMut) -> Self {
249 Self::from_bytes(value.freeze())
250 }
251}
252
253impl From<&BytesMut> for MetadataValue {
254 fn from(value: &BytesMut) -> Self {
255 Self::from_bytes(value.clone().freeze())
256 }
257}
258
259impl From<String> for MetadataValue {
260 fn from(value: String) -> Self {
261 Self::from_str(&value).unwrap()
262 }
263}
264
265impl From<&String> for MetadataValue {
266 fn from(value: &String) -> Self {
267 Self::from_str(value).unwrap()
268 }
269}
270
271impl From<&[u8]> for MetadataValue {
272 fn from(value: &[u8]) -> Self {
273 Self::from_bytes(Bytes::copy_from_slice(value))
274 }
275}
276
277impl From<Vec<u8>> for MetadataValue {
278 fn from(value: Vec<u8>) -> Self {
279 Self::from_bytes(Bytes::from(value))
280 }
281}
282
283impl From<&Vec<u8>> for MetadataValue {
284 fn from(value: &Vec<u8>) -> Self {
285 Self::from_bytes(Bytes::copy_from_slice(value))
286 }
287}
288
289impl<const N: usize> From<[u8; N]> for MetadataValue {
290 fn from(value: [u8; N]) -> Self {
291 Self::from_bytes(Bytes::copy_from_slice(&value))
292 }
293}
294
295impl<const N: usize> From<&[u8; N]> for MetadataValue {
296 fn from(value: &[u8; N]) -> Self {
297 Self::from_bytes(Bytes::copy_from_slice(value))
298 }
299}
300
301macro_rules! impl_from_numbers {
302 ($($t:ty),*) => {
303 $(
304 impl From<$t> for MetadataValue {
306 fn from(value: $t) -> Self {
307 Self::from_bytes(Bytes::copy_from_slice(&value.to_be_bytes()))
308 }
309 }
310
311 impl From<&$t> for MetadataValue {
312 fn from(value: &$t) -> Self {
313 Self::from_bytes(Bytes::copy_from_slice(&value.to_be_bytes()))
314 }
315 }
316 )*
317
318 };
319}
320
321macro_rules! impl_try_from_metadata_for_numbers {
322 ($($t:ty),*) => {
323 $(
324 impl core::convert::TryFrom<MetadataValue> for $t {
326 type Error = core::array::TryFromSliceError;
327
328 fn try_from(value: MetadataValue) -> Result<Self, Self::Error> {
329 let bytes = value.as_bytes();
330 let arr: [u8; core::mem::size_of::<Self>()] = bytes.try_into()?;
331 Ok(Self::from_be_bytes(arr))
332 }
333 }
334
335 impl core::convert::TryFrom<&MetadataValue> for $t {
337 type Error = core::array::TryFromSliceError;
338
339 fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
340 let bytes = value.as_bytes();
341 let arr: [u8; core::mem::size_of::<Self>()] = bytes.try_into()?;
342 Ok(Self::from_be_bytes(arr))
343 }
344 }
345 )*
346 };
347}
348
349impl_from_numbers! { u16, u32, u64, u128, usize, i16, i32, i64, i128, isize }
350impl_try_from_metadata_for_numbers! { u16, u32, u64, u128, usize, i16, i32, i64, i128, isize }
351
352const fn is_visible_ascii(b: u8) -> bool {
353 b >= 32 && b < 127 || b == b'\t'
354}
355
356#[cfg(test)]
357mod tests {
358 use super::MetadataValue;
359
360 #[test]
361 fn numeric_try_from_rejects_invalid_byte_lengths() {
362 let value = MetadataValue::from([1u8]);
363 assert!(u64::try_from(value.clone()).is_err());
364 assert!(u64::try_from(&value).is_err());
365 }
366}