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 135 136 137 138 139 140
use core::fmt::{self, Write};
use crate::{
path::Path,
update::Set,
value::{Num, ValueOrRef},
};
/// Represents a [DynamoDB math operation][1] used as a part of an update expression.
///
/// See also: [`Path::math`]
///
/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Math {
pub(crate) dst: Path,
pub(crate) src: Option<Path>,
op: MathOp,
pub(crate) num: ValueOrRef,
}
/// A [math operation][1] to modify a field and assign the updated value
/// to another (possibly different) field.
///
/// See also: [`Path::math`]
///
/// [1]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
impl Math {
pub fn builder<T>(dst: T) -> Builder
where
T: Into<Path>,
{
Builder {
dst: dst.into(),
src: None,
}
}
/// Add an additional action to this `SET` statement.
///
/// ```
/// use dynamodb_expression::{Num, Path};
/// # use pretty_assertions::assert_eq;
///
/// let set = Path::new_name("foo").math().add(7)
/// .and(Path::new_name("bar").assign("a value"));
/// assert_eq!(r#"SET foo = foo + 7, bar = "a value""#, set.to_string());
/// ```
pub fn and<T>(self, action: T) -> Set
where
T: Into<Set>,
{
Set::from(self).and(action)
}
}
impl fmt::Display for Math {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { dst, src, op, num } = self;
// If no source field is specified, default to using the destination field.
let src = src.as_ref().unwrap_or(dst);
write!(f, "{dst} = {src} {op} {num}")
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum MathOp {
Add,
Sub,
}
impl fmt::Debug for MathOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for MathOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char(match self {
MathOp::Add => '+',
MathOp::Sub => '-',
})
}
}
/// See: [`Path::math`]
#[must_use = "Consume this `Builder` by using its `.add()` or `.sub()` methods"]
#[derive(Debug, Clone)]
pub struct Builder {
dst: Path,
src: Option<Path>,
}
impl Builder {
/// Sets the source field to read the initial value from.
/// Defaults to the destination field.
pub fn src<T>(mut self, src: T) -> Self
where
T: Into<Path>,
{
self.src = Some(src.into());
self
}
/// Sets addition as the operation to perform.
#[allow(clippy::should_implement_trait)]
pub fn add<T>(self, num: T) -> Math
where
T: Into<Num>,
{
self.with_op(MathOp::Add, num)
}
/// Sets subtraction as the operation to perform.
#[allow(clippy::should_implement_trait)]
pub fn sub<T>(self, num: T) -> Math
where
T: Into<Num>,
{
self.with_op(MathOp::Sub, num)
}
fn with_op<T>(self, op: MathOp, num: T) -> Math
where
T: Into<Num>,
{
let Self { dst, src } = self;
Math {
dst,
src,
op,
num: num.into().into(),
}
}
}