dynamodb_expression/value/set/
binary_set.rs

1use core::fmt;
2use std::collections::BTreeSet;
3
4use aws_sdk_dynamodb::{primitives::Blob, types::AttributeValue};
5
6use super::base64;
7
8/// Represents a [DynamoDB binary set][1].
9///
10/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.SetTypes
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct BinarySet(BTreeSet<Vec<u8>>);
13
14impl BinarySet {
15    /// Creates a value to use as a [DynamoDB binary set][1].
16    ///
17    /// It can be created from `IntoIterator<T>` where `T` is `Into<Vec<u8>>`.
18    /// Some examples:
19    /// ```
20    /// use dynamodb_expression::value::BinarySet;
21    /// # use pretty_assertions::assert_eq;
22    ///
23    /// // impl IntoIterator<Item = &str>
24    /// assert_eq!(
25    ///     r#"["YQ==", "Yg==", "Yw=="]"#,
26    ///     BinarySet::new(["a", "b", "c"]).to_string()
27    /// );
28    ///
29    /// // impl IntoIterator<Item = String>
30    /// assert_eq!(
31    ///     r#"["YQ==", "Yg==", "Yw=="]"#,
32    ///     BinarySet::new([String::from("a"), String::from("b"), String::from("c")]).to_string()
33    /// );
34    ///
35    /// // impl IntoIterator<Item = &[u8]>
36    /// assert_eq!(
37    ///     r#"["YQ==", "Yg==", "Yw=="]"#,
38    ///     BinarySet::new([b"a", b"b", b"c"]).to_string()
39    /// );
40    ///
41    /// // impl IntoIterator<Item = Vec<u8>>
42    /// assert_eq!(
43    ///     r#"["YQ==", "Yg==", "Yw=="]"#,
44    ///     BinarySet::new([b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]).to_string()
45    /// );
46    ///
47    /// // impl IntoIterator<Item = impl Iterator<Item = u8>>
48    /// assert_eq!(
49    ///     r#"["YQ==", "Yg==", "Yw=="]"#,
50    ///     BinarySet::new(
51    ///         [[b'a'], [b'b'], [b'c']]
52    ///             // In this case you need to turn the `Iterator<Item = u8>` items into a `Vec<u8>`.
53    ///             // If, one day, `impl<T> From<impl IntoIterator<Item = T>> for Vec<T>` exists, this won't be needed.
54    ///             .map(Vec::from_iter)
55    ///     )
56    ///     .to_string()
57    /// );
58    /// ```
59    ///
60    /// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.SetTypes
61    pub fn new<T>(set: T) -> Self
62    where
63        T: Into<BinarySet>,
64    {
65        set.into()
66    }
67
68    // Intentionally not using `impl From<BinarySet> for AttributeValue` because
69    // I don't want to make this a public API people rely on. The purpose of this
70    // crate is not to make creating `AttributeValues` easier. They should try
71    // `serde_dynamo`.
72    pub(super) fn into_attribute_value(self) -> AttributeValue {
73        AttributeValue::Bs(self.0.into_iter().map(Blob::new).collect())
74    }
75}
76
77impl<T> FromIterator<T> for BinarySet
78where
79    T: Into<Vec<u8>>,
80{
81    fn from_iter<I>(iter: I) -> Self
82    where
83        I: IntoIterator<Item = T>,
84    {
85        Self(iter.into_iter().map(Into::into).collect())
86    }
87}
88
89impl<I, T> From<I> for BinarySet
90where
91    I: IntoIterator<Item = T>,
92    T: Into<Vec<u8>>,
93{
94    fn from(values: I) -> Self {
95        Self::from_iter(values)
96    }
97}
98
99impl fmt::Display for BinarySet {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.debug_list().entries(self.0.iter().map(base64)).finish()
102    }
103}
104
105#[cfg(test)]
106mod test {
107    use pretty_assertions::assert_eq;
108
109    use super::BinarySet;
110    use crate::Scalar;
111
112    #[test]
113    fn from_iter() {
114        // impl IntoIterator<Item = &str>
115        assert_eq!(
116            r#"["YQ==", "Yg==", "Yw=="]"#,
117            BinarySet::new(["a", "b", "c"]).to_string()
118        );
119
120        // impl IntoIterator<Item = String>
121        assert_eq!(
122            r#"["YQ==", "Yg==", "Yw=="]"#,
123            BinarySet::new([String::from("a"), String::from("b"), String::from("c")]).to_string()
124        );
125
126        // impl IntoIterator<Item = &[u8]>
127        assert_eq!(
128            r#"["YQ==", "Yg==", "Yw=="]"#,
129            BinarySet::new([b"a", b"b", b"c"]).to_string()
130        );
131
132        // impl IntoIterator<Item = Vec<u8>>
133        assert_eq!(
134            r#"["YQ==", "Yg==", "Yw=="]"#,
135            BinarySet::new([b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]).to_string()
136        );
137
138        // impl IntoIterator<Item = impl Iterator<Item = u8>>
139        assert_eq!(
140            r#"["YQ==", "Yg==", "Yw=="]"#,
141            BinarySet::new(
142                [[b'a'], [b'b'], [b'c']]
143                    // In this case you need to turn the `Iterator<Item = u8>` items into a `Vec<u8>`.
144                    // If, one day, `impl<T> From<impl IntoIterator<Item = T>> for Vec<T>` exists, this won't be needed.
145                    .map(Vec::from_iter)
146            )
147            .to_string()
148        );
149    }
150
151    #[test]
152    fn comparable_with_binary() {
153        // &str
154        assert_eq!(r#""YQ==""#, Scalar::new_binary("a").to_string());
155        assert_eq!(
156            r#"["YQ==", "Yg==", "Yw=="]"#,
157            BinarySet::new(["a", "b", "c"]).to_string()
158        );
159
160        // String
161        assert_eq!(
162            r#""YQ==""#,
163            Scalar::new_binary(String::from("a")).to_string()
164        );
165        assert_eq!(
166            r#"["YQ==", "Yg==", "Yw=="]"#,
167            BinarySet::new([String::from("a"), String::from("b"), String::from("c")]).to_string()
168        );
169
170        // &[u8]
171        assert_eq!(r#""YQ==""#, Scalar::new_binary(b"a").to_string());
172        assert_eq!(
173            r#"["YQ==", "Yg==", "Yw=="]"#,
174            BinarySet::new([b"a", b"b", b"c"]).to_string()
175        );
176
177        // Vec<u8>
178        assert_eq!(r#""YQ==""#, Scalar::new_binary(b"a".to_vec()).to_string());
179        assert_eq!(
180            r#"["YQ==", "Yg==", "Yw=="]"#,
181            BinarySet::new([b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]).to_string()
182        );
183
184        // impl Iterator<Item = u8>
185        assert_eq!(
186            r#""YQ==""#,
187            Scalar::new_binary(Vec::from_iter("a".bytes())).to_string()
188        );
189        assert_eq!(
190            r#"["YQ==", "Yg==", "Yw=="]"#,
191            BinarySet::new(["a".bytes(), "b".bytes(), "c".bytes()].map(Vec::from_iter)).to_string()
192        );
193    }
194}