dynamodb_expression/update/set/
mod.rs

1mod assign;
2pub mod if_not_exists;
3pub mod list_append;
4pub mod math;
5mod set_action;
6
7pub use self::assign::Assign;
8pub use self::if_not_exists::IfNotExists;
9pub use self::list_append::ListAppend;
10pub use self::math::Math;
11pub use self::set_action::SetAction;
12
13use core::fmt;
14
15use super::Update;
16
17/// Represents a [`SET` statement for an update expression][1]. Most of the time
18/// you won't use this directly.
19///
20/// See also: [`Update`], [`Path::set`], [`Path::if_not_exists`], [`Path::math`]
21/// [`Path::list_append`], [`Set::and`]
22///
23/// # Examples
24///
25/// ```
26/// use dynamodb_expression::{Path, update::Set};
27/// # use pretty_assertions::assert_eq;
28///
29/// let set = Set::from("foo".parse::<Path>().unwrap().math().add(1));
30/// assert_eq!("SET foo = foo + 1", set.to_string());
31///
32/// let set_foo = Set::from("foo".parse::<Path>().unwrap().math().add(1));
33/// assert_eq!("SET foo = foo + 1", set_foo.to_string());
34///
35/// let set_bar = Set::from("bar".parse::<Path>().unwrap().if_not_exists().set("a value"));
36/// assert_eq!(r#"SET bar = if_not_exists(bar, "a value")"#, set_bar.to_string());
37///
38/// let set = set_foo.and(set_bar);
39/// assert_eq!(
40///     r#"SET foo = foo + 1, bar = if_not_exists(bar, "a value")"#,
41///     set.to_string(),
42/// );
43///
44/// let set = Set::from("foo".parse::<Path>().unwrap().list_append().list(["a", "b", "c"]));
45/// assert_eq!(r#"SET foo = list_append(foo, ["a", "b", "c"])"#, set.to_string());
46/// ```
47///
48/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET
49/// [`Update`]: crate::update::Update
50/// [`Path::set`]: crate::path::Path::set
51/// [`Path::if_not_exists`]: crate::path::Path::if_not_exists
52/// [`Path::math`]: crate::path::Path::math
53/// [`Path::list_append`]: crate::path::Path::list_append
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct Set {
56    pub(crate) actions: Vec<SetAction>,
57}
58
59impl Set {
60    /// Add an additional [`Update`] statement to this expression.
61    ///
62    /// ```
63    /// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
64    /// use dynamodb_expression::{Num, Path, update::Set};
65    /// # use pretty_assertions::assert_eq;
66    ///
67    /// let set = "foo"
68    ///     .parse::<Path>()?
69    ///     .set(Num::new(7))
70    ///     .and("bar".parse::<Path>()?.set("a value"))
71    ///     .and("baz".parse::<Path>()?.remove());
72    /// assert_eq!(r#"SET foo = 7, bar = "a value" REMOVE baz"#, set.to_string());
73    /// #
74    /// # Ok(())
75    /// # }
76    /// ```
77    pub fn and<T>(self, other: T) -> Update
78    where
79        T: Into<Update>,
80    {
81        Update::from(self).and(other)
82    }
83}
84
85impl fmt::Display for Set {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.write_str("SET ")?;
88
89        let mut first = true;
90        self.actions.iter().try_for_each(|action| {
91            if first {
92                first = false
93            } else {
94                f.write_str(", ")?;
95            }
96
97            action.fmt(f)
98        })
99    }
100}
101
102impl<T> From<T> for Set
103where
104    T: Into<SetAction>,
105{
106    fn from(value: T) -> Self {
107        Self {
108            actions: vec![value.into()],
109        }
110    }
111}
112
113impl<T> FromIterator<T> for Set
114where
115    T: Into<SetAction>,
116{
117    fn from_iter<I>(iter: I) -> Self
118    where
119        I: IntoIterator<Item = T>,
120    {
121        Self {
122            actions: iter.into_iter().map(Into::into).collect(),
123        }
124    }
125}
126
127#[cfg(test)]
128mod test {
129    use pretty_assertions::assert_eq;
130
131    use crate::{Num, Path};
132
133    use super::{Assign, IfNotExists, ListAppend, Math, Set, SetAction};
134
135    #[test]
136    fn from() -> Result<(), Box<dyn std::error::Error>> {
137        let assign: Assign = "foo".parse::<Path>()?.set(Num::new(8));
138        let if_not_exists: IfNotExists = "bar".parse::<Path>()?.if_not_exists().set(Num::new(7));
139        let math: Math = "baz".parse::<Path>()?.math().add(1);
140        let list_append: ListAppend = "quux".parse::<Path>()?.list_append().list(["d", "e", "f"]);
141
142        let _set = [
143            Set::from(assign.clone()),
144            Set::from(if_not_exists),
145            Set::from(math),
146            Set::from(list_append),
147        ];
148
149        let _set = Set::from(SetAction::from(assign));
150
151        Ok(())
152    }
153
154    #[test]
155    fn and() -> Result<(), Box<dyn std::error::Error>> {
156        let assign: Assign = "bar".parse::<Path>()?.set(Num::new(8));
157        let set: Set = Set::from("foo".parse::<Path>()?.set("a value"));
158
159        // Should be able to concatenate anything that can be turned into a SetAction.
160
161        let combined = set.clone().and(assign.clone());
162        assert_eq!(r#"SET foo = "a value", bar = 8"#, combined.to_string());
163
164        // Should be able to concatenate a SetAction instance.
165
166        let combined = set.clone().and(SetAction::from(assign.clone()));
167        assert_eq!(r#"SET foo = "a value", bar = 8"#, combined.to_string());
168
169        // Should be able to concatenate a Set instance
170
171        let set_2: Set = [
172            SetAction::from(assign),
173            SetAction::from("baz".parse::<Path>()?.if_not_exists().set(Num::new(7))),
174        ]
175        .into_iter()
176        .collect();
177        let combined = set.clone().and(set_2);
178        assert_eq!(
179            r#"SET foo = "a value", bar = 8, baz = if_not_exists(baz, 7)"#,
180            combined.to_string()
181        );
182
183        // Should be able to concatenate a Remove instance
184
185        let combined = set.clone().and("quux".parse::<Path>()?.remove());
186        assert_eq!(r#"SET foo = "a value" REMOVE quux"#, combined.to_string());
187
188        // Should be able to concatenate a SetRemove instance
189
190        let combined = set.and("quux".parse::<Path>()?.remove());
191        assert_eq!(r#"SET foo = "a value" REMOVE quux"#, combined.to_string());
192
193        Ok(())
194    }
195}