Skip to main content

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