use crate::alloc::string::ToString;
use crate::models::{Identifier, TimestampInSeconds};
use crate::utils::now;
use core::fmt::{Display, Formatter};
use minicbor::{CborLen, Decode, Encode};
use ockam_core::compat::borrow::ToOwned;
use ockam_core::compat::string::String;
use ockam_core::compat::{collections::BTreeMap, vec::Vec};
use ockam_core::{cbor_encode_preallocate, Message};
use ockam_core::{Decodable, Encodable, Encoded, Result};
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Encode, Decode, CborLen, PartialEq, Eq, Serialize, Deserialize, Message)]
#[rustfmt::skip]
#[cbor(map)]
pub struct AttributesEntry {
#[n(1)]
#[serde(serialize_with = "serialize_attributes", deserialize_with = "deserialize_attributes")]
attributes: BTreeMap<Vec<u8>, Vec<u8>>,
#[n(2)] added_at: TimestampInSeconds,
#[n(3)] expires_at: Option<TimestampInSeconds>,
#[n(4)] attested_by: Option<Identifier>,
}
impl Encodable for AttributesEntry {
fn encode(self) -> Result<Encoded> {
cbor_encode_preallocate(self)
}
}
impl Decodable for AttributesEntry {
fn decode(e: &[u8]) -> Result<Self> {
Ok(minicbor::decode(e)?)
}
}
fn serialize_attributes<S>(
attrs: &BTreeMap<Vec<u8>, Vec<u8>>,
s: S,
) -> core::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = s.serialize_map(Some(attrs.len()))?;
for (key, value) in attrs {
map.serialize_entry(
&String::from_utf8_lossy(key),
&String::from_utf8_lossy(value),
)?;
}
map.end()
}
fn deserialize_attributes<'de, D>(
d: D,
) -> core::result::Result<BTreeMap<Vec<u8>, Vec<u8>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let map = <BTreeMap<String, String>>::deserialize(d)?;
let mut result = BTreeMap::new();
for (key, value) in map {
result.insert(key.as_bytes().to_vec(), value.as_bytes().to_vec());
}
Ok(result)
}
impl Display for AttributesEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let attributes = self.deserialized_key_value_attrs();
f.debug_struct("AttributesEntry")
.field("attributes", &attributes.join(","))
.field("added_at", &self.added_at)
.field(
"expires_at",
&self.expires_at.map_or("n/a".to_string(), |e| e.to_string()),
)
.field(
"attested_by",
&self
.attested_by
.clone()
.map_or("n/a".to_string(), |e| e.to_string()),
)
.finish()
}
}
impl AttributesEntry {
pub fn new(
attrs: BTreeMap<Vec<u8>, Vec<u8>>,
added_at: TimestampInSeconds,
expires_at: Option<TimestampInSeconds>,
attested_by: Option<Identifier>,
) -> Self {
Self {
attributes: attrs,
added_at,
expires_at,
attested_by,
}
}
pub fn attrs(&self) -> &BTreeMap<Vec<u8>, Vec<u8>> {
&self.attributes
}
pub fn string_attributes(self) -> BTreeMap<String, String> {
self.attributes
.into_iter()
.map(|(vec_key, vec_value)| {
(
String::from_utf8_lossy(&vec_key).to_string(),
String::from_utf8_lossy(&vec_value).to_string(),
)
})
.collect()
}
pub fn deserialized_key_value_attrs(&self) -> Vec<String> {
let mut attributes = vec![];
for (key, value) in self.attributes.clone() {
if let (Ok(k), Ok(v)) = (String::from_utf8(key), String::from_utf8(value)) {
attributes.push(format!("{k}={v}"));
}
}
attributes.sort();
attributes
}
pub fn expires_at(&self) -> Option<TimestampInSeconds> {
self.expires_at
}
pub fn added_at(&self) -> TimestampInSeconds {
self.added_at
}
pub fn attested_by(&self) -> Option<Identifier> {
self.attested_by.to_owned()
}
}
impl AttributesEntry {
pub fn single(
attribute_name: Vec<u8>,
attribute_value: Vec<u8>,
expires_at: Option<TimestampInSeconds>,
attested_by: Option<Identifier>,
) -> Result<Self> {
let attrs = BTreeMap::from([(attribute_name, attribute_value)]);
Ok(Self {
attributes: attrs,
added_at: now()?,
expires_at,
attested_by,
})
}
}