dynamodb_expression/value/set/
mod.rs1mod binary_set;
2mod num_set;
3mod string_set;
4
5pub use binary_set::BinarySet;
6pub use num_set::NumSet;
7pub use string_set::StringSet;
8
9use core::fmt;
10
11use aws_sdk_dynamodb::types::AttributeValue;
12
13use super::base64;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub enum Set {
20 StringSet(StringSet),
21 NumSet(NumSet),
22 BinarySet(BinarySet),
23}
24
25impl Set {
26 pub fn new_string_set<T>(string_set: T) -> Self
33 where
34 T: Into<StringSet>,
35 {
36 string_set.into().into()
37 }
38
39 pub fn new_num_set<T>(num_set: T) -> Self
46 where
47 T: Into<NumSet>,
48 {
49 num_set.into().into()
50 }
51
52 pub fn new_binary_set<T>(binary_set: T) -> Self
59 where
60 T: Into<BinarySet>,
61 {
62 binary_set.into().into()
63 }
64
65 pub(super) fn into_attribute_value(self) -> AttributeValue {
70 match self {
71 Set::StringSet(set) => set.into_attribute_value(),
72 Set::NumSet(set) => set.into_attribute_value(),
73 Set::BinarySet(set) => set.into_attribute_value(),
74 }
75 }
76}
77
78impl fmt::Display for Set {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Set::StringSet(set) => set.fmt(f),
82 Set::NumSet(set) => set.fmt(f),
83 Set::BinarySet(set) => set.fmt(f),
84 }
85 }
86}
87
88impl From<StringSet> for Set {
89 fn from(string_set: StringSet) -> Self {
90 Self::StringSet(string_set)
91 }
92}
93
94impl From<NumSet> for Set {
95 fn from(num_set: NumSet) -> Self {
96 Self::NumSet(num_set)
97 }
98}
99
100impl From<BinarySet> for Set {
101 fn from(binary_set: BinarySet) -> Self {
102 Self::BinarySet(binary_set)
103 }
104}
105
106#[cfg(test)]
107mod test {
108 use std::{cell::RefCell, iter::FusedIterator};
109
110 use itertools::Itertools;
111 use pretty_assertions::assert_eq;
112
113 use crate::{
114 value::{base64, Set},
115 Num,
116 };
117
118 #[test]
119 fn string_set_display() {
120 let set = Set::new_string_set(["foo", "bar", "!@#$%^&*()-=_+\"'{}[]\\|;:<>,./?`~"]);
121 assert_eq!(
122 r#"["!@#$%^&*()-=_+\"'{}[]\\|;:<>,./?`~", "bar", "foo"]"#,
123 set.to_string()
124 );
125
126 let deserialized: Vec<String> =
127 serde_json::from_str(&set.to_string()).expect("Must be valid JSON");
128 assert_eq!(
129 vec!["!@#$%^&*()-=_+\"'{}[]\\|;:<>,./?`~", "bar", "foo"],
130 deserialized
131 );
132 }
133
134 #[test]
135 fn num_set_display() {
136 let set = Set::new_num_set([-1, 0, 1, 42]);
137 assert_eq!("[-1, 0, 1, 42]", set.to_string());
138
139 let deserialized: Vec<i32> =
140 serde_json::from_str(&set.to_string()).expect("Must be valid JSON");
141 assert_eq!(vec![-1, 0, 1, 42], deserialized);
142
143 let set = Set::new_num_set([
144 Num::new_lower_exp(f32::MIN),
145 Num::new(0.0),
146 Num::new(1000),
147 Num::new_upper_exp(f32::MAX),
148 Num::new(9.2),
149 ]);
150 assert_eq!(
151 "[\
152 -3.4028235e38, \
153 0, \
154 1000, \
155 3.4028235E38, \
156 9.2\
157 ]",
158 set.to_string()
159 );
160
161 let deserialized: Vec<f32> =
162 serde_json::from_str(&set.to_string()).expect("Must be valid JSON");
163 assert_eq!(vec![f32::MIN, 0.0, 1000.0, f32::MAX, 9.2], deserialized);
164 }
165
166 #[test]
167 fn binary_set_display() {
168 let set = Set::new_binary_set([" > ", " ? "]);
172 assert_eq!(r#"["ICA+IA==", "ICA/IA=="]"#, set.to_string());
173
174 let deserialized: Vec<String> =
175 serde_json::from_str(&set.to_string()).expect("Must be valid JSON");
176 assert_eq!(vec!["ICA+IA==", "ICA/IA=="], deserialized);
177 }
178
179 #[test]
180 #[ignore = "Just used to find more base64 for JSON encoding testing"]
181 fn find_tricky_base64() {
182 fn charset(
184 ) -> impl ExactSizeIterator<Item = char> + DoubleEndedIterator + FusedIterator + Clone
185 {
186 (32..127).map(char::from_u32).map(Option::unwrap)
187 }
188
189 let specials = RefCell::new(['+', '/'].into_iter().peekable());
192 [charset(), charset(), charset(), charset()]
193 .into_iter()
194 .multi_cartesian_product()
195 .take_while(|_| specials.borrow_mut().peek().is_some())
196 .map(String::from_iter)
197 .enumerate() .map(|(i, raw)| {
199 let encoded = base64(&raw);
200 (i, raw, encoded)
201 })
202 .filter(|(_i, _raw, encoded)| {
203 if encoded.contains(specials.borrow_mut().peek().cloned().unwrap()) {
204 specials.borrow_mut().next();
205 true
206 } else {
207 false
208 }
209 })
210 .for_each(|(index, raw, encoded)| {
211 println!(
212 "The encoded version of iteration {index}, {raw:?}, \
213 includes special characters: {encoded}"
214 )
215 });
216 }
217}