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}