dynamodb_expression/update/
remove.rs

1use core::fmt;
2
3use crate::path::Path;
4
5use super::Update;
6
7/// For use an in an update expression to [remove attributes from an
8/// item][1], or [elements from a list][2].
9///
10/// Prefer [`Path::remove`] over this.
11///
12/// See also: [`Update`]
13///
14/// # Examples
15///
16/// ```
17/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
18/// use dynamodb_expression::{Expression, Path, update::{Remove, Update}};
19/// # use pretty_assertions::assert_eq;
20///
21/// let remove = "foo".parse::<Path>()?.remove();
22/// assert_eq!("REMOVE foo", remove.to_string());
23///
24/// let remove = Remove::from("foo[8]".parse::<Path>()?);
25/// assert_eq!("REMOVE foo[8]", remove.to_string());
26///
27/// let remove: Remove = ["foo", "bar", "baz"].into_iter().map(Path::new_name).collect();
28/// assert_eq!("REMOVE foo, bar, baz", remove.to_string());
29///
30/// let remove = remove.and("quux".parse::<Path>()?.remove());
31/// assert_eq!("REMOVE foo, bar, baz, quux", remove.to_string());
32///
33/// // Use in an update expression
34/// let update = Update::from(remove.clone());
35/// # _ = update;
36///
37/// // Use an expression builder
38/// let expression = Expression::builder().with_update(remove).build();
39/// # _ = expression;
40/// #
41/// # Ok(())
42/// # }
43/// ```
44///
45/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.REMOVE
46/// [2]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.REMOVE.RemovingListElements
47/// [`Update`]: crate::update::Update
48#[must_use = "Use in an update expression with `Update::from(remove)`"]
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct Remove {
51    pub(crate) paths: Vec<Path>,
52}
53
54impl Remove {
55    /// Add an additional [`Update`] statement to this expression.
56    ///
57    /// ```
58    /// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
59    /// use dynamodb_expression::Path;
60    /// # use pretty_assertions::assert_eq;
61    ///
62    /// let remove = "foo".parse::<Path>()?.remove().and("bar".parse::<Path>()?.remove());
63    /// assert_eq!("REMOVE foo, bar", remove.to_string());
64    ///
65    /// let set_remove = remove.and("baz".parse::<Path>()?.set("a value"));
66    /// assert_eq!(r#"SET baz = "a value" REMOVE foo, bar"#, set_remove.to_string());
67    /// #
68    /// # Ok(())
69    /// # }
70    /// ```
71    pub fn and<T>(self, other: T) -> Update
72    where
73        T: Into<Update>,
74    {
75        Update::from(self).and(other)
76    }
77}
78
79impl fmt::Display for Remove {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        f.write_str("REMOVE ")?;
82
83        let mut first = true;
84        self.paths.iter().try_for_each(|name| {
85            if first {
86                first = false;
87            } else {
88                f.write_str(", ")?;
89            }
90
91            name.fmt(f)
92        })
93    }
94}
95
96impl<T> From<T> for Remove
97where
98    T: Into<Path>,
99{
100    fn from(path: T) -> Self {
101        Self {
102            paths: vec![path.into()],
103        }
104    }
105}
106
107impl<T> FromIterator<T> for Remove
108where
109    T: Into<Path>,
110{
111    fn from_iter<I>(paths: I) -> Self
112    where
113        I: IntoIterator<Item = T>,
114    {
115        Self {
116            paths: paths.into_iter().map(Into::into).collect(),
117        }
118    }
119}
120
121#[cfg(test)]
122mod test {
123    use pretty_assertions::assert_eq;
124
125    use crate::Path;
126
127    #[test]
128    fn sub_attributes() {
129        assert_eq!(
130            "REMOVE foo.bar",
131            "foo.bar".parse::<Path>().unwrap().remove().to_string()
132        );
133        assert_eq!(
134            "REMOVE foo[3].bar",
135            "foo[3].bar".parse::<Path>().unwrap().remove().to_string()
136        );
137        assert_eq!(
138            "REMOVE foo[3][7]",
139            "foo[3][7]".parse::<Path>().unwrap().remove().to_string()
140        );
141    }
142
143    #[test]
144    fn and() {
145        let remove = "foo"
146            .parse::<Path>()
147            .unwrap()
148            .remove()
149            .and("bar".parse::<Path>().unwrap().remove());
150        assert_eq!("REMOVE foo, bar", remove.to_string());
151    }
152}