@@ -241,43 +241,126 @@ impl Group {
241
241
}
242
242
}
243
243
244
- pub ( crate ) fn remove_entry ( & mut self , uuid : & Uuid , location : & NodeLocation ) {
244
+ pub ( crate ) fn get_group_mut ( & mut self , location : & NodeLocation ) -> Result < & mut Group , String > {
245
245
if location. len ( ) == 0 {
246
- panic ! ( "TODO handle this with a Response." ) ;
246
+ return Err ( "Empty location." . to_string ( ) ) ;
247
247
}
248
248
249
249
let mut remaining_location = location. clone ( ) ;
250
250
remaining_location. remove ( 0 ) ;
251
251
252
252
if remaining_location. len ( ) == 0 {
253
- // FIXME we should verify that we removed the entry!
254
- self . children . iter_mut ( ) . filter ( |n| {
255
- if let Node :: Entry ( e) = n {
256
- return e. uuid == * uuid;
253
+ return Ok ( self ) ;
254
+ }
255
+
256
+ let next_location = & remaining_location[ 0 ] ;
257
+
258
+ for node in & mut self . children {
259
+ if let Node :: Group ( g) = node {
260
+ if g. uuid != next_location. uuid {
261
+ continue ;
257
262
}
258
- return false ;
259
- } ) ;
260
- return ;
263
+ return g. get_group_mut ( & remaining_location) ;
264
+ }
265
+ }
266
+
267
+ return Err ( "The group was not found." . to_string ( ) ) ;
268
+ }
269
+
270
+ pub ( crate ) fn insert_entry (
271
+ & mut self ,
272
+ entry : Entry ,
273
+ location : & NodeLocation ,
274
+ ) -> Result < ( ) , String > {
275
+ let mut group: & mut Group = self . get_group_mut ( & location) ?;
276
+ group. children . push ( Node :: Entry ( entry) ) ;
277
+ Ok ( ( ) )
278
+ }
279
+
280
+ pub ( crate ) fn remove_entry (
281
+ & mut self ,
282
+ uuid : & Uuid ,
283
+ location : & NodeLocation ,
284
+ ) -> Result < Entry , String > {
285
+ // FIXME this should use get_group_mut instead of re-implementing the group
286
+ // searching algorithm.
287
+ if location. len ( ) == 0 {
288
+ return Err ( "Empty location." . to_string ( ) ) ;
289
+ }
290
+
291
+ let mut remaining_location = location. clone ( ) ;
292
+ remaining_location. remove ( 0 ) ;
293
+
294
+ if remaining_location. len ( ) == 0 {
295
+ let mut removed_entry: Option < Entry > = None ;
296
+ let mut new_nodes: Vec < Node > = vec ! [ ] ;
297
+ println ! ( "Searching for entry {} in {}" , uuid, self . name) ;
298
+ for node in & self . children {
299
+ match node {
300
+ Node :: Entry ( e) => {
301
+ println ! ( "Saw entry {}" , & e. uuid) ;
302
+ if & e. uuid != uuid {
303
+ new_nodes. push ( node. clone ( ) ) ;
304
+ continue ;
305
+ }
306
+ removed_entry = Some ( e. clone ( ) ) ;
307
+ }
308
+ Node :: Group ( _) => {
309
+ new_nodes. push ( node. clone ( ) ) ;
310
+ }
311
+ }
312
+ }
313
+
314
+ if let Some ( entry) = removed_entry {
315
+ self . children = new_nodes;
316
+ return Ok ( entry) ;
317
+ } else {
318
+ return Err ( format ! (
319
+ "Could not find entry {} in group {}." ,
320
+ uuid, self . name
321
+ ) ) ;
322
+ }
261
323
}
262
324
263
325
let next_location = & remaining_location[ 0 ] ;
264
326
265
327
println ! (
266
- "Searching for group {} {:?}" ,
267
- next_location. name, next_location. uuid
328
+ "Searching for group {} {:?} in {} " ,
329
+ next_location. name, next_location. uuid, self . name
268
330
) ;
269
331
for node in & mut self . children {
270
332
if let Node :: Group ( g) = node {
271
333
if g. uuid != next_location. uuid {
272
334
continue ;
273
335
}
274
- g. remove_entry ( uuid, & remaining_location) ;
275
- return ;
336
+ return g. remove_entry ( uuid, & remaining_location) ;
276
337
}
277
338
}
278
339
279
- // The group was not found, so we create it.
280
- panic ! ( "TODO handle this with a Response." ) ;
340
+ return Err ( "The group was not found." . to_string ( ) ) ;
341
+ }
342
+
343
+ pub ( crate ) fn find_entry_location ( & self , id : Uuid ) -> Option < NodeLocation > {
344
+ let mut current_location = vec ! [ GroupRef {
345
+ uuid: self . uuid. clone( ) ,
346
+ name: self . name. clone( ) ,
347
+ } ] ;
348
+ for node in & self . children {
349
+ match node {
350
+ Node :: Entry ( e) => {
351
+ if e. uuid == id {
352
+ return Some ( current_location) ;
353
+ }
354
+ }
355
+ Node :: Group ( g) => {
356
+ if let Some ( mut location) = g. find_entry_location ( id) {
357
+ current_location. append ( & mut location) ;
358
+ return Some ( current_location) ;
359
+ }
360
+ }
361
+ }
362
+ }
363
+ None
281
364
}
282
365
283
366
pub fn find_entry_by_uuid ( & self , id : Uuid ) -> Option < & Entry > {
@@ -347,6 +430,10 @@ impl Group {
347
430
Some ( e) => e,
348
431
None => continue ,
349
432
} ;
433
+ let existing_entry_location = match self . find_entry_location ( entry. uuid ) {
434
+ Some ( l) => l,
435
+ None => continue ,
436
+ } ;
350
437
351
438
let source_location_changed_time = match entry. times . get_location_changed ( ) {
352
439
Some ( t) => * t,
@@ -369,9 +456,13 @@ impl Group {
369
456
}
370
457
} ;
371
458
if source_location_changed_time > destination_location_changed {
372
- // self.remove_entry(&entry.uuid, &entry_location);
459
+ log. events . push ( MergeEvent {
460
+ event_type : MergeEventType :: EntryLocationUpdated ,
461
+ node_uuid : entry. uuid ,
462
+ } ) ;
463
+ let removed_entry = self . remove_entry ( & entry. uuid , & existing_entry_location) ?;
464
+ self . insert_entry ( entry. clone ( ) , & entry_location) ;
373
465
}
374
- // TODO relocate the existing entry if necessary
375
466
}
376
467
377
468
for ( entry, entry_location) in other. get_all_entries ( & vec ! [ ] ) {
@@ -397,7 +488,6 @@ impl Group {
397
488
}
398
489
}
399
490
400
- // TODO update locations
401
491
// TODO handle deleted objects
402
492
Ok ( log)
403
493
}
@@ -452,7 +542,7 @@ impl<'a> IntoIterator for &'a Group {
452
542
mod group_tests {
453
543
use std:: { thread, time} ;
454
544
455
- use super :: { Entry , Group , Node , Times , Value } ;
545
+ use super :: { Entry , Group , GroupRef , Node , Times , Value } ;
456
546
457
547
#[ test]
458
548
fn test_merge_idempotence ( ) {
@@ -548,9 +638,76 @@ mod group_tests {
548
638
assert_eq ! ( created_entry_location. len( ) , 2 ) ;
549
639
}
550
640
641
+ #[ test]
642
+ fn test_merge_entry_relocation_existing_group ( ) {
643
+ let mut entry = Entry :: new ( ) ;
644
+ let entry_uuid = entry. uuid . clone ( ) ;
645
+ entry. set_field_and_commit ( "Title" , "entry1" ) ;
646
+ let mut destination_group = Group :: new ( "group1" ) ;
647
+ let mut destination_sub_group1 = Group :: new ( "subgroup1" ) ;
648
+ let mut destination_sub_group2 = Group :: new ( "subgroup2" ) ;
649
+ destination_sub_group1
650
+ . children
651
+ . push ( Node :: Entry ( entry. clone ( ) ) ) ;
652
+ destination_group
653
+ . children
654
+ . push ( Node :: Group ( destination_sub_group1. clone ( ) ) ) ;
655
+ destination_group
656
+ . children
657
+ . push ( Node :: Group ( destination_sub_group2. clone ( ) ) ) ;
658
+
659
+ let mut source_group = destination_group. clone ( ) ;
660
+ assert ! ( source_group. get_all_entries( & vec![ ] ) . len( ) == 1 ) ;
661
+
662
+ let mut removed_entry = source_group
663
+ . remove_entry (
664
+ & entry_uuid,
665
+ & vec ! [
666
+ GroupRef {
667
+ uuid: destination_group. uuid. clone( ) ,
668
+ name: "" . to_string( ) ,
669
+ } ,
670
+ GroupRef {
671
+ uuid: destination_sub_group1. uuid. clone( ) ,
672
+ name: "" . to_string( ) ,
673
+ } ,
674
+ ] ,
675
+ )
676
+ . unwrap ( ) ;
677
+ removed_entry. times . set_location_changed ( Times :: now ( ) ) ;
678
+ assert ! ( source_group. get_all_entries( & vec![ ] ) . len( ) == 0 ) ;
679
+ // FIXME we should not have to update the history here. We should
680
+ // have a better compare function in the merge function instead.
681
+ removed_entry. update_history ( ) ;
682
+ source_group
683
+ . insert_entry (
684
+ removed_entry,
685
+ & vec ! [
686
+ GroupRef {
687
+ uuid: destination_group. uuid. clone( ) ,
688
+ name: "" . to_string( ) ,
689
+ } ,
690
+ GroupRef {
691
+ uuid: destination_sub_group2. uuid. clone( ) ,
692
+ name: "" . to_string( ) ,
693
+ } ,
694
+ ] ,
695
+ )
696
+ . unwrap ( ) ;
697
+
698
+ destination_group. merge ( & source_group) ;
699
+
700
+ let destination_entries = destination_group. get_all_entries ( & vec ! [ ] ) ;
701
+ assert_eq ! ( destination_entries. len( ) , 1 ) ;
702
+ let ( moved_entry, moved_entry_location) = destination_entries. get ( 0 ) . unwrap ( ) ;
703
+ assert_eq ! ( moved_entry_location. len( ) , 2 ) ;
704
+ assert_eq ! ( moved_entry_location[ 0 ] . name, "group1" . to_string( ) ) ;
705
+ assert_eq ! ( moved_entry_location[ 1 ] . name, "subgroup2" . to_string( ) ) ;
706
+ }
707
+
551
708
#[ test]
552
709
#[ ignore]
553
- fn test_merge_entry_relocation ( ) {
710
+ fn test_merge_entry_relocation_new_group ( ) {
554
711
let mut entry = Entry :: new ( ) ;
555
712
let entry_uuid = entry. uuid . clone ( ) ;
556
713
entry. set_field_and_commit ( "Title" , "entry1" ) ;
0 commit comments