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}