cipherstash_dynamodb/encrypted_table/
table_attributes.rs

1use super::{AttributeName, SealError, TableAttribute};
2use std::{
3    borrow::Cow,
4    collections::{hash_map::IntoIter, HashMap},
5};
6
7#[derive(Clone, Debug)]
8/// Represents a collection of attributes for a table entry.
9/// Attributes are stored as a map of `String` to `TableAttribute`.
10pub struct TableAttributes(HashMap<AttributeName, TableAttribute>);
11
12impl TableAttributes {
13    pub(crate) fn new() -> Self {
14        Self(HashMap::new())
15    }
16
17    /// Merge this table attributes with another set of table attributes.
18    pub(crate) fn merge(mut self, other: Self) -> Self {
19        self.0.extend(other.0);
20        self
21    }
22
23    // TODO: Test, docs
24    // TODO: Remove this logic from the NormalisedKey
25    pub(crate) fn insert(
26        &mut self,
27        name: impl Into<AttributeName>,
28        value: impl Into<TableAttribute>,
29    ) {
30        let name: AttributeName = name.into();
31        let attr: TableAttribute = value.into();
32        self.0.insert(name, attr);
33    }
34
35    /// Attempts to insert a value into a map with key `subkey` where the map is stored at `key`.
36    /// If the map doesn't exist, it will be created.
37    /// If an attribute with the same key already exists but is not a map, an error is returned.
38    pub(crate) fn try_insert_map(
39        &mut self,
40        name: impl Into<AttributeName>,
41        subkey: impl Into<String>,
42        value: impl Into<TableAttribute>,
43    ) -> Result<(), SealError> {
44        self.0
45            .entry(name.into())
46            .or_insert(TableAttribute::new_map())
47            .try_insert_map(subkey.into(), value.into())
48    }
49
50    pub(crate) fn remove(&mut self, name: impl Into<AttributeName>) -> Option<TableAttribute> {
51        self.0.remove(&name.into())
52    }
53
54    // TODO: Add unit tests for this
55    /// Partition the attributes into protected and unprotected attributes
56    /// given the list of protected keys.
57    pub(crate) fn partition(self, protected_keys: &[Cow<'_, str>]) -> (Self, Self) {
58        let (protected, unprotected): (HashMap<_, _>, HashMap<_, _>) =
59            self.0.into_iter().partition(|(k, _)| {
60                let check = k.as_external_name();
61                protected_keys.iter().any(|key| check == key)
62            });
63
64        (protected.into(), unprotected.into())
65    }
66
67    // TODO: Doc, test
68    pub(crate) fn get(&self, name: impl Into<AttributeName>) -> Option<&TableAttribute> {
69        let name: AttributeName = name.into();
70        self.0.get(&name)
71    }
72}
73
74impl From<HashMap<AttributeName, TableAttribute>> for TableAttributes {
75    fn from(map: HashMap<AttributeName, TableAttribute>) -> Self {
76        Self(map)
77    }
78}
79
80impl IntoIterator for TableAttributes {
81    type Item = (AttributeName, TableAttribute);
82    type IntoIter = IntoIter<AttributeName, TableAttribute>;
83
84    /// Iterates over the table attributes, returning each pair of [AttributeName] and [TableAttribute].
85    fn into_iter(self) -> Self::IntoIter {
86        self.0.into_iter()
87    }
88}
89
90impl Default for TableAttributes {
91    fn default() -> Self {
92        Self::new()
93    }
94}