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