use core::convert::Infallible;
use core::fmt;
use core::str::FromStr;
use alloc::borrow::Cow;
use alloc::collections::BTreeMap;
use alloc::collections::btree_map::{Entry, Iter, IterMut};
use alloc::string::String;
use alloc::vec::Vec;
use bytes::{Bytes, BytesMut};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MetadataMap<T> {
map: BTreeMap<Cow<'static, str>, T>,
}
impl<T> MetadataMap<T> {
pub fn new() -> Self {
Self {
map: BTreeMap::new(),
}
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<T>
where
K: Into<Cow<'static, str>>,
V: Into<T>,
{
self.map.insert(key.into(), value.into())
}
pub fn get<K>(&self, key: K) -> Option<&T>
where
K: AsRef<str>,
{
self.map.get(key.as_ref())
}
pub fn get_mut<K>(&mut self, key: &K) -> Option<&mut T>
where
K: AsRef<str>,
{
self.map.get_mut(key.as_ref())
}
pub fn remove<K>(&mut self, key: &K) -> Option<T>
where
K: AsRef<str>,
{
self.map.remove(key.as_ref())
}
pub fn clear(&mut self) {
self.map.clear();
}
#[allow(clippy::iter_without_into_iter)] pub fn iter(&self) -> Iter<'_, Cow<'static, str>, T> {
self.map.iter()
}
#[allow(clippy::iter_without_into_iter)] pub fn iter_mut(&mut self) -> IterMut<'_, Cow<'static, str>, T> {
self.map.iter_mut()
}
pub fn entry<K>(&mut self, key: K) -> Entry<'_, Cow<'static, str>, T>
where
K: Into<Cow<'static, str>>,
{
self.map.entry(key.into())
}
pub fn extend(&mut self, other: Self) {
self.map.extend(other.map);
}
}
#[derive(Clone, Default)]
pub struct MetadataValue {
inner: Bytes,
is_sensitive: bool,
}
impl FromStr for MetadataValue {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
inner: Bytes::copy_from_slice(s.as_bytes()),
is_sensitive: false,
})
}
}
impl MetadataValue {
pub fn from_bytes(value: Bytes) -> Self {
Self {
inner: value,
is_sensitive: false,
}
}
pub fn from_sensitive_str(value: &str) -> Self {
Self {
inner: Bytes::copy_from_slice(value.as_bytes()),
is_sensitive: true,
}
}
pub fn from_sensitive_bytes(value: Bytes) -> Self {
Self {
inner: value,
is_sensitive: true,
}
}
pub fn is_sensitive(&self) -> bool {
self.is_sensitive
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(&self.inner)
}
pub fn into_bytes(self) -> Bytes {
self.inner
}
pub fn as_bytes(&self) -> &[u8] {
&self.inner[..]
}
}
impl AsRef<[u8]> for MetadataValue {
#[inline]
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
impl fmt::Debug for MetadataValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_sensitive {
f.write_str("Sensitive")
} else {
f.write_str("\"")?;
let mut from = 0;
let bytes = self.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if !is_visible_ascii(b) || b == b'"' {
if from != i {
f.write_str(
core::str::from_utf8(&bytes[from..i]).map_err(|_| fmt::Error)?,
)?;
}
if b == b'"' {
f.write_str("\\\"")?;
} else {
write!(f, "\\x{b:x}")?;
}
from = i + 1;
}
}
if from != bytes.len() {
f.write_str(core::str::from_utf8(&bytes[from..]).map_err(|_| fmt::Error)?)?;
}
f.write_str("\"")
}
}
}
impl From<&str> for MetadataValue {
fn from(value: &str) -> Self {
Self::from_str(value).unwrap()
}
}
impl From<Bytes> for MetadataValue {
fn from(value: Bytes) -> Self {
Self::from_bytes(value)
}
}
impl From<&Bytes> for MetadataValue {
fn from(value: &Bytes) -> Self {
Self::from_bytes(value.clone())
}
}
impl From<BytesMut> for MetadataValue {
fn from(value: BytesMut) -> Self {
Self::from_bytes(value.freeze())
}
}
impl From<&BytesMut> for MetadataValue {
fn from(value: &BytesMut) -> Self {
Self::from_bytes(value.clone().freeze())
}
}
impl From<String> for MetadataValue {
fn from(value: String) -> Self {
Self::from_str(&value).unwrap()
}
}
impl From<&String> for MetadataValue {
fn from(value: &String) -> Self {
Self::from_str(value).unwrap()
}
}
impl From<&[u8]> for MetadataValue {
fn from(value: &[u8]) -> Self {
Self::from_bytes(Bytes::copy_from_slice(value))
}
}
impl From<Vec<u8>> for MetadataValue {
fn from(value: Vec<u8>) -> Self {
Self::from_bytes(Bytes::from(value))
}
}
impl From<&Vec<u8>> for MetadataValue {
fn from(value: &Vec<u8>) -> Self {
Self::from_bytes(Bytes::copy_from_slice(value))
}
}
impl<const N: usize> From<[u8; N]> for MetadataValue {
fn from(value: [u8; N]) -> Self {
Self::from_bytes(Bytes::copy_from_slice(&value))
}
}
impl<const N: usize> From<&[u8; N]> for MetadataValue {
fn from(value: &[u8; N]) -> Self {
Self::from_bytes(Bytes::copy_from_slice(value))
}
}
macro_rules! impl_from_numbers {
($($t:ty),*) => {
$(
impl From<$t> for MetadataValue {
fn from(value: $t) -> Self {
Self::from_bytes(Bytes::copy_from_slice(&value.to_be_bytes()))
}
}
impl From<&$t> for MetadataValue {
fn from(value: &$t) -> Self {
Self::from_bytes(Bytes::copy_from_slice(&value.to_be_bytes()))
}
}
)*
};
}
macro_rules! impl_try_from_metadata_for_numbers {
($($t:ty),*) => {
$(
impl core::convert::TryFrom<MetadataValue> for $t {
type Error = core::array::TryFromSliceError;
fn try_from(value: MetadataValue) -> Result<Self, Self::Error> {
let bytes = value.as_bytes();
let arr: [u8; core::mem::size_of::<Self>()] = bytes.try_into()?;
Ok(Self::from_be_bytes(arr))
}
}
impl core::convert::TryFrom<&MetadataValue> for $t {
type Error = core::array::TryFromSliceError;
fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
let bytes = value.as_bytes();
let arr: [u8; core::mem::size_of::<Self>()] = bytes.try_into()?;
Ok(Self::from_be_bytes(arr))
}
}
)*
};
}
impl_from_numbers! { u16, u32, u64, u128, usize, i16, i32, i64, i128, isize }
impl_try_from_metadata_for_numbers! { u16, u32, u64, u128, usize, i16, i32, i64, i128, isize }
const fn is_visible_ascii(b: u8) -> bool {
b >= 32 && b < 127 || b == b'\t'
}
#[cfg(test)]
mod tests {
use super::MetadataValue;
#[test]
fn numeric_try_from_rejects_invalid_byte_lengths() {
let value = MetadataValue::from([1u8]);
assert!(u64::try_from(value.clone()).is_err());
assert!(u64::try_from(&value).is_err());
}
}