Skip to main content

dynamodb_expression/update/
mod.rs

1//! Types related to [DynamoDB update expressions][1]. For more, see [`Update`].
2//!
3//! [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html
4
5mod add;
6mod delete;
7mod remove;
8mod set;
9
10use core::fmt;
11
12pub use self::{
13    add::{Add, AddAction, AddValue},
14    delete::{Delete, DeleteAction},
15    remove::Remove,
16    set::{
17        if_not_exists, list_append, math, Assign, IfNotExists, ListAppend, Math, Set, SetAction,
18    },
19};
20
21/// Represents a [DynamoDB update expression][1].
22///
23/// See also: [`Expression`], [`Set`], [`Remove`], [`Add`], [`Delete`]
24///
25/// # Examples
26///
27/// ```
28/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
29/// use dynamodb_expression::{update::Update, value::StringSet, Expression, Path};
30/// # use pretty_assertions::assert_eq;
31///
32/// let update = Update::from("foo".parse::<Path>()?.math().add(7));
33/// assert_eq!("SET foo = foo + 7", update.to_string());
34///
35/// let update = Update::from("foo".parse::<Path>()?.if_not_exists().set("a value"));
36/// assert_eq!(
37///     r#"SET foo = if_not_exists(foo, "a value")"#,
38///     update.to_string()
39/// );
40///
41/// let update = Update::from("foo".parse::<Path>()?.remove());
42/// assert_eq!(r#"REMOVE foo"#, update.to_string());
43///
44/// let update = Update::from(
45///     "foo"
46///         .parse::<Path>()?
47///         .add(StringSet::from(["a value", "another value"])),
48/// );
49/// assert_eq!(
50///     r#"ADD foo ["a value", "another value"]"#,
51///     update.to_string()
52/// );
53///
54/// let update = Update::from("foo".parse::<Path>()?.delete(StringSet::from(["a value"])));
55/// assert_eq!(r#"DELETE foo ["a value"]"#, update.to_string());
56///
57/// // To use an `Update`, build an `Expression`.
58/// let expression = Expression::builder().with_update(update).build();
59/// # _ = expression;
60/// #
61/// # Ok(())
62/// # }
63/// ```
64///
65/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html
66/// [`Expression`]: crate::Expression
67#[must_use = "Use in a DynamoDB expression with `Expression::builder().with_update(update)`"]
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct Update {
70    pub(crate) set: Option<Set>,
71    pub(crate) remove: Option<Remove>,
72    pub(crate) add: Option<Add>,
73    pub(crate) delete: Option<Delete>,
74}
75
76impl Update {
77    /// A new update expression for a [`Set`] statement.
78    pub fn new_set<T>(set: T) -> Self
79    where
80        T: Into<Set>,
81    {
82        set.into().into()
83    }
84
85    /// A new update expression for a [`Remove`] statement.
86    pub fn new_remove<T>(remove: T) -> Self
87    where
88        T: Into<Remove>,
89    {
90        remove.into().into()
91    }
92
93    /// A new update expression for an [`Add`] statement.
94    pub fn new_add<T>(add: T) -> Self
95    where
96        T: Into<Add>,
97    {
98        add.into().into()
99    }
100
101    /// A new update expression for a [`Delete`] statement.
102    pub fn new_delete<T>(delete: T) -> Self
103    where
104        T: Into<Delete>,
105    {
106        delete.into().into()
107    }
108
109    /// Combine this [`Update`] statement with another.
110    pub fn and<T>(mut self, other: T) -> Self
111    where
112        T: Into<Update>,
113    {
114        let other = other.into();
115
116        if let Some(mut other) = other.set {
117            if let Some(current) = &mut self.set {
118                current.actions.append(&mut other.actions);
119            } else {
120                self.set = Some(other);
121            }
122        }
123
124        if let Some(mut other) = other.remove {
125            if let Some(current) = &mut self.remove {
126                current.paths.append(&mut other.paths);
127            } else {
128                self.remove = Some(other);
129            }
130        }
131
132        if let Some(mut other) = other.add {
133            if let Some(current) = &mut self.add {
134                current.actions.append(&mut other.actions);
135            } else {
136                self.add = Some(other);
137            }
138        }
139
140        if let Some(mut other) = other.delete {
141            if let Some(current) = &mut self.delete {
142                current.actions.append(&mut other.actions);
143            } else {
144                self.delete = Some(other);
145            }
146        }
147
148        self
149    }
150}
151
152impl fmt::Display for Update {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        let mut first = true;
155        if let Some(set) = &self.set {
156            first = false;
157            set.fmt(f)?;
158        }
159
160        if let Some(remove) = &self.remove {
161            if first {
162                first = false;
163            } else {
164                f.write_str(" ")?;
165            }
166
167            remove.fmt(f)?;
168        }
169
170        if let Some(add) = &self.add {
171            if first {
172                first = false;
173            } else {
174                f.write_str(" ")?;
175            }
176
177            add.fmt(f)?;
178        }
179
180        if let Some(delete) = &self.delete {
181            if !first {
182                f.write_str(" ")?;
183            }
184
185            delete.fmt(f)?;
186        }
187
188        Ok(())
189    }
190}
191
192impl From<Set> for Update {
193    fn from(value: Set) -> Self {
194        Self {
195            set: Some(value),
196            remove: None,
197            add: None,
198            delete: None,
199        }
200    }
201}
202
203impl From<SetAction> for Update {
204    fn from(value: SetAction) -> Self {
205        Self {
206            set: Some(value.into()),
207            remove: None,
208            add: None,
209            delete: None,
210        }
211    }
212}
213
214impl From<Assign> for Update {
215    fn from(value: Assign) -> Self {
216        Self {
217            set: Some(value.into()),
218            remove: None,
219            add: None,
220            delete: None,
221        }
222    }
223}
224
225impl From<Math> for Update {
226    fn from(value: Math) -> Self {
227        Self {
228            set: Some(value.into()),
229            remove: None,
230            add: None,
231            delete: None,
232        }
233    }
234}
235
236impl From<ListAppend> for Update {
237    fn from(value: ListAppend) -> Self {
238        Self {
239            set: Some(value.into()),
240            remove: None,
241            add: None,
242            delete: None,
243        }
244    }
245}
246
247impl From<IfNotExists> for Update {
248    fn from(value: IfNotExists) -> Self {
249        Self {
250            set: Some(value.into()),
251            remove: None,
252            add: None,
253            delete: None,
254        }
255    }
256}
257
258impl From<Remove> for Update {
259    fn from(value: Remove) -> Self {
260        Self {
261            set: None,
262            remove: Some(value),
263            add: None,
264            delete: None,
265        }
266    }
267}
268
269impl From<Add> for Update {
270    fn from(value: Add) -> Self {
271        Self {
272            set: None,
273            remove: None,
274            add: Some(value),
275            delete: None,
276        }
277    }
278}
279
280impl From<AddAction> for Update {
281    fn from(value: AddAction) -> Self {
282        Self {
283            set: None,
284            remove: None,
285            add: Some(value.into()),
286            delete: None,
287        }
288    }
289}
290
291impl From<Delete> for Update {
292    fn from(value: Delete) -> Self {
293        Self {
294            set: None,
295            remove: None,
296            add: None,
297            delete: Some(value),
298        }
299    }
300}
301
302impl From<DeleteAction> for Update {
303    fn from(value: DeleteAction) -> Self {
304        Self {
305            set: None,
306            remove: None,
307            add: None,
308            delete: Some(value.into()),
309        }
310    }
311}
312
313#[cfg(test)]
314mod examples {
315    #[test]
316    fn example() -> Result<(), Box<dyn std::error::Error>> {
317        use crate::{update::Update, value::StringSet, Expression, Path};
318        use pretty_assertions::assert_eq;
319
320        let update = Update::from("foo".parse::<Path>()?.math().add(7));
321        assert_eq!("SET foo = foo + 7", update.to_string());
322
323        let update = Update::from("foo".parse::<Path>()?.if_not_exists().set("a value"));
324        assert_eq!(
325            r#"SET foo = if_not_exists(foo, "a value")"#,
326            update.to_string()
327        );
328
329        let update = Update::from("foo".parse::<Path>()?.remove());
330        assert_eq!(r#"REMOVE foo"#, update.to_string());
331
332        let update = Update::from(
333            "foo"
334                .parse::<Path>()?
335                .add(StringSet::from(["a value", "another value"])),
336        );
337        assert_eq!(
338            r#"ADD foo ["a value", "another value"]"#,
339            update.to_string()
340        );
341
342        let update = Update::from("foo".parse::<Path>()?.delete(StringSet::from(["a value"])));
343        assert_eq!(r#"DELETE foo ["a value"]"#, update.to_string());
344
345        // To use an `Update`, build an `Expression`.
346        let expression = Expression::builder().with_update(update).build();
347        _ = expression;
348
349        Ok(())
350    }
351}