dynamodb_expression/update/set/
math.rs1use core::fmt::{self, Write};
2
3use crate::{
4 path::Path,
5 update::Update,
6 value::{Num, ValueOrRef},
7};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Math {
16 pub(crate) dst: Path,
17 pub(crate) src: Option<Path>,
18 op: MathOp,
19 pub(crate) num: ValueOrRef,
20}
21
22impl Math {
29 pub fn builder<T>(dst: T) -> Builder
31 where
32 T: Into<Path>,
33 {
34 Builder {
35 dst: dst.into(),
36 src: None,
37 }
38 }
39
40 pub fn and<T>(self, other: T) -> Update
58 where
59 T: Into<Update>,
60 {
61 Update::from(self).and(other)
62 }
63}
64
65impl fmt::Display for Math {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 self.dst.fmt(f)?;
68 f.write_str(" = ")?;
69 self.src.as_ref().unwrap_or(&self.dst).fmt(f)?;
71 f.write_char(' ')?;
72 self.op.fmt(f)?;
73 f.write_char(' ')?;
74 self.num.fmt(f)
75 }
76}
77
78#[derive(Clone, Copy, PartialEq, Eq)]
79enum MathOp {
80 Add,
81 Sub,
82}
83
84impl fmt::Debug for MathOp {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 fmt::Display::fmt(self, f)
87 }
88}
89
90impl fmt::Display for MathOp {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.write_char(match self {
93 MathOp::Add => '+',
94 MathOp::Sub => '-',
95 })
96 }
97}
98
99#[must_use = "Consume this `Builder` by using its `.add()` or `.sub()` methods"]
101#[derive(Debug, Clone)]
102pub struct Builder {
103 dst: Path,
104 src: Option<Path>,
105}
106
107impl Builder {
108 pub fn src<T>(mut self, src: T) -> Self
111 where
112 T: Into<Path>,
113 {
114 self.src = Some(src.into());
115
116 self
117 }
118
119 #[allow(clippy::should_implement_trait)]
121 pub fn add<T>(self, num: T) -> Math
122 where
123 T: Into<Num>,
124 {
125 self.with_op(MathOp::Add, num)
126 }
127
128 #[allow(clippy::should_implement_trait)]
130 pub fn sub<T>(self, num: T) -> Math
131 where
132 T: Into<Num>,
133 {
134 self.with_op(MathOp::Sub, num)
135 }
136
137 fn with_op<T>(self, op: MathOp, num: T) -> Math
138 where
139 T: Into<Num>,
140 {
141 let Self { dst, src } = self;
142
143 Math {
144 dst,
145 src,
146 op,
147 num: num.into().into(),
148 }
149 }
150}
151
152#[cfg(test)]
153mod test {
154 use pretty_assertions::assert_eq;
155
156 use crate::{
157 update::{Assign, Set, SetAction},
158 Num, Path,
159 };
160
161 use super::Math;
162
163 #[test]
164 fn and() -> Result<(), Box<dyn std::error::Error>> {
165 let math: Math = "foo".parse::<Path>()?.math().add(1);
166 let assign: Assign = "bar".parse::<Path>()?.set(Num::new(8));
167
168 let combined = math.clone().and(assign.clone());
171 assert_eq!(r#"SET foo = foo + 1, bar = 8"#, combined.to_string());
172
173 let combined = math.clone().and(SetAction::from(assign.clone()));
176 assert_eq!(r#"SET foo = foo + 1, bar = 8"#, combined.to_string());
177
178 let set: Set = [
181 SetAction::from(assign),
182 SetAction::from("baz".parse::<Path>()?.list_append().list(["d", "e", "f"])),
183 ]
184 .into_iter()
185 .collect();
186 let combined = math.clone().and(set);
187 assert_eq!(
188 r#"SET foo = foo + 1, bar = 8, baz = list_append(baz, ["d", "e", "f"])"#,
189 combined.to_string()
190 );
191
192 let combined = math.clone().and("quux".parse::<Path>()?.remove());
195 assert_eq!(r#"SET foo = foo + 1 REMOVE quux"#, combined.to_string());
196
197 let combined = math.and("quux".parse::<Path>()?.remove());
200 assert_eq!(r#"SET foo = foo + 1 REMOVE quux"#, combined.to_string());
201
202 Ok(())
203 }
204}