Skip to content

Commit e9c8b42

Browse files
committed
feat: relocation is working when group exists
1 parent 32e3b40 commit e9c8b42

File tree

1 file changed

+177
-20
lines changed

1 file changed

+177
-20
lines changed

src/db/group.rs

+177-20
Original file line numberDiff line numberDiff line change
@@ -241,43 +241,126 @@ impl Group {
241241
}
242242
}
243243

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> {
245245
if location.len() == 0 {
246-
panic!("TODO handle this with a Response.");
246+
return Err("Empty location.".to_string());
247247
}
248248

249249
let mut remaining_location = location.clone();
250250
remaining_location.remove(0);
251251

252252
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;
257262
}
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+
}
261323
}
262324

263325
let next_location = &remaining_location[0];
264326

265327
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
268330
);
269331
for node in &mut self.children {
270332
if let Node::Group(g) = node {
271333
if g.uuid != next_location.uuid {
272334
continue;
273335
}
274-
g.remove_entry(uuid, &remaining_location);
275-
return;
336+
return g.remove_entry(uuid, &remaining_location);
276337
}
277338
}
278339

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
281364
}
282365

283366
pub fn find_entry_by_uuid(&self, id: Uuid) -> Option<&Entry> {
@@ -347,6 +430,10 @@ impl Group {
347430
Some(e) => e,
348431
None => continue,
349432
};
433+
let existing_entry_location = match self.find_entry_location(entry.uuid) {
434+
Some(l) => l,
435+
None => continue,
436+
};
350437

351438
let source_location_changed_time = match entry.times.get_location_changed() {
352439
Some(t) => *t,
@@ -369,9 +456,13 @@ impl Group {
369456
}
370457
};
371458
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);
373465
}
374-
// TODO relocate the existing entry if necessary
375466
}
376467

377468
for (entry, entry_location) in other.get_all_entries(&vec![]) {
@@ -397,7 +488,6 @@ impl Group {
397488
}
398489
}
399490

400-
// TODO update locations
401491
// TODO handle deleted objects
402492
Ok(log)
403493
}
@@ -452,7 +542,7 @@ impl<'a> IntoIterator for &'a Group {
452542
mod group_tests {
453543
use std::{thread, time};
454544

455-
use super::{Entry, Group, Node, Times, Value};
545+
use super::{Entry, Group, GroupRef, Node, Times, Value};
456546

457547
#[test]
458548
fn test_merge_idempotence() {
@@ -548,9 +638,76 @@ mod group_tests {
548638
assert_eq!(created_entry_location.len(), 2);
549639
}
550640

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+
551708
#[test]
552709
#[ignore]
553-
fn test_merge_entry_relocation() {
710+
fn test_merge_entry_relocation_new_group() {
554711
let mut entry = Entry::new();
555712
let entry_uuid = entry.uuid.clone();
556713
entry.set_field_and_commit("Title", "entry1");

0 commit comments

Comments
 (0)