dynamodb_expression/update/set/assign.rs
1use core::fmt;
2
3use crate::{
4 path::Path,
5 update::Update,
6 value::{Value, ValueOrRef},
7};
8
9/// Represents assigning a value of an [attribute][1], [list][2], or [map][3]
10/// for a DynamoDB update expression.
11///
12/// Prefer [`Path::set`] over this.
13///
14/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.ModifyingAttributes
15/// [2]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.AddingListElements
16/// [3]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.AddingNestedMapAttributes
17#[derive(Debug, Clone, PartialEq, Eq)]
18#[must_use] // TODO: More detail
19pub struct Assign {
20 pub(crate) path: Path,
21 pub(crate) value: ValueOrRef,
22}
23
24impl Assign {
25 /// Allows for manual creation of an [`Assign`] statement.
26 ///
27 /// Prefer [`Path::set`] over this.
28 pub fn new<P, V>(path: P, value: V) -> Self
29 where
30 P: Into<Path>,
31 V: Into<Value>,
32 {
33 Self {
34 path: path.into(),
35 value: value.into().into(),
36 }
37 }
38
39 /// Add an additional [`Update`] to this expression.
40 ///
41 /// ```
42 /// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
43 /// use dynamodb_expression::{Num, Path};
44 /// # use pretty_assertions::assert_eq;
45 ///
46 /// let set = "foo"
47 /// .parse::<Path>()?
48 /// .set(Num::new(7))
49 /// .and("bar".parse::<Path>()?.set("a value"))
50 /// .and("baz".parse::<Path>()?.remove());
51 /// assert_eq!(r#"SET foo = 7, bar = "a value" REMOVE baz"#, set.to_string());
52 /// #
53 /// # Ok(())
54 /// # }
55 /// ```
56 pub fn and<T>(self, other: T) -> Update
57 where
58 T: Into<Update>,
59 {
60 Update::from(self).and(other)
61 }
62}
63
64impl fmt::Display for Assign {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 self.path.fmt(f)?;
67 f.write_str(" = ")?;
68 self.value.fmt(f)
69 }
70}
71
72#[cfg(test)]
73mod test {
74 use pretty_assertions::assert_eq;
75
76 use crate::{
77 update::{IfNotExists, Set, SetAction},
78 Num, Path,
79 };
80
81 use super::Assign;
82
83 #[test]
84 fn and() -> Result<(), Box<dyn std::error::Error>> {
85 let assign: Assign = "foo".parse::<Path>()?.set("a value");
86 let if_not_exists: IfNotExists = "bar".parse::<Path>()?.if_not_exists().set(Num::new(8));
87
88 // Should be able to concatenate anything that can be turned into a SetAction.
89
90 let combined = assign.clone().and(if_not_exists.clone());
91 assert_eq!(
92 r#"SET foo = "a value", bar = if_not_exists(bar, 8)"#,
93 combined.to_string()
94 );
95
96 // Should be able to concatenate a SetAction instance.
97
98 let combined = assign.clone().and(SetAction::from(if_not_exists.clone()));
99 assert_eq!(
100 r#"SET foo = "a value", bar = if_not_exists(bar, 8)"#,
101 combined.to_string()
102 );
103
104 // Should be able to concatenate a Set instance
105
106 let set: Set = [
107 SetAction::from(if_not_exists),
108 SetAction::from("baz".parse::<Path>()?.math().add(1)),
109 ]
110 .into_iter()
111 .collect();
112 let combined = assign.clone().and(set);
113 assert_eq!(
114 r#"SET foo = "a value", bar = if_not_exists(bar, 8), baz = baz + 1"#,
115 combined.to_string()
116 );
117
118 // Should be able to concatenate a Remove instance
119
120 let combined = assign.clone().and("quux".parse::<Path>()?.remove());
121 assert_eq!(r#"SET foo = "a value" REMOVE quux"#, combined.to_string());
122
123 // Should be able to concatenate a SetRemove instance
124
125 let combined = assign.and("quux".parse::<Path>()?.remove());
126 assert_eq!(r#"SET foo = "a value" REMOVE quux"#, combined.to_string());
127
128 Ok(())
129 }
130}