use azure_core::error::{Error, ErrorKind, ResultExt};
use azure_core::headers::Headers;
use azure_core::headers::{self, Header, PROPERTIES};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Properties(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Default for Properties {
fn default() -> Self {
Self::new()
}
}
impl From<BTreeMap<Cow<'static, str>, Cow<'static, str>>> for Properties {
fn from(value: BTreeMap<Cow<'static, str>, Cow<'static, str>>) -> Self {
Self(value)
}
}
impl Properties {
pub fn new() -> Self {
Self(BTreeMap::new())
}
pub fn insert<K: Into<Cow<'static, str>>, V: Into<Cow<'static, str>>>(
&mut self,
k: K,
v: V,
) -> Option<Cow<'static, str>> {
self.0.insert(k.into(), v.into())
}
pub fn get(&self, key: &str) -> std::option::Option<&Cow<'_, str>> {
self.0.get(key)
}
}
impl Header for Properties {
fn name(&self) -> headers::HeaderName {
PROPERTIES
}
fn value(&self) -> headers::HeaderValue {
self.0
.iter()
.map(|(k, v)| format!("{}={}", k.as_ref(), base64::encode(v.as_ref())))
.collect::<Vec<_>>()
.join(",")
.into()
}
}
impl TryFrom<&Headers> for Properties {
type Error = crate::Error;
fn try_from(headers: &Headers) -> Result<Self, Self::Error> {
let header_value = headers.get_str(&PROPERTIES)?;
Properties::try_from(header_value)
}
}
impl TryFrom<&str> for Properties {
type Error = crate::Error;
fn try_from(header_value: &str) -> Result<Self, Self::Error> {
let mut properties = Self::new();
if header_value.is_empty() {
return Ok(properties);
}
header_value
.split(',') .map(|key_value_pair| {
let mut key_and_value = key_value_pair.split('=');
let key = key_and_value
.next()
.ok_or_else(|| Error::message(ErrorKind::Other, "missing key"))?;
let value = key_and_value
.next()
.ok_or_else(|| Error::message(ErrorKind::Other, "missing value"))?;
Ok((key, value))
})
.collect::<crate::Result<Vec<(&str, &str)>>>()? .into_iter()
.map(|(key, value)| {
let value = std::str::from_utf8(
&base64::decode(value).map_kind(ErrorKind::DataConversion)?,
)?
.to_owned(); Ok((key, value))
})
.collect::<crate::Result<Vec<(&str, String)>>>()? .into_iter()
.for_each(|(key, value)| {
properties.insert(key.to_owned(), value); });
Ok(properties)
}
}
impl FromStr for Properties {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
TryFrom::try_from(s)
}
}