dynamodb_expression/value/
map.rs

1use core::{
2    fmt::{self, Write},
3    hash,
4};
5
6use aws_sdk_dynamodb::types::AttributeValue;
7use itermap::IterMap;
8
9use crate::path::Name;
10
11use super::Value;
12
13type MapType<K, V> = std::collections::BTreeMap<K, V>;
14// TODO: Allow this to be configured via feature to switch between HashMap and BTreeMap
15//       Using BTreeMap for stable testing.
16// type MapType<K, V> = std::collections::HashMap<K, V>;
17
18/// Represents a [DynamoDB map][1].
19///
20/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.Document.Map
21#[derive(Clone, Default, PartialEq, Eq)]
22pub struct Map {
23    map: MapType<Name, Value>,
24}
25
26impl Map {
27    pub fn new<T>(map: T) -> Self
28    where
29        T: Into<Map>,
30    {
31        map.into()
32    }
33
34    // Intentionally not using `impl From<ScalarValue> for AttributeValue` because
35    // I don't want to make this a public API people rely on. The purpose of this
36    // crate is not to make creating `AttributeValues` easier. They should try
37    // `serde_dynamo`.
38    pub(super) fn into_attribute_value(self) -> AttributeValue {
39        AttributeValue::M(
40            self.map
41                .into_iter()
42                .map_keys(|name| name.name)
43                .map_values(Value::into_attribute_value)
44                .collect(),
45        )
46    }
47}
48
49impl hash::Hash for Map {
50    fn hash<H>(&self, state: &mut H)
51    where
52        H: hash::Hasher,
53    {
54        self.map.iter().for_each(|(k, v)| {
55            k.hash(state);
56            v.hash(state);
57        })
58    }
59}
60
61impl fmt::Debug for Map {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_map().entries(self.map.iter()).finish()
64    }
65}
66
67impl fmt::Display for Map {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        f.write_char('{')?;
70
71        let mut first = true;
72        self.map.iter().try_for_each(|(k, v)| {
73            if first {
74                first = false;
75            } else {
76                f.write_str(", ")?;
77            }
78
79            k.fmt(f)?;
80            f.write_str(": ")?;
81            v.fmt(f)
82        })?;
83
84        f.write_char('}')
85    }
86}
87
88impl<K, V> FromIterator<(K, V)> for Map
89where
90    K: Into<Name>,
91    V: Into<Value>,
92{
93    fn from_iter<I>(iter: I) -> Self
94    where
95        I: IntoIterator<Item = (K, V)>,
96    {
97        Self {
98            map: iter
99                .into_iter()
100                .map_keys(Into::into)
101                .map_values(Into::into)
102                .collect(),
103        }
104    }
105}
106
107impl<I, K, V> From<I> for Map
108where
109    I: IntoIterator<Item = (K, V)>,
110    K: Into<Name>,
111    V: Into<Value>,
112{
113    fn from(iter: I) -> Self {
114        Self::from_iter(iter)
115    }
116}