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}