dynamodb_expression/value/
mod.rs1mod list;
7mod map;
8mod num;
9mod scalar;
10mod set;
11mod value_or_ref;
12
13pub use list::List;
14pub use map::Map;
15pub use num::Num;
16pub use scalar::Scalar;
17pub use set::{BinarySet, NumSet, Set, StringSet};
18pub use value_or_ref::{Ref, StringOrRef};
19
20pub(crate) use value_or_ref::ValueOrRef;
21
22use core::fmt::{self, LowerExp, UpperExp};
23use std::error::Error;
24
25use aws_sdk_dynamodb::{primitives::Blob, types::AttributeValue};
26use base64::{engine::general_purpose, Engine as _};
27use itertools::Itertools;
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
31pub enum Value {
32 Scalar(Scalar),
33 Set(Set),
34 Map(Map),
35 List(List),
36}
37
38impl Value {
39 pub fn new_string<T>(value: T) -> Self
45 where
46 T: Into<String>,
47 {
48 value.into().into()
49 }
50
51 pub fn new_num<N>(value: N) -> Self
58 where
59 N: ToString + ::num::Num,
60 {
61 Num::new(value).into()
62 }
63
64 pub fn new_num_lower_exp<N>(value: N) -> Self
72 where
73 N: LowerExp + ::num::Num,
74 {
75 Num::new_lower_exp(value).into()
76 }
77
78 pub fn new_num_upper_exp<N>(value: N) -> Self
86 where
87 N: UpperExp + ::num::Num,
88 {
89 Num::new_upper_exp(value).into()
90 }
91
92 pub fn new_bool(b: bool) -> Self {
98 b.into()
99 }
100
101 pub fn new_binary<B>(binary: B) -> Self
107 where
108 B: Into<Vec<u8>>,
109 {
110 binary.into().into()
111 }
112
113 pub fn new_null() -> Self {
119 Scalar::new_null().into()
120 }
121
122 pub fn new_string_set<T>(string_set: T) -> Self
128 where
129 T: Into<StringSet>,
130 {
131 string_set.into().into()
132 }
133
134 pub fn new_num_set<T>(num_set: T) -> Self
140 where
141 T: Into<NumSet>,
142 {
143 num_set.into().into()
144 }
145
146 pub fn new_binary_set<T>(binary_set: T) -> Self
152 where
153 T: Into<BinarySet>,
154 {
155 binary_set.into().into()
156 }
157
158 pub fn new_map<T>(map: T) -> Self
164 where
165 T: Into<Map>,
166 {
167 map.into().into()
168 }
169
170 pub fn new_list<T>(list: T) -> Self
176 where
177 T: Into<List>,
178 {
179 list.into().into()
180 }
181
182 pub(crate) fn into_attribute_value(self) -> AttributeValue {
187 match self {
188 Self::Scalar(value) => value.into_attribute_value(),
189 Self::Set(value) => value.into_attribute_value(),
190 Self::Map(value) => value.into_attribute_value(),
191 Self::List(value) => value.into_attribute_value(),
192 }
193 }
194}
195
196impl From<Scalar> for Value {
197 fn from(value: Scalar) -> Self {
198 Self::Scalar(value)
199 }
200}
201
202impl From<String> for Value {
203 fn from(value: String) -> Self {
204 Scalar::from(value).into()
205 }
206}
207
208impl From<&String> for Value {
209 fn from(value: &String) -> Self {
210 Scalar::from(value).into()
211 }
212}
213
214impl From<&str> for Value {
215 fn from(value: &str) -> Self {
216 Scalar::from(value).into()
217 }
218}
219
220impl From<&&str> for Value {
221 fn from(value: &&str) -> Self {
222 Scalar::from(value).into()
223 }
224}
225
226impl From<Num> for Value {
227 fn from(value: Num) -> Self {
228 Scalar::from(value).into()
229 }
230}
231
232impl From<bool> for Value {
233 fn from(value: bool) -> Self {
234 Scalar::from(value).into()
235 }
236}
237
238impl From<Vec<u8>> for Value {
239 fn from(value: Vec<u8>) -> Self {
240 Scalar::from(value).into()
241 }
242}
243
244impl<const N: usize> From<[u8; N]> for Value {
245 fn from(value: [u8; N]) -> Self {
246 Scalar::from(value).into()
247 }
248}
249
250impl From<()> for Value {
251 fn from(value: ()) -> Self {
252 Scalar::from(value).into()
253 }
254}
255
256impl FromIterator<u8> for Value {
257 fn from_iter<T>(iter: T) -> Self
258 where
259 T: IntoIterator<Item = u8>,
260 {
261 Scalar::from_iter(iter).into()
262 }
263}
264
265impl From<Set> for Value {
266 fn from(set: Set) -> Self {
267 Self::Set(set)
268 }
269}
270
271impl From<StringSet> for Value {
272 fn from(string_set: StringSet) -> Self {
273 Self::Set(string_set.into())
274 }
275}
276
277impl From<NumSet> for Value {
278 fn from(num_set: NumSet) -> Self {
279 Self::Set(num_set.into())
280 }
281}
282
283impl From<BinarySet> for Value {
284 fn from(string_set: BinarySet) -> Self {
285 Self::Set(string_set.into())
286 }
287}
288
289impl From<Map> for Value {
290 fn from(map: Map) -> Self {
291 Self::Map(map)
292 }
293}
294
295impl From<List> for Value {
296 fn from(list: List) -> Self {
297 Self::List(list)
298 }
299}
300
301impl TryFrom<AttributeValue> for Value {
302 type Error = UnknownAttributeValueError;
303
304 fn try_from(value: AttributeValue) -> Result<Self, Self::Error> {
309 Ok(match value {
310 AttributeValue::B(b) => Scalar::Binary(b.into_inner()).into(),
311 AttributeValue::Bool(b) => Scalar::Bool(b).into(),
312 AttributeValue::Bs(bs) => {
313 BinarySet::from_iter(bs.into_iter().map(Blob::into_inner)).into()
314 }
315 AttributeValue::L(l) => List::from(
316 l.into_iter()
317 .map(Self::try_from)
318 .try_collect::<_, Vec<_>, _>()?,
319 )
320 .into(),
321 AttributeValue::M(m) => Map::from(
322 m.into_iter()
323 .map(|(k, v)| Self::try_from(v).map(|v| (k, v)))
324 .try_collect::<_, Vec<_>, _>()?,
325 )
326 .into(),
327 AttributeValue::N(n) => Num { n }.into(),
328 AttributeValue::Ns(ns) => NumSet::from_iter(ns.into_iter().map(|n| Num { n })).into(),
329 AttributeValue::Null(_null) => Scalar::Null.into(),
330 AttributeValue::S(s) => Scalar::String(s).into(),
331 AttributeValue::Ss(ss) => StringSet::from(ss).into(),
332 _ => return Err(UnknownAttributeValueError(value)),
333 })
334 }
335}
336
337impl fmt::Display for Value {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 match self {
340 Self::Scalar(value) => value.fmt(f),
341 Self::Set(value) => value.fmt(f),
342 Self::Map(value) => value.fmt(f),
343 Self::List(value) => value.fmt(f),
344 }
345 }
346}
347
348#[derive(Debug)]
357pub struct UnknownAttributeValueError(pub AttributeValue);
358
359impl fmt::Display for UnknownAttributeValueError {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 write!(f, "unknown AttributeValue variant: {:?}", self.0)
362 }
363}
364
365impl Error for UnknownAttributeValueError {}
366
367pub(crate) fn base64<T>(b: T) -> String
369where
370 T: AsRef<[u8]>,
371{
372 general_purpose::STANDARD.encode(b)
373}
374
375#[cfg(test)]
376mod test {
377 use aws_sdk_dynamodb::{primitives::Blob, types::AttributeValue};
378 use pretty_assertions::assert_eq;
379
380 use crate::value::{BinarySet, List, Map, Num, NumSet, StringSet};
381
382 use super::Value;
383
384 #[test]
385 fn display() {
386 assert_eq!(r#""a""#, Value::new_string("a").to_string());
387 assert_eq!(r#"1000"#, Value::new_num(1000).to_string());
388 assert_eq!(r#"1e3"#, Value::new_num_lower_exp(1000).to_string());
389 assert_eq!(r#"1E3"#, Value::new_num_upper_exp(1000).to_string());
390 assert_eq!(r#""YQ==""#, Value::new_binary("a").to_string());
391 assert_eq!("true", Value::new_bool(true).to_string());
392 assert_eq!("NULL", Value::new_null().to_string());
393
394 assert_eq!(
396 r#"["a", "b", "c"]"#,
397 Value::new_string_set(["a", "c", "b"]).to_string()
398 );
399 assert_eq!(
400 r#"[-7, 1e3, 42]"#,
401 Value::new_num_set([Num::new_lower_exp(1000), Num::new(42), Num::new(-7)]).to_string()
402 );
403 assert_eq!(
404 r#"["YQ==", "Yg==", "Yw=="]"#,
405 Value::new_binary_set([b"a", b"b", b"c"]).to_string()
406 );
407
408 assert_eq!(
409 r#"[NULL, 8, "a string"]"#,
410 Value::new_list([
411 Value::new_null(),
412 Value::new_num(8),
413 Value::new_string("a string")
414 ])
415 .to_string()
416 );
417
418 assert_eq!(
419 r#"{n: 8, null: NULL, s: "a string"}"#,
420 Value::new_map([
421 (String::from("s"), Value::new_string("a string")),
422 (String::from("n"), Value::new_num(8)),
423 (String::from("null"), Value::new_null()),
424 ])
425 .to_string()
426 );
427 }
428
429 #[test]
430 fn from_attribute_value() {
431 assert_eq!(
432 Value::from(Map::from([
433 ("s", Value::from("a string")),
434 ("int", Value::from(Num::from(8))),
435 ("b", Value::from(b"foo".to_vec())),
436 ("null", Value::from(())),
437 ("yes", Value::from(true)),
438 ("no", Value::from(false)),
439 (
440 "list",
441 List::from([
442 Value::from("foo"),
443 Value::from(Num::from(42)),
444 Value::from(()),
445 ])
446 .into(),
447 ),
448 ("ss", StringSet::from(["foo"]).into()),
449 ("ns", NumSet::from([Num::from(42)]).into()),
450 (
451 "bs",
452 BinarySet::from([b"foo".to_vec(), b"bar".to_vec(), b"baz".to_vec()]).into()
453 ),
454 ])),
455 Value::try_from(AttributeValue::M(
456 [
457 ("s".to_string(), AttributeValue::S("a string".to_string())),
458 ("int".to_string(), AttributeValue::N("8".to_string())),
459 ("b".to_string(), AttributeValue::B(Blob::new(b"foo"))),
460 ("null".to_string(), AttributeValue::Null(true)),
461 ("yes".to_string(), AttributeValue::Bool(true)),
462 ("no".to_string(), AttributeValue::Bool(false)),
463 (
464 "list".to_string(),
465 AttributeValue::L(vec![
466 AttributeValue::S("foo".to_string()),
467 AttributeValue::N("42".to_string()),
468 AttributeValue::Null(true),
469 ]),
470 ),
471 (
472 "ss".to_string(),
473 AttributeValue::Ss(vec!["foo".to_string()])
474 ),
475 ("ns".to_string(), AttributeValue::Ns(vec!["42".to_string()])),
476 (
477 "bs".to_string(),
478 AttributeValue::Bs(vec![
479 Blob::new(b"foo"),
480 Blob::new(b"bar"),
481 Blob::new(b"baz"),
482 ])
483 ),
484 ]
485 .into_iter()
486 .collect(),
487 ))
488 .expect("Could not convert AttributeValue to Value"),
489 );
490 }
491}