1use prost_types::{ListValue, Value};
2
3use google_cloud_googleapis::spanner::v1::mutation::{Delete, Operation, Write};
4use google_cloud_googleapis::spanner::v1::Mutation;
5
6use crate::key::KeySet;
7use crate::statement::{ToKind, ToStruct};
8
9fn write(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Write {
10 let values = values
11 .iter()
12 .map(|x| Value {
13 kind: Some(x.to_kind()),
14 })
15 .collect();
16
17 Write {
18 table: table.to_string(),
19 columns: columns.iter().map(|x| x.to_string()).collect(),
20 values: vec![ListValue { values }],
21 }
22}
23
24fn write_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Write {
25 let mut columns = Vec::with_capacity(columns_ans_values.len());
26 let mut values = Vec::with_capacity(columns_ans_values.len());
27 columns_ans_values.iter().for_each(|x| {
28 columns.push(x.0.to_string());
29 values.push(Value {
30 kind: Some(x.1.to_kind()),
31 })
32 });
33 Write {
34 table: table.to_string(),
35 columns,
36 values: vec![ListValue { values }],
37 }
38}
39
40fn write_struct(table: &str, to_struct: impl ToStruct) -> Write {
41 let kinds = to_struct.to_kinds();
42 let mut columns = Vec::with_capacity(kinds.len());
43 let mut values = Vec::with_capacity(kinds.len());
44 kinds.into_iter().for_each(|x| {
45 columns.push(x.0.to_string());
46 values.push(Value { kind: Some(x.1) })
47 });
48 Write {
49 table: table.to_string(),
50 columns,
51 values: vec![ListValue { values }],
52 }
53}
54
55pub fn insert(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
58 Mutation {
59 operation: Some(Operation::Insert(write(table, columns, values))),
60 }
61}
62
63pub fn insert_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
67 Mutation {
68 operation: Some(Operation::Insert(write_map(table, columns_ans_values))),
69 }
70}
71
72pub fn insert_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
76 Mutation {
77 operation: Some(Operation::Insert(write_struct(table, to_struct))),
78 }
79}
80
81pub fn update(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
84 Mutation {
85 operation: Some(Operation::Update(write(table, columns, values))),
86 }
87}
88
89pub fn update_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
93 Mutation {
94 operation: Some(Operation::Update(write_map(table, columns_ans_values))),
95 }
96}
97
98pub fn update_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
101 Mutation {
102 operation: Some(Operation::Update(write_struct(table, to_struct))),
103 }
104}
105
106pub fn replace(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
112 Mutation {
113 operation: Some(Operation::Replace(write(table, columns, values))),
114 }
115}
116
117pub fn replace_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
123 Mutation {
124 operation: Some(Operation::Replace(write_map(table, columns_ans_values))),
125 }
126}
127
128pub fn replace_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
132 Mutation {
133 operation: Some(Operation::Replace(write_struct(table, to_struct))),
134 }
135}
136
137pub fn insert_or_update(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
143 Mutation {
144 operation: Some(Operation::InsertOrUpdate(write(table, columns, values))),
145 }
146}
147
148pub fn insert_or_update_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
154 Mutation {
155 operation: Some(Operation::InsertOrUpdate(write_map(table, columns_ans_values))),
156 }
157}
158
159pub fn insert_or_update_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
164 Mutation {
165 operation: Some(Operation::InsertOrUpdate(write_struct(table, to_struct))),
166 }
167}
168
169pub fn delete(table: &str, key_set: impl Into<KeySet>) -> Mutation {
172 Mutation {
173 operation: Some(Operation::Delete(Delete {
174 table: table.to_string(),
175 key_set: Some(key_set.into().inner),
176 })),
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use prost_types::value::Kind;
183
184 use google_cloud_googleapis::spanner::*;
185
186 use crate::key::*;
187 use crate::mutation::*;
188 use crate::statement::{Kinds, ToKind, Types};
189 use crate::value::CommitTimestamp;
190
191 struct TestStruct {
192 pub struct_field: String,
193 }
194
195 impl ToStruct for TestStruct {
196 fn to_kinds(&self) -> Kinds {
197 vec![("StructField", self.struct_field.to_kind())]
198 }
199
200 fn get_types() -> Types {
201 vec![("StructField", String::get_type())]
202 }
203 }
204
205 #[test]
206 fn test_insert() {
207 let mutation = insert(
208 "Guild",
209 &["GuildId", "UserId", "UpdatedAt"],
210 &[&"1", &"2", &CommitTimestamp::new()],
211 );
212 match mutation.operation.unwrap() {
213 v1::mutation::Operation::Insert(mut w) => {
214 assert_eq!("Guild", w.table);
215 assert_eq!(3, w.values.pop().unwrap().values.len());
216 assert_eq!("UpdatedAt", w.columns.pop().unwrap());
217 assert_eq!("UserId", w.columns.pop().unwrap());
218 assert_eq!("GuildId", w.columns.pop().unwrap());
219 }
220 _ => panic!("invalid operation"),
221 }
222 }
223
224 #[test]
225 fn test_insert_map() {
226 let user_id = 1;
227 let mutation = insert_map(
228 "Guild",
229 &[
230 ("UserId", &"aa"),
231 ("GuildId", &user_id),
232 ("updatedAt", &CommitTimestamp::new()),
233 ],
234 );
235 match mutation.operation.unwrap() {
236 v1::mutation::Operation::Insert(mut w) => {
237 assert_eq!("Guild", w.table);
238 assert_eq!(3, w.values.pop().unwrap().values.len());
239 assert_eq!(3, w.columns.len());
240 }
241 _ => panic!("invalid operation"),
242 }
243 }
244
245 #[test]
246 fn test_insert_struct() {
247 let mutation = insert_struct(
248 "Guild",
249 TestStruct {
250 struct_field: "abc".to_string(),
251 },
252 );
253 match mutation.operation.unwrap() {
254 v1::mutation::Operation::Insert(w) => assert_struct(w),
255 _ => panic!("invalid operation"),
256 }
257 }
258
259 #[test]
260 fn test_insert_struct_ref() {
261 let mutation = insert_struct(
262 "Guild",
263 TestStruct {
264 struct_field: "abc".to_string(),
265 },
266 );
267 match mutation.operation.unwrap() {
268 v1::mutation::Operation::Insert(w) => assert_struct(w),
269 _ => panic!("invalid operation"),
270 }
271 }
272
273 #[test]
274 fn test_update() {
275 let mutation = update(
276 "Guild",
277 &["GuildId", "UserId", "UpdatedAt"],
278 &[&"1", &"2", &CommitTimestamp::new()],
279 );
280 match mutation.operation.unwrap() {
281 v1::mutation::Operation::Update(mut w) => {
282 assert_eq!("Guild", w.table);
283 assert_eq!(3, w.values.pop().unwrap().values.len());
284 assert_eq!(3, w.columns.len());
285 }
286 _ => panic!("invalid operation"),
287 }
288 }
289
290 #[test]
291 fn test_update_struct() {
292 let mutation = update_struct(
293 "Guild",
294 TestStruct {
295 struct_field: "abc".to_string(),
296 },
297 );
298 match mutation.operation.unwrap() {
299 v1::mutation::Operation::Update(w) => assert_struct(w),
300 _ => panic!("invalid operation"),
301 }
302 }
303
304 #[test]
305 fn test_update_struct_ref() {
306 let st = TestStruct {
307 struct_field: "abc".to_string(),
308 };
309 let mutation = update_struct("Guild", st);
310 match mutation.operation.unwrap() {
311 v1::mutation::Operation::Update(w) => assert_struct(w),
312 _ => panic!("invalid operation"),
313 }
314 }
315
316 #[test]
317 fn test_replace() {
318 let mutation = replace(
319 "Guild",
320 &["GuildId", "UserId", "UpdatedAt"],
321 &[&"1", &"2", &CommitTimestamp::new()],
322 );
323 match mutation.operation.unwrap() {
324 v1::mutation::Operation::Replace(mut w) => {
325 assert_eq!("Guild", w.table);
326 assert_eq!(3, w.values.pop().unwrap().values.len());
327 assert_eq!(3, w.columns.len());
328 }
329 _ => panic!("invalid operation"),
330 }
331 }
332
333 #[test]
334 fn test_replace_struct() {
335 let mutation = replace_struct(
336 "Guild",
337 TestStruct {
338 struct_field: "abc".to_string(),
339 },
340 );
341 match mutation.operation.unwrap() {
342 v1::mutation::Operation::Replace(w) => assert_struct(w),
343 _ => panic!("invalid operation"),
344 }
345 }
346
347 #[test]
348 fn test_insert_or_update() {
349 let mutation = insert_or_update(
350 "Guild",
351 &["GuildId", "UserId", "UpdatedAt"],
352 &[&"1", &"2", &CommitTimestamp::new()],
353 );
354 match mutation.operation.unwrap() {
355 v1::mutation::Operation::InsertOrUpdate(mut w) => {
356 assert_eq!("Guild", w.table);
357 assert_eq!(3, w.values.pop().unwrap().values.len());
358 assert_eq!(3, w.columns.len());
359 }
360 _ => panic!("invalid operation"),
361 }
362 }
363
364 #[test]
365 fn test_insert_or_update_struct() {
366 let mutation = insert_or_update_struct(
367 "Guild",
368 TestStruct {
369 struct_field: "abc".to_string(),
370 },
371 );
372 match mutation.operation.unwrap() {
373 v1::mutation::Operation::InsertOrUpdate(w) => assert_struct(w),
374 _ => panic!("invalid operation"),
375 }
376 }
377
378 #[test]
379 fn test_delete() {
380 let mutation = delete("Guild", all_keys());
381 match mutation.operation.unwrap() {
382 v1::mutation::Operation::Delete(w) => {
383 assert_eq!("Guild", w.table);
384 assert!(w.key_set.unwrap().all);
385 }
386 _ => panic!("invalid operation"),
387 }
388 }
389
390 fn assert_struct(mut w: Write) {
391 assert_eq!("Guild", w.table);
392 assert_eq!("StructField", w.columns.pop().unwrap());
393 assert_eq!(
394 "abc",
395 match w.values.pop().unwrap().values.pop().unwrap().kind.unwrap() {
396 Kind::StringValue(v) => v,
397 _ => panic!("error"),
398 }
399 );
400 }
401}