use std::collections::{btree_map, BTreeMap};
use serde::{
de::{MapAccess, Visitor},
ser::SerializeMap,
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{Error, Label, Value};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CoseMap(BTreeMap<Label, Value>);
impl CoseMap {
pub fn new() -> Self {
CoseMap(BTreeMap::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn contains_key(&self, key: impl Into<Label>) -> bool {
self.0.contains_key(&key.into())
}
pub fn get(&self, key: impl Into<Label>) -> Option<&Value> {
self.0.get(&key.into())
}
pub fn insert(&mut self, key: impl Into<Label>, value: impl Into<Value>) -> Option<Value> {
self.0.insert(key.into(), value.into())
}
pub fn remove(&mut self, key: impl Into<Label>) -> Option<Value> {
self.0.remove(&key.into())
}
pub fn iter(&self) -> btree_map::Iter<'_, Label, Value> {
self.0.iter()
}
pub fn get_i64(&self, key: impl Into<Label>) -> Result<Option<i64>, Error> {
match self.0.get(&key.into()) {
None => Ok(None),
Some(Value::Integer(i)) => i64::try_from(*i)
.map(Some)
.map_err(|_| Error::UnexpectedType("integer out of i64 range".into())),
Some(_) => Err(Error::UnexpectedType("expected an integer".into())),
}
}
pub fn get_bytes(&self, key: impl Into<Label>) -> Result<Option<&[u8]>, Error> {
match self.0.get(&key.into()) {
None => Ok(None),
Some(Value::Bytes(b)) => Ok(Some(b)),
Some(_) => Err(Error::UnexpectedType("expected a byte string".into())),
}
}
pub fn get_text(&self, key: impl Into<Label>) -> Result<Option<&str>, Error> {
match self.0.get(&key.into()) {
None => Ok(None),
Some(Value::Text(s)) => Ok(Some(s)),
Some(_) => Err(Error::UnexpectedType("expected a text string".into())),
}
}
pub fn get_bool(&self, key: impl Into<Label>) -> Result<Option<bool>, Error> {
match self.0.get(&key.into()) {
None => Ok(None),
Some(Value::Bool(b)) => Ok(Some(*b)),
Some(_) => Err(Error::UnexpectedType("expected a boolean".into())),
}
}
pub fn get_array(&self, key: impl Into<Label>) -> Result<Option<&[Value]>, Error> {
match self.0.get(&key.into()) {
None => Ok(None),
Some(Value::Array(a)) => Ok(Some(a)),
Some(_) => Err(Error::UnexpectedType("expected an array".into())),
}
}
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
Ok(cbor2::from_slice(data)?)
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
Ok(cbor2::to_canonical_vec(self)?)
}
}
impl FromIterator<(Label, Value)> for CoseMap {
fn from_iter<T: IntoIterator<Item = (Label, Value)>>(iter: T) -> Self {
CoseMap(BTreeMap::from_iter(iter))
}
}
impl<'a> IntoIterator for &'a CoseMap {
type Item = (&'a Label, &'a Value);
type IntoIter = btree_map::Iter<'a, Label, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for CoseMap {
type Item = (Label, Value);
type IntoIter = btree_map::IntoIter<Label, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Serialize for CoseMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in &self.0 {
map.serialize_entry(k, v)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for CoseMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MapVisitor;
impl<'de> Visitor<'de> for MapVisitor {
type Value = CoseMap;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a COSE map keyed by integers or text strings")
}
fn visit_map<A>(self, mut access: A) -> Result<CoseMap, A::Error>
where
A: MapAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((key, value)) = access.next_entry::<Label, Value>()? {
if map.insert(key, value).is_some() {
return Err(serde::de::Error::custom("duplicate COSE map label"));
}
}
Ok(CoseMap(map))
}
}
deserializer.deserialize_map(MapVisitor)
}
}