use core::fmt;
use std::fmt::Debug;
use http::HeaderMap;
use http::HeaderName;
use http::HeaderValue;
use super::encoding::Ascii;
use super::encoding::Binary;
use super::encoding::ValueEncoding;
use super::key::MetadataKey;
use super::value::MetadataValue;
use crate::metadata::value::UnencodedHeaderValue;
use crate::private;
#[derive(Clone, Debug, Default)]
pub struct MetadataMap {
headers: Vec<(HeaderName, UnencodedHeaderValue)>,
}
#[derive(Debug)]
pub struct Iter<'a> {
inner: std::slice::Iter<'a, (HeaderName, UnencodedHeaderValue)>,
}
#[derive(Debug)]
pub enum KeyAndValueRef<'a> {
Ascii(&'a MetadataKey<Ascii>, &'a MetadataValue<Ascii>),
Binary(&'a MetadataKey<Binary>, &'a MetadataValue<Binary>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Key {
Ascii(MetadataKey<Ascii>),
Binary(MetadataKey<Binary>),
}
pub struct ValueIter<'a, VE>
where
MetadataKey<VE>: Debug,
{
inner: std::slice::Iter<'a, (HeaderName, UnencodedHeaderValue)>,
key: Option<MetadataKey<VE>>,
}
impl<'a, VE> Debug for ValueIter<'a, VE>
where
VE: ValueEncoding,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ValueIter")
.field("inner", &self.inner)
.field("key", &self.key)
.finish()
}
}
pub struct GetAll<'a, VE> {
map: &'a MetadataMap,
key: Option<MetadataKey<VE>>,
}
impl<'a, VE> std::fmt::Debug for GetAll<'a, VE>
where
VE: ValueEncoding,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GetAll")
.field("map", &self.map)
.field("key", &self.key)
.finish()
}
}
impl MetadataMap {
pub fn new() -> Self {
MetadataMap::with_capacity(0)
}
pub(crate) fn from_headers(headers: HeaderMap) -> Result<Self, String> {
let mut ret = Vec::with_capacity(headers.len());
let mut current_key: Option<HeaderName> = None;
for (key, value) in headers {
if let Some(k) = key {
current_key = Some(k);
}
let Some(k) = current_key.as_ref() else {
continue;
};
let key_str = k.as_str();
if Ascii::is_valid_key(key_str) {
if let Ok(mv) = MetadataValue::<Ascii>::try_from(value.as_bytes()) {
ret.push((k.clone(), mv.into_inner()));
}
} else if Binary::is_valid_key(key_str) {
let b = Binary::decode(value.as_bytes(), private::Internal).map_err(|e| {
format!("failed to decode base64 value for key '{key_str}': {e}")
})?;
let mv = unsafe { MetadataValue::<Binary>::from_shared_unchecked(b) };
ret.push((k.clone(), mv.into_inner()));
}
}
Ok(Self { headers: ret })
}
pub(crate) fn into_headers(self) -> HeaderMap {
let mut ret = HeaderMap::with_capacity(self.capacity());
for (key, value) in self.headers {
let bytes = if key.as_str().ends_with("-bin") {
MetadataValue::<Binary>::encode(value.into_bytes())
} else {
MetadataValue::<Ascii>::encode(value.into_bytes())
};
unsafe {
ret.append(key, HeaderValue::from_maybe_shared_unchecked(bytes));
}
}
ret
}
pub fn with_capacity(capacity: usize) -> MetadataMap {
MetadataMap {
headers: Vec::with_capacity(capacity),
}
}
pub fn len(&self) -> usize {
self.headers.len()
}
pub fn is_empty(&self) -> bool {
self.headers.is_empty()
}
pub fn clear(&mut self) {
self.headers.clear();
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(KeyAndValueRef<'_>) -> bool,
{
self.headers.retain(|(name, value)| {
let key_and_value = if !name.as_str().ends_with("-bin") {
KeyAndValueRef::Ascii(
MetadataKey::unchecked_from_header_name_ref(name),
MetadataValue::unchecked_from_header_value_ref(value),
)
} else {
KeyAndValueRef::Binary(
MetadataKey::unchecked_from_header_name_ref(name),
MetadataValue::unchecked_from_header_value_ref(value),
)
};
f(key_and_value)
});
}
pub fn capacity(&self) -> usize {
self.headers.capacity()
}
pub fn reserve(&mut self, additional: usize) {
self.headers.reserve(additional);
}
pub fn get<K>(&self, key: K) -> Option<&MetadataValue<Ascii>>
where
K: AsMetadataKey<Ascii>,
{
key.get(self, private::Internal)
}
pub fn get_bin<K>(&self, key: K) -> Option<&MetadataValue<Binary>>
where
K: AsMetadataKey<Binary>,
{
key.get(self, private::Internal)
}
pub fn get_all<K>(&self, key: K) -> GetAll<'_, Ascii>
where
K: AsMetadataKey<Ascii>,
{
key.get_all(self, private::Internal)
}
pub fn get_all_bin<K>(&self, key: K) -> GetAll<'_, Binary>
where
K: AsMetadataKey<Binary>,
{
key.get_all(self, private::Internal)
}
pub fn contains_key<K>(&self, key: K) -> bool
where
K: AsEncodingAgnosticMetadataKey,
{
key.contains_key(self, private::Internal)
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.headers.iter(),
}
}
pub fn insert<K>(&mut self, key: K, val: MetadataValue<Ascii>) -> Option<MetadataValue<Ascii>>
where
K: IntoMetadataKey<Ascii>,
{
key.insert(self, val, private::Internal)
}
pub fn insert_bin<K>(
&mut self,
key: K,
val: MetadataValue<Binary>,
) -> Option<MetadataValue<Binary>>
where
K: IntoMetadataKey<Binary>,
{
key.insert(self, val, private::Internal)
}
pub fn append<K>(&mut self, key: K, value: MetadataValue<Ascii>)
where
K: IntoMetadataKey<Ascii>,
{
key.append(self, value, private::Internal);
}
pub fn append_bin<K>(&mut self, key: K, value: MetadataValue<Binary>)
where
K: IntoMetadataKey<Binary>,
{
key.append(self, value, private::Internal);
}
pub fn remove<K>(&mut self, key: K) -> Option<MetadataValue<Ascii>>
where
K: AsMetadataKey<Ascii>,
{
key.remove(self, private::Internal)
}
pub fn remove_bin<K>(&mut self, key: K) -> Option<MetadataValue<Binary>>
where
K: AsMetadataKey<Binary>,
{
key.remove(self, private::Internal)
}
pub fn remove_all<K>(&mut self, key: K)
where
K: AsMetadataKey<Ascii>,
{
key.remove_all(self, private::Internal)
}
pub fn remove_all_bin<K>(&mut self, key: K)
where
K: AsMetadataKey<Binary>,
{
key.remove_all(self, private::Internal)
}
pub(crate) fn merge(&mut self, other: MetadataMap) {
self.headers.extend(other.headers);
}
}
impl TryFrom<tonic::metadata::MetadataMap> for MetadataMap {
type Error = String;
fn try_from(tonic_map: tonic::metadata::MetadataMap) -> Result<Self, Self::Error> {
Self::from_headers(tonic_map.into_headers())
}
}
impl From<MetadataMap> for tonic::metadata::MetadataMap {
fn from(map: MetadataMap) -> Self {
Self::from_headers(map.into_headers())
}
}
impl<'a> Iterator for Iter<'a> {
type Item = KeyAndValueRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(name, value)| {
if !name.as_str().ends_with("-bin") {
KeyAndValueRef::Ascii(
MetadataKey::unchecked_from_header_name_ref(name),
MetadataValue::unchecked_from_header_value_ref(value),
)
} else {
KeyAndValueRef::Binary(
MetadataKey::unchecked_from_header_name_ref(name),
MetadataValue::unchecked_from_header_value_ref(value),
)
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, VE: ValueEncoding> Iterator for ValueIter<'a, VE>
where
VE: 'a,
{
type Item = &'a MetadataValue<VE>;
fn next(&mut self) -> Option<Self::Item> {
let key = self.key.as_ref()?;
for (k, value) in self.inner.by_ref() {
if k == key.inner {
return Some(MetadataValue::unchecked_from_header_value_ref(value));
}
}
None
}
}
impl<'a, VE: ValueEncoding> GetAll<'a, VE> {
pub fn iter(&self) -> ValueIter<'a, VE> {
ValueIter {
inner: self.map.headers.iter(),
key: self.key.clone(),
}
}
}
impl<VE: ValueEncoding> PartialEq for GetAll<'_, VE> {
fn eq(&self, other: &Self) -> bool {
self.iter().eq(other.iter())
}
}
impl<'a, VE: ValueEncoding> IntoIterator for GetAll<'a, VE>
where
VE: 'a,
{
type Item = &'a MetadataValue<VE>;
type IntoIter = ValueIter<'a, VE>;
fn into_iter(self) -> ValueIter<'a, VE> {
self.iter()
}
}
impl<'a, 'b: 'a, VE: ValueEncoding> IntoIterator for &'b GetAll<'a, VE> {
type Item = &'a MetadataValue<VE>;
type IntoIter = ValueIter<'a, VE>;
fn into_iter(self) -> ValueIter<'a, VE> {
self.iter()
}
}
pub trait IntoMetadataKey<VE: ValueEncoding> {
#[doc(hidden)]
fn insert(
self,
map: &mut MetadataMap,
val: MetadataValue<VE>,
_: private::Internal,
) -> Option<MetadataValue<VE>>;
#[doc(hidden)]
fn append(self, map: &mut MetadataMap, val: MetadataValue<VE>, _: private::Internal);
}
impl<VE: ValueEncoding> IntoMetadataKey<VE> for MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn insert(
self,
map: &mut MetadataMap,
val: MetadataValue<VE>,
_: private::Internal,
) -> Option<MetadataValue<VE>> {
let key = self.inner;
let mut new_val = Some(val.into_inner());
let mut old_val = None;
let mut write_idx = 0;
for read_idx in 0..map.headers.len() {
if map.headers[read_idx].0 == key {
if let Some(v) = new_val.take() {
let replaced = std::mem::replace(&mut map.headers[read_idx].1, v);
old_val = Some(MetadataValue::unchecked_from_header_value(replaced));
write_idx += 1;
}
} else {
if read_idx != write_idx {
map.headers.swap(read_idx, write_idx);
}
write_idx += 1;
}
}
map.headers.truncate(write_idx);
if let Some(v) = new_val {
map.headers.push((key, v));
}
old_val
}
#[doc(hidden)]
#[inline]
fn append(self, map: &mut MetadataMap, val: MetadataValue<VE>, _: private::Internal) {
map.headers.push((self.inner, val.into_inner()));
}
}
impl<VE: ValueEncoding> IntoMetadataKey<VE> for &MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn insert(
self,
map: &mut MetadataMap,
val: MetadataValue<VE>,
_: private::Internal,
) -> Option<MetadataValue<VE>> {
let key = &self.inner;
let mut new_val = Some(val.into_inner());
let mut old_val = None;
let mut write_idx = 0;
for read_idx in 0..map.headers.len() {
if map.headers[read_idx].0 == key {
if let Some(v) = new_val.take() {
let replaced = std::mem::replace(&mut map.headers[read_idx].1, v);
old_val = Some(MetadataValue::unchecked_from_header_value(replaced));
write_idx += 1;
}
} else {
if read_idx != write_idx {
map.headers.swap(read_idx, write_idx);
}
write_idx += 1;
}
}
map.headers.truncate(write_idx);
if let Some(v) = new_val {
map.headers.push((key.clone(), v));
}
old_val
}
#[doc(hidden)]
#[inline]
fn append(self, map: &mut MetadataMap, val: MetadataValue<VE>, _: private::Internal) {
map.headers.push((self.inner.clone(), val.into_inner()));
}
}
impl<VE: ValueEncoding> IntoMetadataKey<VE> for &'static str {
#[doc(hidden)]
#[inline]
fn insert(
self,
map: &mut MetadataMap,
val: MetadataValue<VE>,
token: private::Internal,
) -> Option<MetadataValue<VE>> {
let key = MetadataKey::<VE>::from_static(self);
key.insert(map, val, token)
}
#[doc(hidden)]
#[inline]
fn append(self, map: &mut MetadataMap, val: MetadataValue<VE>, token: private::Internal) {
let key = MetadataKey::<VE>::from_static(self);
key.append(map, val, token);
}
}
pub trait AsMetadataKey<VE: ValueEncoding> {
#[doc(hidden)]
fn get(self, map: &MetadataMap, _: private::Internal) -> Option<&MetadataValue<VE>>;
#[doc(hidden)]
fn get_all(self, map: &MetadataMap, _: private::Internal) -> GetAll<'_, VE>;
#[doc(hidden)]
fn remove(self, map: &mut MetadataMap, _: private::Internal) -> Option<MetadataValue<VE>>;
#[doc(hidden)]
fn remove_all(self, map: &mut MetadataMap, _: private::Internal);
}
impl<VE: ValueEncoding> AsMetadataKey<VE> for MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn get(self, map: &MetadataMap, _: private::Internal) -> Option<&MetadataValue<VE>> {
map.headers
.iter()
.find(|(k, _)| k == self.inner)
.map(|(_, v)| MetadataValue::unchecked_from_header_value_ref(v))
}
#[doc(hidden)]
#[inline]
fn get_all(self, map: &MetadataMap, _: private::Internal) -> GetAll<'_, VE> {
GetAll {
map,
key: Some(self),
}
}
#[doc(hidden)]
#[inline]
fn remove(self, map: &mut MetadataMap, token: private::Internal) -> Option<MetadataValue<VE>> {
self.inner.as_str().remove(map, token)
}
#[doc(hidden)]
#[inline]
fn remove_all(self, map: &mut MetadataMap, _: private::Internal) {
map.headers.retain(|h| h.0 != self.inner);
}
}
impl<VE: ValueEncoding> AsMetadataKey<VE> for &MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn get(self, map: &MetadataMap, _: private::Internal) -> Option<&MetadataValue<VE>> {
map.headers
.iter()
.find(|(k, _)| k == self.inner)
.map(|(_, v)| MetadataValue::unchecked_from_header_value_ref(v))
}
#[doc(hidden)]
#[inline]
fn get_all(self, map: &MetadataMap, _: private::Internal) -> GetAll<'_, VE> {
GetAll {
map,
key: Some(self.clone()),
}
}
#[doc(hidden)]
#[inline]
fn remove(self, map: &mut MetadataMap, token: private::Internal) -> Option<MetadataValue<VE>> {
self.inner.as_str().remove(map, token)
}
#[doc(hidden)]
#[inline]
fn remove_all(self, map: &mut MetadataMap, _: private::Internal) {
map.headers.retain(|h| h.0 != self.inner);
}
}
impl<VE: ValueEncoding> AsMetadataKey<VE> for &str {
#[doc(hidden)]
#[inline]
fn get(self, map: &MetadataMap, _: private::Internal) -> Option<&MetadataValue<VE>> {
if !VE::is_valid_key(self) {
return None;
}
map.headers
.iter()
.find(|(k, _)| k == self)
.map(|(_, v)| MetadataValue::unchecked_from_header_value_ref(v))
}
#[doc(hidden)]
#[inline]
fn get_all(self, map: &MetadataMap, _: private::Internal) -> GetAll<'_, VE> {
let key = if VE::is_valid_key(self) {
Some(MetadataKey::<VE>::from_bytes(self.as_bytes()).unwrap())
} else {
None
};
GetAll { map, key }
}
#[doc(hidden)]
#[inline]
fn remove(self, map: &mut MetadataMap, _: private::Internal) -> Option<MetadataValue<VE>> {
if !VE::is_valid_key(self) {
return None;
}
let mut extracted = map.headers.extract_if(.., |(k, _v)| *k == self);
let first_match = extracted
.next()
.map(|(_k, v)| MetadataValue::unchecked_from_header_value(v));
extracted.for_each(drop);
first_match
}
#[doc(hidden)]
#[inline]
fn remove_all(self, map: &mut MetadataMap, _: private::Internal) {
if !VE::is_valid_key(self) {
return;
}
map.headers.retain(|h| h.0 != self);
}
}
impl<VE: ValueEncoding> AsMetadataKey<VE> for String {
#[doc(hidden)]
#[inline]
fn get(self, map: &MetadataMap, token: private::Internal) -> Option<&MetadataValue<VE>> {
AsMetadataKey::<VE>::get(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn get_all(self, map: &MetadataMap, token: private::Internal) -> GetAll<'_, VE> {
AsMetadataKey::<VE>::get_all(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn remove(self, map: &mut MetadataMap, token: private::Internal) -> Option<MetadataValue<VE>> {
AsMetadataKey::<VE>::remove(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn remove_all(self, map: &mut MetadataMap, token: private::Internal) {
AsMetadataKey::<VE>::remove_all(self.as_str(), map, token)
}
}
impl<VE: ValueEncoding> AsMetadataKey<VE> for &String {
#[doc(hidden)]
#[inline]
fn get(self, map: &MetadataMap, token: private::Internal) -> Option<&MetadataValue<VE>> {
AsMetadataKey::<VE>::get(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn get_all(self, map: &MetadataMap, token: private::Internal) -> GetAll<'_, VE> {
AsMetadataKey::<VE>::get_all(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn remove(self, map: &mut MetadataMap, token: private::Internal) -> Option<MetadataValue<VE>> {
AsMetadataKey::<VE>::remove(self.as_str(), map, token)
}
#[doc(hidden)]
#[inline]
fn remove_all(self, map: &mut MetadataMap, token: private::Internal) {
AsMetadataKey::<VE>::remove_all(self.as_str(), map, token)
}
}
pub trait AsEncodingAgnosticMetadataKey {
#[doc(hidden)]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool;
}
impl<VE: ValueEncoding> AsEncodingAgnosticMetadataKey for MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool {
map.headers.iter().any(|(k, _)| k == self.inner)
}
}
impl<VE: ValueEncoding> AsEncodingAgnosticMetadataKey for &MetadataKey<VE> {
#[doc(hidden)]
#[inline]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool {
map.headers.iter().any(|(k, _)| k == self.inner)
}
}
impl AsEncodingAgnosticMetadataKey for &str {
#[doc(hidden)]
#[inline]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool {
map.headers.iter().any(|(k, _)| k == *self)
}
}
impl AsEncodingAgnosticMetadataKey for String {
#[doc(hidden)]
#[inline]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool {
map.headers.iter().any(|(k, _)| k == self.as_str())
}
}
impl AsEncodingAgnosticMetadataKey for &String {
#[doc(hidden)]
#[inline]
fn contains_key(&self, map: &MetadataMap, _: private::Internal) -> bool {
map.headers.iter().any(|(k, _)| k == self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_headers_filters_invalid_headers() {
let mut http_map = http::HeaderMap::new();
http_map.insert("x-host", "example.com".parse().unwrap());
http_map.insert("trace-proto-bin", "SGVsbG8hIQ==".parse().unwrap());
http_map.insert(HeaderName::from_static("x-host!"), "val".parse().unwrap());
http_map.insert("x-invalid-ascii", HeaderValue::from_bytes(&[0xFA]).unwrap());
let map = MetadataMap::from_headers(http_map).unwrap();
assert_eq!(map.len(), 2);
assert_eq!(map.get("x-host").unwrap(), "example.com");
assert_eq!(map.get_bin("trace-proto-bin").unwrap(), "Hello!!");
assert!(!map.contains_key("x-host!"));
assert!(!map.contains_key("x-invalid-ascii"));
}
#[test]
fn test_from_headers_fails_on_invalid_bin_header() {
let mut http_map = http::HeaderMap::new();
http_map.insert("invalid-bin", "not-base64-!!!".parse().unwrap());
let result = MetadataMap::from_headers(http_map);
assert!(result.is_err());
}
#[test]
fn test_into_headers() {
let mut map = MetadataMap::new();
map.insert("x-host", "example.com".parse().unwrap());
map.append("x-host", "google.com".parse().unwrap());
map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"Hello!!"));
let headers = map.into_headers();
assert_eq!(headers.len(), 3);
let hosts: Vec<_> = headers.get_all("x-host").iter().collect();
assert_eq!(hosts.len(), 2);
assert_eq!(hosts[0], "example.com");
assert_eq!(hosts[1], "google.com");
assert_eq!(headers.get("trace-proto-bin").unwrap(), "SGVsbG8hIQ");
}
#[test]
fn test_iter_categorizes_ascii_entries() {
let mut map = MetadataMap::new();
map.insert("x-word", "hello".parse().unwrap());
map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye"));
map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123"));
let mut found_x_word = false;
for key_and_value in map.iter() {
if let KeyAndValueRef::Ascii(key, _value) = key_and_value {
if key.as_str() == "x-word" {
found_x_word = true;
} else {
panic!("Unexpected key");
}
}
}
assert!(found_x_word);
}
#[test]
fn test_iter_categorizes_binary_entries() {
let mut map = MetadataMap::new();
map.insert("x-word", "hello".parse().unwrap());
map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye"));
let mut found_x_word_bin = false;
for key_and_value in map.iter() {
if let KeyAndValueRef::Binary(key, _value) = key_and_value {
if key.as_str() == "x-word-bin" {
found_x_word_bin = true;
} else {
panic!("Unexpected key");
}
}
}
assert!(found_x_word_bin);
}
#[test]
fn test_remove_all_preserves_other_keys_order() {
let mut map = MetadataMap::new();
map.append("a", "1".parse().unwrap());
map.append("b", "2".parse().unwrap());
map.append("a", "3".parse().unwrap());
map.append("b", "4".parse().unwrap());
map.remove_all("a");
let keys: Vec<_> = map
.iter()
.map(|kv| match kv {
KeyAndValueRef::Ascii(_, v) => v.to_str(),
_ => panic!(),
})
.collect();
assert_eq!(keys, vec!["2", "4"]);
}
#[test]
fn test_remove_all_bin() {
let mut map = MetadataMap::new();
map.insert_bin(
"trace-proto-bin",
MetadataValue::from_bytes(b"[binary data]"),
);
map.append_bin(
"trace-proto-bin",
MetadataValue::from_bytes(b"[binary data 2]"),
);
map.insert("x-host", "example.com".parse().unwrap());
map.remove_all_bin("trace-proto-bin");
assert!(map.get_bin("trace-proto-bin").is_none());
assert!(map.get("x-host").is_some());
}
#[test]
fn test_retain() {
let mut map = MetadataMap::new();
map.insert("x-host", "hello".parse().unwrap());
map.insert("x-number", "123".parse().unwrap());
map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"world"));
map.retain(|entry| match entry {
KeyAndValueRef::Ascii(key, _) => key == "x-host",
_ => false,
});
assert_eq!(map.len(), 1);
assert!(map.contains_key("x-host"));
assert!(!map.contains_key("x-number"));
assert!(!map.contains_key("trace-proto-bin"));
}
#[test]
fn test_tonic_conversions() {
let mut map = MetadataMap::new();
map.insert("x-host", "example.com".parse().unwrap());
map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"Hello!!"));
let tonic_map: tonic::metadata::MetadataMap = map.clone().into();
assert_eq!(tonic_map.len(), 2);
assert_eq!(tonic_map.get("x-host").unwrap(), "example.com");
assert_eq!(tonic_map.get_bin("trace-proto-bin").unwrap(), "Hello!!");
assert!(!tonic_map.get("x-host").unwrap().is_sensitive());
assert!(!tonic_map.get_bin("trace-proto-bin").unwrap().is_sensitive());
let back_map: MetadataMap = tonic_map.try_into().unwrap();
assert_eq!(back_map.len(), 2);
assert_eq!(back_map.get("x-host").unwrap(), "example.com");
assert_eq!(back_map.get_bin("trace-proto-bin").unwrap(), "Hello!!");
assert!(back_map.get("x-host").unwrap().is_sensitive());
assert!(back_map.get_bin("trace-proto-bin").unwrap().is_sensitive());
}
}