1use crate::db::*;
2use chrono::NaiveDateTime;
3use std::collections::{HashMap, VecDeque};
4use uuid::Uuid;
5
6#[derive(Debug, Clone)]
7pub enum MergeEventType {
8 EntryCreated,
9 EntryDeleted,
10 EntryLocationUpdated,
11 EntryUpdated,
12
13 GroupCreated,
14 GroupDeleted,
15 GroupLocationUpdated,
16 GroupUpdated,
17
18 IconCreated,
19 IconUpdated,
20}
21
22#[derive(Debug, Clone)]
23pub struct MergeEvent {
24 pub node_uuid: Uuid,
27
28 pub event_type: MergeEventType,
29}
30
31#[derive(Debug, Default, Clone)]
32pub struct MergeLog {
33 pub warnings: Vec<String>,
34 pub events: Vec<MergeEvent>,
35}
36
37#[derive(thiserror::Error, Debug)]
39pub enum MergeError {
40 #[error("{0}")]
41 GenericError(String),
42
43 #[error("Could not find group at {0:?}")]
44 FindGroupError(Vec<Uuid>),
45
46 #[error("Could not find entry at {0:?}")]
47 FindEntryError(Vec<Uuid>),
48
49 #[error("Entries with UUID {0} have the same modification time but have diverged.")]
50 EntryModificationTimeNotUpdated(String),
51
52 #[error("Groups with UUID {0} have the same modification time but have diverged.")]
53 GroupModificationTimeNotUpdated(String),
54
55 #[error("Found history entries with the same timestamp ({0}) for entry {1}.")]
56 DuplicateHistoryEntries(String, String),
57}
58
59impl MergeLog {
60 pub fn merge_with(&self, other: &MergeLog) -> MergeLog {
61 let mut response = MergeLog::default();
62 response.warnings.append(self.warnings.clone().as_mut());
63 response.warnings.append(other.warnings.clone().as_mut());
64 response.events.append(self.events.clone().as_mut());
65 response.events.append(other.events.clone().as_mut());
66 response
67 }
68
69 pub fn append(&mut self, other: &MergeLog) {
70 self.warnings.append(other.warnings.clone().as_mut());
71 self.events.append(other.events.clone().as_mut());
72 }
73}
74
75impl Database {
76 pub fn merge(&mut self, other: &Database) -> Result<MergeLog, MergeError> {
80 let mut log = MergeLog::default();
81 log.append(&self.merge_group(&[], &other.root, false)?);
82 log.append(&self.merge_icons(other)?);
83 log.append(&self.merge_deletions(other)?);
84 Ok(log)
85 }
86
87 fn merge_icons(&mut self, other: &Database) -> Result<MergeLog, MergeError> {
88 let mut log = MergeLog::default();
89
90 for (uuid, source_icon) in &other.meta.custom_icons {
91 let Some(destination_icon) = self.meta.custom_icons.get_mut(uuid) else {
92 if let Some(Some(deletion_time)) = self.deleted_objects.get(uuid)
93 && source_icon.last_modification_time.is_none_or(|modified| modified <= *deletion_time)
94 {
95 continue;
96 }
97
98 self.meta.custom_icons.insert(*uuid, source_icon.clone());
99 log.events.push(MergeEvent {
100 node_uuid: *uuid,
101 event_type: MergeEventType::IconCreated,
102 });
103 continue;
104 };
105
106 let source_is_newer = match (source_icon.last_modification_time, destination_icon.last_modification_time) {
107 (Some(source), Some(destination)) => source > destination,
108 (Some(_), None) => true,
109 _ => false,
110 };
111 if source_is_newer {
112 *destination_icon = source_icon.clone();
113 log.events.push(MergeEvent {
114 node_uuid: *uuid,
115 event_type: MergeEventType::IconUpdated,
116 });
117 }
118 }
119
120 Ok(log)
121 }
122
123 fn merge_deletions(&mut self, other: &Database) -> Result<MergeLog, MergeError> {
124 let is_in_deleted_queue = |uuid: Uuid, deleted_groups_queue: &VecDeque<(Uuid, NaiveDateTime)>| -> bool {
126 for (deleted_uuid, _) in deleted_groups_queue {
127 if *deleted_uuid == uuid {
129 return true;
130 }
131 }
132 false
133 };
134 let mut log = MergeLog::default();
135 let mut new_deleted_objects = self.deleted_objects.clone();
136 for (uuid, deletion_time) in &other.deleted_objects {
138 let deletion_time = deletion_time.unwrap_or_else(Times::now);
139 if new_deleted_objects.contains_key(uuid) {
140 continue;
141 }
142 let entry_location = match Self::find_node_location(&self.root, *uuid) {
143 Some(l) => l,
144 None => continue,
145 };
146 let parent_group = Group::find_group(&self.root, &entry_location).ok_or(MergeError::FindGroupError(entry_location))?;
147
148 let entry = match Group::find_entry(&parent_group, &[*uuid]) {
149 Some(e) => e,
150 None => continue,
152 };
153
154 let entry_last_modification = match with_node::<Entry, _, _>(&entry, |e| e.get_times().get_last_modification()).unwrap() {
155 Some(t) => t,
156 None => {
157 log.warnings.push(format!(
158 "Entry {} did not have a last modification timestamp",
159 entry.borrow().downcast_ref::<Entry>().unwrap().uuid
160 ));
161 Times::now()
162 }
163 };
164 if entry_last_modification < deletion_time {
165 with_node_mut::<Group, _, _>(&parent_group, |pg| pg.remove_node(*uuid)).unwrap()?;
166 log.events.push(MergeEvent {
167 event_type: MergeEventType::EntryDeleted,
168 node_uuid: *uuid,
169 });
170 new_deleted_objects.insert(*uuid, Some(deletion_time));
171 }
172 }
173 let mut deleted_groups_queue: VecDeque<(Uuid, NaiveDateTime)> = VecDeque::new();
174 for (uuid, deletion_time) in &other.deleted_objects {
175 if new_deleted_objects.contains_key(uuid) {
176 continue;
177 }
178 deleted_groups_queue.push_back((*uuid, deletion_time.unwrap_or_else(Times::now)));
179 }
180 while !deleted_groups_queue.is_empty() {
181 let (deleted_uuid, deletion_time) = deleted_groups_queue.pop_front().unwrap();
182 if new_deleted_objects.contains_key(&deleted_uuid) {
183 continue;
184 }
185 let group_location = match Self::find_node_location(&self.root, deleted_uuid) {
186 Some(l) => l,
187 None => continue,
188 };
189 let parent_group = Group::find_group(&self.root, &group_location).ok_or(MergeError::FindGroupError(group_location))?;
190
191 let group = match Group::find_group(&parent_group, &[deleted_uuid]) {
192 Some(g) => g,
193 None => {
194 continue;
197 }
198 };
199 if !with_node::<Group, _, _>(&group, |g| g.entries()).unwrap().is_empty() {
201 continue;
202 }
203 if with_node::<Group, _, _>(&group, |g| {
206 g.groups()
207 .iter()
208 .any(|child| is_in_deleted_queue(child.borrow().get_uuid(), &deleted_groups_queue))
209 })
210 .unwrap()
211 {
212 deleted_groups_queue.push_back((deleted_uuid, deletion_time));
213 continue;
214 }
215 if !with_node::<Group, _, _>(&group, |g| g.groups()).unwrap().is_empty() {
217 continue;
218 }
219 let group_last_modification = match with_node::<Group, _, _>(&group, |g| g.get_times().get_last_modification()).unwrap() {
220 Some(t) => t,
221 None => {
222 log.warnings.push(format!(
223 "Group {} did not have a last modification timestamp",
224 group.borrow().downcast_ref::<Group>().unwrap().uuid
225 ));
226 Times::now()
227 }
228 };
229 if group_last_modification < deletion_time {
230 with_node_mut::<Group, _, _>(&parent_group, |pg| pg.remove_node(deleted_uuid)).unwrap()?;
231 log.events.push(MergeEvent {
232 event_type: MergeEventType::GroupDeleted,
233 node_uuid: deleted_uuid,
234 });
235 new_deleted_objects.insert(deleted_uuid, Some(deletion_time));
236 }
237 }
238 self.deleted_objects = new_deleted_objects;
239 Ok(log)
240 }
241
242 pub(crate) fn find_node_location(root: &NodePtr, id: Uuid) -> Option<Vec<Uuid>> {
243 for node in &group_get_children(root).unwrap_or_default() {
246 let node_uuid = node.borrow().get_uuid();
247 if node_is_entry(node) {
248 if node_uuid == id {
249 return Some(vec![]);
252 }
253 } else if node_is_group(node) {
254 if node_uuid == id {
255 return Some(vec![]);
258 }
259 #[allow(unused_mut)]
260 if let Some(mut location) = Group::find_node_location(node, id) {
261 return Some(location);
264 }
265 }
266 }
267 None
268 }
269
270 fn merge_group(&self, current_group_path: &[Uuid], current_group: &NodePtr, is_in_deleted_group: bool) -> Result<MergeLog, MergeError> {
271 let mut log = MergeLog::default();
272 if let Some(destination_group_location) = Self::find_node_location(&self.root, current_group.borrow().get_uuid()) {
273 let mut destination_group_path = destination_group_location.clone();
274 destination_group_path.push(current_group.borrow().get_uuid());
275 let destination_group =
276 Group::find_group(&self.root, &destination_group_path).ok_or(MergeError::FindGroupError(destination_group_path))?;
277 let group_update_merge_events = Group::merge_with(&destination_group, current_group)?;
278 log.append(&group_update_merge_events);
279 }
280 for other_entry in &with_node::<Group, _, _>(current_group, |g| g.entries()).unwrap() {
281 let other_entry_uuid = other_entry.borrow().get_uuid();
282 let destination_entry_location = Self::find_node_location(&self.root, other_entry_uuid);
284 if let Some(destination_entry_location) = destination_entry_location {
286 let mut existing_entry_location = destination_entry_location.clone();
287 existing_entry_location.push(other_entry_uuid);
288 let existing_entry = Group::find_entry(&self.root, &existing_entry_location)
291 .ok_or(MergeError::FindEntryError(existing_entry_location.clone()))?
292 .borrow()
293 .duplicate();
294 if current_group_path.last() != destination_entry_location.last() && !is_in_deleted_group {
297 let source_location_changed_time =
298 match with_node::<Entry, _, _>(other_entry, |e| e.get_times().get_location_changed()).unwrap() {
299 Some(t) => t,
300 None => {
301 log.warnings
302 .push(format!("Entry {other_entry_uuid} did not have a location updated timestamp"));
303 Times::epoch()
304 }
305 };
306 let destination_location_changed =
307 match with_node::<Entry, _, _>(&existing_entry, |e| e.get_times().get_location_changed()).unwrap() {
308 Some(t) => t,
309 None => {
310 log.warnings
311 .push(format!("Entry {other_entry_uuid} did not have a location updated timestamp"));
312 Times::now()
313 }
314 };
315 if source_location_changed_time > destination_location_changed {
316 log.events.push(MergeEvent {
317 event_type: MergeEventType::EntryLocationUpdated,
318 node_uuid: other_entry_uuid,
319 });
320 self.relocate_node(
321 other_entry_uuid,
322 &destination_entry_location,
323 current_group_path,
324 source_location_changed_time,
325 )?;
326 existing_entry_location = current_group_path.to_owned();
329 existing_entry_location.push(other_entry_uuid);
330 with_node_mut::<Entry, _, _>(&existing_entry, |e| {
331 e.get_times_mut().set_location_changed(Some(source_location_changed_time));
332 });
333 }
334 }
335 if !has_diverged_from(&existing_entry, other_entry) {
336 continue;
337 }
338 let (merged_entry, entry_merge_log) = Entry::merge(&existing_entry, other_entry)?;
341 let merged_entry = match merged_entry {
342 Some(m) => m,
343 None => continue,
344 };
345 if node_is_equals_to(&existing_entry, &merged_entry) {
346 continue;
347 }
348 let existing_entry =
349 Group::find_entry(&self.root, &existing_entry_location).ok_or(MergeError::FindEntryError(existing_entry_location))?;
350 with_node_mut::<Entry, _, _>(&existing_entry, |e| e.replaced_with(&merged_entry)).unwrap();
352 log.events.push(MergeEvent {
353 event_type: MergeEventType::EntryUpdated,
354 node_uuid: merged_entry.borrow().get_uuid(),
355 });
356 log.append(&entry_merge_log);
357 continue;
358 }
359 if self.deleted_objects.contains_key(&other_entry_uuid) {
360 continue;
361 }
362 if is_in_deleted_group {
364 continue;
365 }
366 let new_entry = other_entry.borrow().duplicate();
369 let new_entry_parent_group =
370 Group::find_group(&self.root, current_group_path).ok_or(MergeError::FindGroupError(current_group_path.to_owned()))?;
371
372 group_add_child(&new_entry_parent_group, new_entry.clone(), 0).unwrap();
374 log.events.push(MergeEvent {
376 event_type: MergeEventType::EntryCreated,
377 node_uuid: new_entry.borrow().get_uuid(),
378 });
379 }
380 for other_group in ¤t_group.borrow().downcast_ref::<Group>().unwrap().groups() {
381 let mut new_group_location = current_group_path.to_owned();
382 let other_group_uuid = other_group.borrow().get_uuid();
383 new_group_location.push(other_group_uuid);
384 if self.deleted_objects.contains_key(&other_group_uuid) || is_in_deleted_group {
385 let new_merge_log = self.merge_group(&new_group_location, other_group, true)?;
386 log.append(&new_merge_log);
387 continue;
388 }
389 let destination_group_location = Self::find_node_location(&self.root, other_group_uuid);
390 if let Some(destination_group_location) = &destination_group_location {
392 if current_group_path != destination_group_location {
393 let mut existing_group_location = destination_group_location.clone();
394 existing_group_location.push(other_group_uuid);
395 let existing_group = Group::find_group(&self.root, &existing_group_location)
398 .ok_or(MergeError::FindGroupError(existing_group_location))?;
399 let existing_group_location_changed =
400 match with_node::<Group, _, _>(&existing_group, |g| g.get_times().get_location_changed()).unwrap() {
401 Some(t) => t,
402 None => {
403 let uuid = existing_group.borrow().get_uuid();
404 log.warnings.push(format!("Entry {uuid} did not have a location changed timestamp"));
405 Times::now()
406 }
407 };
408 let other_group_location_changed =
409 match with_node::<Group, _, _>(other_group, |g| g.get_times().get_location_changed()).unwrap() {
410 Some(t) => t,
411 None => {
412 log.warnings
413 .push(format!("Entry {other_group_uuid} did not have a location changed timestamp"));
414 Times::epoch()
415 }
416 };
417 if existing_group_location_changed < other_group_location_changed {
419 self.relocate_node(
420 other_group_uuid,
421 destination_group_location,
422 current_group_path,
423 other_group_location_changed,
424 )?;
425 log.events.push(MergeEvent {
426 event_type: MergeEventType::GroupLocationUpdated,
427 node_uuid: other_group_uuid,
428 });
429 let new_merge_log = self.merge_group(&new_group_location, other_group, is_in_deleted_group)?;
430 log.append(&new_merge_log);
431 continue;
432 }
433 }
434 let new_merge_log = self.merge_group(&new_group_location, other_group, is_in_deleted_group)?;
437 log.append(&new_merge_log);
438 continue;
439 }
440 let new_group = other_group.borrow().duplicate();
443 with_node_mut::<Group, _, _>(&new_group, |g| g.reset_children(vec![])).unwrap();
445 log.events.push(MergeEvent {
446 event_type: MergeEventType::GroupCreated,
447 node_uuid: new_group.borrow().get_uuid(),
448 });
449 let new_group_parent_group =
450 Group::find_group(&self.root, current_group_path).ok_or(MergeError::FindGroupError(current_group_path.to_owned()))?;
451 with_node_mut::<Group, _, _>(&new_group_parent_group, |g| g.add_child(new_group, 0)).unwrap();
452 let new_merge_log = self.merge_group(&new_group_location, other_group, is_in_deleted_group)?;
453 log.append(&new_merge_log);
454 }
455 Ok(log)
456 }
457
458 fn relocate_node(
459 &self,
460 node_uuid: Uuid,
461 from: &[Uuid],
462 to: &[Uuid],
463 new_location_changed_timestamp: NaiveDateTime,
464 ) -> Result<(), MergeError> {
465 let source_group = Group::find_group(&self.root, from).ok_or(MergeError::FindGroupError(from.to_owned()))?;
466 let relocated_node = with_node_mut::<Group, _, _>(&source_group, |s| s.remove_node(node_uuid)).unwrap()?;
467 relocated_node
468 .borrow_mut()
469 .get_times_mut()
470 .set_location_changed(Some(new_location_changed_timestamp));
471
472 let destination_group = Group::find_group(&self.root, to).ok_or(MergeError::FindGroupError(to.to_owned()))?;
473 group_add_child(&destination_group, relocated_node, 0).unwrap();
474 Ok(())
475 }
476}
477
478pub(crate) fn has_diverged_from(node: &NodePtr, other_node: &NodePtr) -> bool {
479 if let Some(entry) = node.borrow().downcast_ref::<Entry>()
480 && let Some(other_entry) = other_node.borrow().downcast_ref::<Entry>()
481 {
482 return entry._has_diverged_from(other_entry);
483 }
484 if let Some(group) = node.borrow().downcast_ref::<Group>()
485 && let Some(other_group) = other_node.borrow().downcast_ref::<Group>()
486 {
487 return group._has_diverged_from(other_group);
488 }
489 false
490}
491
492impl Group {
493 pub(crate) fn find_group(group: &NodePtr, path: &[Uuid]) -> Option<NodePtr> {
494 let path: Vec<String> = path.iter().map(|p| p.to_string()).collect();
495 let node_ref = Self::get_by_uuid(group, &path)?;
496 if node_is_group(&node_ref) { Some(node_ref) } else { None }
497 }
498
499 pub(crate) fn find_entry(group: &NodePtr, path: &[Uuid]) -> Option<NodePtr> {
500 let path: Vec<String> = path.iter().map(|p| p.to_string()).collect();
501 let node_ref = Self::get_by_uuid(group, &path)?;
502 if node_is_entry(&node_ref) { Some(node_ref) } else { None }
503 }
504
505 pub(crate) fn remove_node(&mut self, uuid: Uuid) -> Result<NodePtr, MergeError> {
506 let mut removed_node = None;
507 self.children.retain(|c| {
508 if c.borrow().get_uuid() == uuid {
509 removed_node = Some(NodePtr::from(c));
510 return false;
511 }
512 true
513 });
514
515 let title = self.get_title().unwrap_or("No title").to_string();
516 let node = removed_node.ok_or(MergeError::GenericError(format!("Could not find node {uuid} in group \"{title}\"")))?;
517 Ok(node)
518 }
519
520 pub(crate) fn find_node_location(parent: &NodePtr, id: Uuid) -> Option<Vec<Uuid>> {
521 let parent_uuid = parent.borrow().get_uuid();
522 let mut current_location = vec![parent_uuid];
523 for node in &group_get_children(parent).unwrap_or_default() {
524 let node_uuid = node.borrow().get_uuid();
525 if node_is_entry(node) {
526 if node_uuid == id {
527 return Some(current_location);
529 }
530 } else if node_is_group(node) {
531 if node_uuid == id {
532 return Some(current_location);
534 }
535 if let Some(mut location) = Self::find_node_location(node, id) {
536 current_location.append(&mut location);
537 return Some(current_location);
538 }
539 }
540 }
541 None
542 }
543
544 pub(crate) fn merge_with(group: &NodePtr, other: &NodePtr) -> Result<MergeLog, MergeError> {
545 let mut log = MergeLog::default();
546
547 let group_uuid = group.borrow().get_uuid();
548
549 let other = other.borrow();
550 let other = other
551 .downcast_ref::<Group>()
552 .ok_or(MergeError::GenericError("Could not downcast node to group".to_string()))?;
553 let source_last_modification = match other.times.get_last_modification() {
554 Some(t) => t,
555 None => {
556 log.warnings
557 .push(format!("Group {group_uuid} did not have a last modification timestamp"));
558 Times::epoch()
559 }
560 };
561 let destination_last_modification = match group.borrow().get_times().get_last_modification() {
562 Some(t) => t,
563 None => {
564 log.warnings
565 .push(format!("Group {group_uuid} did not have a last modification timestamp"));
566 Times::now()
567 }
568 };
569 if destination_last_modification == source_last_modification {
570 if group.borrow().downcast_ref::<Group>().unwrap()._has_diverged_from(other) {
571 return Err(MergeError::GroupModificationTimeNotUpdated(other.uuid.to_string()));
575 }
576 return Ok(log);
577 }
578 if destination_last_modification > source_last_modification {
579 return Ok(log);
580 }
581 with_node_mut::<Group, _, _>(group, |group| {
582 group.name = other.name.clone();
583 group.notes = other.notes.clone();
584 group.icon = other.icon;
585 group.custom_data = other.custom_data.clone();
586 let current_times = group.times.clone();
588 group.times = other.times.clone();
589 if let Some(t) = current_times.get_location_changed() {
590 group.times.set_location_changed(Some(t));
591 }
592 group.is_expanded = other.is_expanded;
593 group.default_autotype_sequence = other.default_autotype_sequence.clone();
594 group.enable_autotype = other.enable_autotype;
595 group.enable_searching = other.enable_searching;
596 group.last_top_visible_entry = other.last_top_visible_entry;
597 })
598 .unwrap();
599 log.events.push(MergeEvent {
600 event_type: MergeEventType::GroupUpdated,
601 node_uuid: group_uuid,
602 });
603 Ok(log)
604 }
605
606 pub(crate) fn _has_diverged_from(&self, other: &Group) -> bool {
607 let new_times = Times::new();
608 let mut self_purged = self.clone();
609 self_purged.times = new_times.clone();
610 self_purged.children = vec![];
611 let mut other_purged = other.clone();
612 other_purged.times = new_times.clone();
613 other_purged.children = vec![];
614 !self_purged.eq(&other_purged)
615 }
616
617 fn replace_entry(root: &NodePtr, entry: &NodePtr) -> bool {
618 let uuid = entry.borrow().get_uuid();
619 if let Some(target_entry) = search_node_by_uuid_with_specific_type::<Entry>(root, uuid) {
620 return with_node_mut::<Entry, _, _>(&target_entry, |e| e.replaced_with(entry)).unwrap_or(false);
621 }
622 false
623 }
624
625 pub(crate) fn has_group(&self, uuid: Uuid) -> bool {
626 self.children.iter().any(|n| n.borrow().get_uuid() == uuid && node_is_group(n))
627 }
628
629 fn get_or_create_group(group: &NodePtr, location: &[Uuid], create_groups: bool) -> crate::Result<NodePtr> {
630 if location.is_empty() {
631 return Err("Empty location.".into());
632 }
633
634 let mut remaining_location = location.to_owned();
635 remaining_location.remove(0);
636
637 if remaining_location.is_empty() {
638 return Ok(group.clone());
639 }
640
641 let next_location = &remaining_location[0];
642 let mut next_location_uuid = *next_location;
643
644 if !with_node::<Group, _, _>(group, |g| g.has_group(next_location_uuid)).unwrap() && create_groups {
645 let mut current_group: Option<NodePtr> = None;
646 for i in (0..(remaining_location.len())).rev() {
647 let mut new_group = Group::new(&remaining_location[i].to_string());
648 new_group.set_uuid(remaining_location[i]);
649 if let Some(current_group) = current_group {
650 let count = group_get_children(group).map(|c| c.len()).unwrap_or(0);
651 new_group.add_child(current_group, count);
652 }
653 current_group = Some(rc_refcell_node(new_group));
654 }
655
656 if let Some(current_group) = current_group {
657 next_location_uuid = current_group.borrow().get_uuid();
658 let count = group_get_children(group).map_or(0, |c| c.len());
659 group_add_child(group, current_group, count)?;
660 } else {
661 return Err("Could not create group.".into());
662 }
663 }
664
665 let mut target = None;
666 for node in group_get_children(group).unwrap_or_default().iter() {
667 if node_is_group(node) && node.borrow().get_uuid() == next_location_uuid {
668 target = Some(node.clone());
669 break;
670 }
671 }
672
673 match &target {
674 Some(target) => Self::get_or_create_group(target, &remaining_location, create_groups),
675 None => Err("The group was not found.".into()),
676 }
677 }
678
679 pub(crate) fn insert_entry(group: &NodePtr, entry: NodePtr, location: &[Uuid]) -> crate::Result<()> {
680 let group = Self::get_or_create_group(group, location, true)?;
681 with_node_mut::<Group, _, _>(&group, |g| {
682 let count = g.children.len();
683 g.add_child(entry, count);
684 Ok::<(), crate::Error>(())
685 })
686 .ok_or("Could not add entry")??;
687 Ok(())
688 }
689
690 pub(crate) fn remove_entry(group: &NodePtr, uuid: Uuid, location: &[Uuid]) -> crate::Result<NodePtr> {
691 let group = Self::get_or_create_group(group, location, false)?;
692
693 let mut removed_entry: Option<NodePtr> = None;
694 let mut new_nodes: Vec<NodePtr> = vec![];
695 println!(
696 "Searching for entry {} in {}",
697 uuid,
698 group.borrow().get_title().unwrap_or("No title")
699 );
700
701 with_node::<Group, _, _>(&group, |g| {
702 for node in g.children.iter() {
703 if node_is_entry(node) {
704 let node_uuid = node.borrow().get_uuid();
705 println!("Saw entry {node_uuid}");
706 if node_uuid != uuid {
707 new_nodes.push(NodePtr::from(node));
708 continue;
709 }
710 removed_entry = Some(NodePtr::from(node));
711 } else if node_is_group(node) {
712 new_nodes.push(NodePtr::from(node));
713 }
714 }
715 });
716
717 if let Some(entry) = removed_entry {
718 with_node_mut::<Group, _, _>(&group, |g| g.reset_children(new_nodes)).ok_or("Could not reset children")?;
719 Ok(entry)
720 } else {
721 let title = group.borrow().get_title().unwrap_or("No title").to_string();
722 Err(format!("Could not find entry {uuid} in group \"{title}\".").into())
723 }
724 }
725
726 pub(crate) fn find_entry_location(&self, uuid: Uuid) -> Option<Vec<Uuid>> {
727 let mut current_location = vec![self.uuid];
728 for node in &self.children {
729 if node_is_entry(node) {
730 if node.borrow().get_uuid() == uuid {
731 return Some(current_location);
732 }
733 } else if let Some(g) = node.borrow().downcast_ref::<Group>()
734 && let Some(mut location) = g.find_entry_location(uuid)
735 {
736 current_location.append(&mut location);
737 return Some(current_location);
738 }
739 }
740 None
741 }
742
743 pub(crate) fn add_entry(parent: &NodePtr, entry: NodePtr, location: &[Uuid]) -> crate::Result<()> {
744 if location.is_empty() {
745 panic!("TODO handle this with a Response.");
746 }
747
748 let mut remaining_location = location.to_owned();
749 remaining_location.remove(0);
750
751 if remaining_location.is_empty() {
752 with_node_mut::<Group, _, _>(parent, |g| {
753 let count = g.children.len();
754 g.add_child(entry, count);
755 Ok::<(), crate::Error>(())
756 })
757 .ok_or("Could not add entry")??;
758 return Ok(());
759 }
760
761 let next_location = remaining_location[0];
762
763 println!("Searching for group {next_location:?}");
764 for node in group_get_children(parent).unwrap_or_default() {
765 if node_is_group(&node) {
766 if node.borrow().get_uuid() != next_location {
767 continue;
768 }
769 Self::add_entry(&node, entry, &remaining_location)?;
770 return Ok(());
771 }
772 }
773
774 let new_group = rc_refcell_node(Group::new(&next_location.to_string()));
776 new_group.borrow_mut().set_uuid(next_location);
777 Self::add_entry(&new_group, entry, &remaining_location)?;
778 let count = group_get_children(parent).map_or(0, |c| c.len());
779 group_add_child(parent, new_group, count)?;
780 Ok(())
781 }
782
783 #[allow(clippy::too_many_lines)]
785 pub fn merge(root: &NodePtr, other_group: &NodePtr) -> crate::Result<MergeLog> {
786 let mut log = MergeLog::default();
787
788 let other_entries = with_node::<Group, _, _>(other_group, |g| Ok(g.get_all_entries(&[])))
789 .unwrap_or(Err(crate::Error::from("Could not downcast other group to group")))?;
790
791 for (entry, entry_location) in &other_entries {
793 let entry_uuid = entry.borrow().get_uuid();
794 let the_entry = search_node_by_uuid_with_specific_type::<Entry>(root, entry_uuid);
795
796 let existing_entry = match the_entry {
797 Some(e) => e,
798 None => continue,
799 };
800
801 let the_entry_location = with_node::<Group, _, _>(root, |g| Ok(g.find_entry_location(entry_uuid)))
802 .unwrap_or(Err("Could not downcast root to group"))?;
803
804 let existing_entry_location = match the_entry_location {
805 Some(l) => l,
806 None => continue,
807 };
808
809 let source_location_changed_time = if let Some(t) = entry.borrow().get_times().get_location_changed() {
810 t
811 } else {
812 log.warnings
813 .push(format!("Entry {entry_uuid} did not have a location updated timestamp"));
814 Times::epoch()
815 };
816 let destination_location_changed = if let Some(t) = existing_entry.borrow().get_times().get_location_changed() {
817 t
818 } else {
819 log.warnings
820 .push(format!("Entry {entry_uuid} did not have a location updated timestamp"));
821 Times::now()
822 };
823 if source_location_changed_time > destination_location_changed {
824 log.events.push(MergeEvent {
825 event_type: MergeEventType::EntryLocationUpdated,
826 node_uuid: entry_uuid,
827 });
828 Self::remove_entry(root, entry_uuid, &existing_entry_location)?;
829 Self::insert_entry(root, entry.borrow().duplicate(), entry_location)?;
830 }
831 }
832
833 for (entry, entry_location) in &other_entries {
835 let entry_uuid = entry.borrow().get_uuid();
836 let the_entry = search_node_by_uuid_with_specific_type::<Entry>(root, entry_uuid);
837 if let Some(existing_entry) = the_entry {
838 if node_is_equals_to(&existing_entry, entry) {
839 continue;
840 }
841
842 let source_last_modification = if let Some(t) = entry.borrow().get_times().get_last_modification() {
843 t
844 } else {
845 log.warnings
846 .push(format!("Entry {entry_uuid} did not have a last modification timestamp"));
847 Times::epoch()
848 };
849 let destination_last_modification = if let Some(t) = existing_entry.borrow().get_times().get_last_modification() {
850 t
851 } else {
852 log.warnings
853 .push(format!("Entry {entry_uuid} did not have a last modification timestamp"));
854 Times::now()
855 };
856
857 if destination_last_modification == source_last_modification {
858 if !node_is_equals_to(&existing_entry, entry) {
859 return Err("Entries have the same modification time but are not the same!".into());
863 }
864 continue;
865 }
866
867 let (merged_entry, entry_merge_log) = if destination_last_modification > source_last_modification {
868 Entry::merge(&existing_entry, entry)?
869 } else {
870 Entry::merge(entry, &existing_entry)?
871 };
872 let Some(merged_entry) = merged_entry else {
873 continue;
874 };
875 if node_is_equals_to(&existing_entry, &merged_entry) {
877 continue;
878 }
879
880 Group::replace_entry(root, &merged_entry);
881
882 log.events.push(MergeEvent {
883 event_type: MergeEventType::EntryUpdated,
884 node_uuid: merged_entry.borrow().get_uuid(),
885 });
886 log = log.merge_with(&entry_merge_log);
887 } else {
888 Self::add_entry(root, entry.borrow().duplicate(), entry_location)?;
889 log.events.push(MergeEvent {
891 event_type: MergeEventType::EntryCreated,
892 node_uuid: entry.borrow().get_uuid(),
893 });
894 }
895 }
896
897 Ok(log)
899 }
900
901 pub(crate) fn get_all_entries(&self, current_location: &[Uuid]) -> Vec<(NodePtr, Vec<Uuid>)> {
904 let mut response: Vec<(NodePtr, Vec<Uuid>)> = vec![];
905 let mut new_location = current_location.to_owned();
906 new_location.push(self.uuid);
907
908 for node in &self.children {
909 if node_is_entry(node) {
910 response.push((node.into(), new_location.clone()));
911 }
912 with_node::<Group, _, _>(node, |g| {
913 let mut new_entries = g.get_all_entries(&new_location);
914 response.append(&mut new_entries);
915 });
916 }
917 response
918 }
919}
920
921#[cfg(test)]
922pub fn entry_set_field_and_commit(entry: &NodePtr, field_name: &str, field_value: &str) -> crate::Result<()> {
923 with_node_mut::<Entry, _, _>(entry, |entry| {
924 entry.set_field_and_commit(field_name, field_value);
925 Ok(())
926 })
927 .unwrap_or(Err("node is not an Entry.".to_string()))?;
928 Ok(())
929}
930
931impl Entry {
932 pub(crate) fn merge(entry: &NodePtr, other: &NodePtr) -> Result<(Option<NodePtr>, MergeLog), MergeError> {
933 let mut log = MergeLog::default();
934 let source_last_modification = match with_node::<Entry, _, _>(other, |e| e.get_times().get_last_modification()).unwrap() {
935 Some(t) => t,
936 None => {
937 let info = format!("Entry {} did not have a last modification timestamp", other.borrow().get_uuid());
938 log.warnings.push(info);
939 Times::epoch()
940 }
941 };
942
943 let destination_last_modification = match with_node::<Entry, _, _>(entry, |e| e.get_times().get_last_modification()).unwrap() {
944 Some(t) => t,
945 None => {
946 let info = format!("Entry {} did not have a last modification timestamp", entry.borrow().get_uuid());
947 log.warnings.push(info);
948 Times::epoch()
949 }
950 };
951
952 if destination_last_modification == source_last_modification {
953 if !crate::db::merge::has_diverged_from(entry, other) {
954 return Err(MergeError::EntryModificationTimeNotUpdated(other.borrow().get_uuid().to_string()));
956 }
957 return Ok((None, log));
958 }
959 let (mut merged_entry, entry_merge_log) = with_node::<Entry, _, _>(entry, |entry| {
960 with_node::<Entry, _, _>(other, |other| {
961 if destination_last_modification > source_last_modification {
962 entry.merge_history(other)
963 } else {
964 other.merge_history(entry)
965 }
966 })
967 .unwrap()
968 })
969 .unwrap()?;
970
971 if let location_changed_timestamp @ Some(_) = entry.borrow().get_times().get_location_changed() {
972 merged_entry.get_times_mut().set_location_changed(location_changed_timestamp);
973 }
974 Ok((Some(rc_refcell_node(merged_entry)), entry_merge_log))
975 }
976
977 pub(crate) fn merge_history(&self, other: &Entry) -> Result<(Entry, MergeLog), MergeError> {
978 let mut log = MergeLog::default();
979 let mut source_history = match &other.history {
980 Some(h) => h.clone(),
981 None => {
982 log.warnings
983 .push(format!("Entry {} from source database had no history.", self.uuid));
984 History::default()
985 }
986 };
987 let mut destination_history = match &self.history {
988 Some(h) => h.clone(),
989 None => {
990 log.warnings
991 .push(format!("Entry {} from destination database had no history.", self.uuid));
992 History::default()
993 }
994 };
995 let mut response = self.clone();
996 if other.has_uncommited_changes() {
997 log.warnings
998 .push(format!("Entry {} from source database has uncommitted changes.", self.uuid));
999 source_history.add_entry(other.clone());
1000 }
1001 let history_merge_log = destination_history.merge_with(&source_history)?;
1004 response.history = Some(destination_history);
1005 Ok((response, log.merge_with(&history_merge_log)))
1006 }
1007
1008 pub(crate) fn _has_diverged_from(&self, other_entry: &Entry) -> bool {
1010 let new_times = Times::default();
1011
1012 let mut self_without_times = self.clone();
1013 self_without_times.times = new_times.clone();
1014
1015 let mut other_without_times = other_entry.clone();
1016 other_without_times.times = new_times;
1017 !self_without_times.eq(&other_without_times)
1018 }
1019
1020 #[cfg(test)]
1055 pub(crate) fn set_field_and_commit(&mut self, field_name: &str, field_value: &str) {
1056 self.set_unprotected_field_pair(field_name, Some(field_value));
1057 std::thread::sleep(std::time::Duration::from_secs(1));
1058 self.update_history();
1059 }
1060
1061 pub(crate) fn replaced_with(&mut self, other: &NodePtr) -> bool {
1062 let mut success = false;
1063 with_node::<Entry, _, _>(other, |other| {
1064 self.uuid = other.uuid;
1065 self.fields = other.fields.clone();
1066 self.autotype = other.autotype.clone();
1067 self.tags = other.tags.clone();
1068 self.times = other.times.clone();
1069 self.custom_data = other.custom_data.clone();
1070 self.icon = other.icon;
1071 self.foreground_color = other.foreground_color;
1072 self.background_color = other.background_color;
1073 self.override_url = other.override_url.clone();
1074 self.quality_check = other.quality_check;
1075 self.history = other.history.clone();
1076 success = true;
1078 });
1079 success
1080 }
1081}
1082
1083impl History {
1084 #[cfg(test)]
1087 pub(crate) fn is_ordered(&self) -> bool {
1088 let mut last_modification_time: Option<chrono::NaiveDateTime> = None;
1089 for entry in &self.entries {
1090 if last_modification_time.is_none() {
1091 last_modification_time = entry.times.get_last_modification();
1092 }
1093
1094 let entry_modification_time = entry.times.get_last_modification().unwrap();
1095 if last_modification_time.unwrap() < entry_modification_time {
1097 return false;
1098 }
1099 last_modification_time = Some(entry_modification_time);
1100 }
1101 true
1102 }
1103
1104 pub(crate) fn merge_with(&mut self, other: &History) -> Result<MergeLog, MergeError> {
1106 let mut log = MergeLog::default();
1107 let mut new_history_entries: HashMap<chrono::NaiveDateTime, Entry> = HashMap::new();
1108
1109 for history_entry in &self.entries {
1110 let modification_time = history_entry.times.get_last_modification().unwrap();
1111 if new_history_entries.contains_key(&modification_time) {
1112 return Err(MergeError::DuplicateHistoryEntries(
1113 modification_time.to_string(),
1114 history_entry.uuid.to_string(),
1115 ));
1116 }
1117 new_history_entries.insert(modification_time, history_entry.clone());
1118 }
1119
1120 for history_entry in &other.entries {
1121 let modification_time = history_entry.times.get_last_modification().unwrap();
1122 let existing_history_entry = new_history_entries.get(&modification_time);
1123 if let Some(existing_history_entry) = existing_history_entry {
1124 if existing_history_entry._has_diverged_from(history_entry) {
1125 log.warnings.push(format!(
1126 "History entries for {} have the same modification timestamp but were not the same.",
1127 existing_history_entry.uuid
1128 ));
1129 }
1130 } else {
1131 new_history_entries.insert(modification_time, history_entry.clone());
1132 }
1133 }
1134
1135 let mut all_modification_times: Vec<&chrono::NaiveDateTime> = new_history_entries.keys().collect();
1136 all_modification_times.sort();
1137 all_modification_times.reverse();
1138 let mut new_entries: Vec<Entry> = vec![];
1139 for modification_time in &all_modification_times {
1140 new_entries.push(new_history_entries.get(modification_time).unwrap().clone());
1141 }
1142 self.entries = new_entries;
1143 Ok(log)
1144 }
1145}
1146
1147#[cfg(test)]
1148mod merge_tests {
1149 use std::{thread, time};
1150 use uuid::Uuid;
1151
1152 use crate::db::{
1153 CustomIcon, Database, Entry, Group, Icon, Node, NodePtr, Times, group_add_child, group_get_children, node_is_group,
1154 rc_refcell_node, search_node_by_uuid_with_specific_type, with_node, with_node_mut,
1155 };
1156
1157 fn get_entry(db: &Database, path: &[&str]) -> NodePtr {
1158 Group::get(&db.root, path).unwrap()
1159 }
1160
1161 fn get_group(db: &Database, path: &[&str]) -> NodePtr {
1162 Group::get(&db.root, path).unwrap()
1163 }
1164
1165 fn get_all_groups(group: &NodePtr) -> Vec<NodePtr> {
1166 let mut response: Vec<NodePtr> = vec![];
1167 for node in group_get_children(group).unwrap() {
1168 if node_is_group(&node) {
1169 let mut new_groups = get_all_groups(&node);
1170 response.append(&mut new_groups);
1171 response.push(node);
1172 }
1173 }
1174
1175 response
1176 }
1177
1178 fn get_all_entries(group: &NodePtr) -> Vec<NodePtr> {
1179 let mut response: Vec<NodePtr> = vec![];
1180 for node in group_get_children(group).unwrap() {
1181 if node_is_group(&node) {
1182 let mut new_entries = get_all_entries(&node);
1183 response.append(&mut new_entries);
1184 } else {
1185 response.push(node);
1186 }
1187 }
1188 response
1189 }
1190
1191 const ROOT_GROUP_ID: &str = "00000000-0000-0000-0000-000000000001";
1192 const GROUP1_ID: &str = "00000000-0000-0000-0000-000000000002";
1193 const GROUP2_ID: &str = "00000000-0000-0000-0000-000000000003";
1194 const SUBGROUP1_ID: &str = "00000000-0000-0000-0000-000000000004";
1195 const SUBGROUP2_ID: &str = "00000000-0000-0000-0000-000000000005";
1196
1197 const ENTRY1_ID: &str = "00000000-0000-0000-0000-000000000006";
1198 const ENTRY2_ID: &str = "00000000-0000-0000-0000-000000000007";
1199
1200 fn create_test_database() -> Database {
1201 let mut db = Database::new(Default::default());
1202 let mut root_group = Group::new("root");
1203 root_group.uuid = Uuid::parse_str(ROOT_GROUP_ID).unwrap();
1204
1205 let mut group1 = Group::new("group1");
1206 group1.uuid = Uuid::parse_str(GROUP1_ID).unwrap();
1207 let mut group2 = Group::new("group2");
1208 group2.uuid = Uuid::parse_str(GROUP2_ID).unwrap();
1209
1210 let mut subgroup1 = Group::new("subgroup1");
1211 subgroup1.uuid = Uuid::parse_str(SUBGROUP1_ID).unwrap();
1212 let mut subgroup2 = Group::new("subgroup2");
1213 subgroup2.uuid = Uuid::parse_str(SUBGROUP2_ID).unwrap();
1214
1215 let mut entry1 = Entry::default();
1217 entry1.set_uuid(Uuid::parse_str(ENTRY1_ID).unwrap());
1218 entry1.set_field_and_commit("Title", "entry1");
1219 root_group.add_child(rc_refcell_node(entry1), 0);
1220
1221 let mut entry2 = Entry::default();
1223 entry2.set_uuid(Uuid::parse_str(ENTRY2_ID).unwrap());
1224 entry2.set_field_and_commit("Title", "entry2");
1225 subgroup1.add_child(rc_refcell_node(entry2), 0);
1226
1227 group1.add_child(rc_refcell_node(subgroup1), 0);
1228 group2.add_child(rc_refcell_node(subgroup2), 0);
1229
1230 root_group.add_child(rc_refcell_node(group1), 1);
1231 root_group.add_child(rc_refcell_node(group2), 2);
1232
1233 db.root = rc_refcell_node(root_group).into();
1234 db
1235 }
1236
1237 #[test]
1238 fn test_idempotence() {
1239 let mut destination_db = create_test_database();
1240 let source_db = destination_db.clone();
1241
1242 let entry_count_before = get_all_entries(&destination_db.root).len();
1243 let group_count_before = get_all_groups(&destination_db.root).len();
1244
1245 let merge_result = destination_db.merge(&source_db).unwrap();
1246 assert_eq!(merge_result.warnings.len(), 0);
1247 assert_eq!(merge_result.events.len(), 0);
1248 assert_eq!(group_get_children(&destination_db.root).unwrap().len(), 3);
1249
1250 assert_eq!(destination_db, source_db);
1253
1254 let entry_count_after = get_all_entries(&destination_db.root).len();
1255 let group_count_after = get_all_groups(&destination_db.root).len();
1256 assert_eq!(entry_count_after, entry_count_before);
1257 assert_eq!(group_count_after, group_count_before);
1258
1259 let entry = get_all_entries(&destination_db.root)[0].clone();
1260 with_node_mut::<Entry, _, _>(&entry, |entry| {
1261 entry.set_field_and_commit("Title", "entry1_updated");
1262 });
1263
1264 let merge_result = destination_db.merge(&source_db).unwrap();
1265 assert_eq!(merge_result.warnings.len(), 0);
1266 assert_eq!(merge_result.events.len(), 0);
1267 let destination_db_just_after_merge = destination_db.clone();
1268
1269 let merge_result = destination_db.merge(&source_db).unwrap();
1270 assert_eq!(merge_result.warnings.len(), 0);
1271 assert_eq!(merge_result.events.len(), 0);
1272 assert_eq!(destination_db_just_after_merge, destination_db);
1275 }
1276
1277 #[test]
1278 fn test_add_new_entry() {
1279 let mut destination_db = create_test_database();
1280 let source_db = destination_db.clone();
1281
1282 let entry_count_before = get_all_entries(&destination_db.root).len();
1283 let group_count_before = get_all_groups(&destination_db.root).len();
1284
1285 let mut new_entry = Entry::default();
1286 new_entry.set_field_and_commit("Title", "new_entry");
1287 group_add_child(&source_db.root, rc_refcell_node(new_entry), 0).unwrap();
1288
1289 let merge_result = destination_db.merge(&source_db).unwrap();
1290 assert_eq!(merge_result.warnings.len(), 0);
1291 assert_eq!(merge_result.events.len(), 1);
1292
1293 let entry_count_after = get_all_entries(&destination_db.root).len();
1294 let group_count_after = get_all_groups(&destination_db.root).len();
1295 assert_eq!(entry_count_after, entry_count_before + 1);
1296 assert_eq!(group_count_after, group_count_before);
1297
1298 let root_entries = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap();
1299 assert_eq!(root_entries.len(), 2);
1300
1301 let new_entry = get_entry(&destination_db, &["new_entry"]);
1302 assert_eq!(new_entry.borrow().get_title().unwrap(), "new_entry".to_string());
1303
1304 let merge_result = destination_db.merge(&source_db).unwrap();
1306 assert_eq!(merge_result.warnings.len(), 0);
1307 assert_eq!(merge_result.events.len(), 0);
1308
1309 let entry_count_after = get_all_entries(&destination_db.root).len();
1310 let group_count_after = get_all_groups(&destination_db.root).len();
1311 assert_eq!(entry_count_after, entry_count_before + 1);
1312 assert_eq!(group_count_after, group_count_before);
1313 }
1314
1315 #[test]
1316 fn test_deleted_entry_in_destination() {
1317 let mut destination_db = create_test_database();
1318 let source_db = destination_db.clone();
1319
1320 let entry_count_before = get_all_entries(&destination_db.root).len();
1321 let group_count_before = get_all_groups(&destination_db.root).len();
1322
1323 let mut deleted_entry = Entry::default();
1324 let deleted_entry_uuid = deleted_entry.uuid;
1325 deleted_entry.set_field_and_commit("Title", "deleted_entry");
1326 group_add_child(&source_db.root, rc_refcell_node(deleted_entry), 0).unwrap();
1327
1328 destination_db.deleted_objects.insert(deleted_entry_uuid, Some(Times::now()));
1329
1330 let merge_result = destination_db.merge(&source_db).unwrap();
1331 assert_eq!(merge_result.warnings.len(), 0);
1332 assert_eq!(merge_result.events.len(), 0);
1333
1334 let entry_count_after = get_all_entries(&destination_db.root).len();
1335 let group_count_after = get_all_groups(&destination_db.root).len();
1336 assert_eq!(entry_count_after, entry_count_before);
1337 assert_eq!(group_count_after, group_count_before);
1338
1339 let new_entry = Group::find_node_location(&destination_db.root, deleted_entry_uuid);
1340 assert!(new_entry.is_none());
1341 }
1342
1343 #[test]
1344 fn test_updated_entry_under_deleted_group() {
1345 let mut destination_db = create_test_database();
1346 let source_db = destination_db.clone();
1347
1348 let mut modified_entry = Entry::default();
1349 modified_entry.set_field_and_commit("Title", "original_title");
1350 group_add_child(&destination_db.root, modified_entry.duplicate(), 0).unwrap();
1351
1352 let mut deleted_group = Group::new("deleted_group");
1353 let deleted_group_uuid = deleted_group.uuid;
1354 let modified_entry_uuid = modified_entry.uuid;
1355 modified_entry.set_field_and_commit("Title", "modified_title");
1356 deleted_group.add_child(rc_refcell_node(modified_entry), 0);
1357 group_add_child(&source_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1358
1359 let entry_count_before = get_all_entries(&destination_db.root).len();
1360 let group_count_before = get_all_groups(&destination_db.root).len();
1361
1362 destination_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1363
1364 let merge_result = destination_db.merge(&source_db).unwrap();
1365 assert_eq!(merge_result.warnings.len(), 0);
1366 assert_eq!(merge_result.events.len(), 1);
1367
1368 let entry_count_after = get_all_entries(&destination_db.root).len();
1369 let group_count_after = get_all_groups(&destination_db.root).len();
1370 assert_eq!(entry_count_after, entry_count_before);
1371 assert_eq!(group_count_after, group_count_before);
1372
1373 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1374 assert!(deleted_group.is_none());
1375
1376 let modified_entry_location = Group::find_node_location(&destination_db.root, modified_entry_uuid);
1377 assert!(modified_entry_location.is_some());
1378
1379 let modified_entry = Group::find_entry(&destination_db.root, &[modified_entry_uuid]).unwrap();
1380 assert_eq!(modified_entry.borrow().get_title(), Some("modified_title"));
1381 }
1382
1383 #[test]
1384 fn test_deleted_group_in_destination() {
1385 let mut destination_db = create_test_database();
1386 let source_db = destination_db.clone();
1387
1388 let entry_count_before = get_all_entries(&destination_db.root).len();
1389 let group_count_before = get_all_groups(&destination_db.root).len();
1390
1391 let deleted_group = Group::new("deleted_group");
1392 let deleted_group_uuid = deleted_group.uuid;
1393 group_add_child(&source_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1394
1395 destination_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1396
1397 let merge_result = destination_db.merge(&source_db).unwrap();
1398 assert_eq!(merge_result.warnings.len(), 0);
1399 assert_eq!(merge_result.events.len(), 0);
1400
1401 let entry_count_after = get_all_entries(&destination_db.root).len();
1402 let group_count_after = get_all_groups(&destination_db.root).len();
1403 assert_eq!(entry_count_after, entry_count_before);
1404 assert_eq!(group_count_after, group_count_before);
1405
1406 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1407 assert!(deleted_group.is_none());
1408 }
1409
1410 #[test]
1411 fn test_deleted_entry_in_source() {
1412 let mut destination_db = create_test_database();
1413 let mut source_db = destination_db.clone();
1414
1415 let mut deleted_entry = Entry::default();
1416 let deleted_entry_uuid = deleted_entry.uuid;
1417 deleted_entry.set_field_and_commit("Title", "deleted_entry");
1418 group_add_child(&destination_db.root, rc_refcell_node(deleted_entry), 0).unwrap();
1419
1420 let entry_count_before = get_all_entries(&destination_db.root).len();
1421 let group_count_before = get_all_groups(&destination_db.root).len();
1422
1423 thread::sleep(time::Duration::from_secs(1));
1424 source_db.deleted_objects.insert(deleted_entry_uuid, Some(Times::now()));
1425
1426 let merge_result = destination_db.merge(&source_db).unwrap();
1427 assert_eq!(merge_result.warnings.len(), 0);
1428 assert_eq!(merge_result.events.len(), 1);
1429
1430 let entry_count_after = get_all_entries(&destination_db.root).len();
1431 let group_count_after = get_all_groups(&destination_db.root).len();
1432 assert_eq!(entry_count_after, entry_count_before - 1);
1433 assert_eq!(group_count_after, group_count_before);
1434
1435 let new_entry = Group::find_node_location(&destination_db.root, deleted_entry_uuid);
1436 assert!(new_entry.is_none());
1437
1438 assert!(destination_db.deleted_objects.contains_key(&deleted_entry_uuid));
1439 }
1440
1441 #[test]
1442 fn test_deleted_group_in_source() {
1443 let mut destination_db = create_test_database();
1444 let mut source_db = destination_db.clone();
1445
1446 let deleted_group = Group::new("deleted_group");
1447 let deleted_group_uuid = deleted_group.uuid;
1448 group_add_child(&destination_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1449
1450 let entry_count_before = get_all_entries(&destination_db.root).len();
1451 let group_count_before = get_all_groups(&destination_db.root).len();
1452
1453 thread::sleep(time::Duration::from_secs(1));
1454 source_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1455
1456 let merge_result = destination_db.merge(&source_db).unwrap();
1457 assert_eq!(merge_result.warnings.len(), 0);
1458 assert_eq!(merge_result.events.len(), 1);
1459
1460 let entry_count_after = get_all_entries(&destination_db.root).len();
1461 let group_count_after = get_all_groups(&destination_db.root).len();
1462 assert_eq!(entry_count_after, entry_count_before);
1463 assert_eq!(group_count_after, group_count_before - 1);
1464
1465 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1466 assert!(deleted_group.is_none());
1467
1468 assert!(destination_db.deleted_objects.contains_key(&deleted_group_uuid));
1469 }
1470
1471 #[test]
1472 fn test_deleted_entry_in_source_modified_in_destination() {
1473 let mut destination_db = create_test_database();
1474 let mut source_db = destination_db.clone();
1475
1476 let deleted_entry_uuid = Uuid::new_v4();
1477
1478 thread::sleep(time::Duration::from_secs(1));
1479 source_db.deleted_objects.insert(deleted_entry_uuid, Some(Times::now()));
1480
1481 thread::sleep(time::Duration::from_secs(1));
1482 let mut deleted_entry = Entry::default();
1483 deleted_entry.set_uuid(deleted_entry_uuid);
1484 deleted_entry.set_field_and_commit("Title", "deleted_entry");
1485 group_add_child(&destination_db.root, rc_refcell_node(deleted_entry), 0).unwrap();
1486
1487 let entry_count_before = get_all_entries(&destination_db.root).len();
1488 let group_count_before = get_all_groups(&destination_db.root).len();
1489
1490 let merge_result = destination_db.merge(&source_db).unwrap();
1491 assert_eq!(merge_result.warnings.len(), 0);
1492 assert_eq!(merge_result.events.len(), 0);
1493
1494 let entry_count_after = get_all_entries(&destination_db.root).len();
1495 let group_count_after = get_all_groups(&destination_db.root).len();
1496 assert_eq!(entry_count_after, entry_count_before);
1497 assert_eq!(group_count_after, group_count_before);
1498
1499 let new_entry = Group::find_node_location(&destination_db.root, deleted_entry_uuid);
1500 assert!(new_entry.is_some());
1501
1502 assert!(!destination_db.deleted_objects.contains_key(&deleted_entry_uuid));
1503 }
1504
1505 #[test]
1506 fn test_group_subtree_deletion() {
1507 let mut destination_db = create_test_database();
1508 let mut source_db = destination_db.clone();
1509
1510 let deleted_entry_uuid = Uuid::new_v4();
1511 let deleted_group_uuid = Uuid::new_v4();
1512 let deleted_subgroup_uuid = Uuid::new_v4();
1513
1514 thread::sleep(time::Duration::from_secs(1));
1515 let mut deleted_entry = Entry::default();
1516 deleted_entry.set_uuid(deleted_entry_uuid);
1517 deleted_entry.set_field_and_commit("Title", "deleted_entry");
1518
1519 let mut deleted_subgroup = Group::new("deleted_subgroup");
1520 deleted_subgroup.uuid = deleted_subgroup_uuid;
1521 deleted_subgroup.add_child(rc_refcell_node(deleted_entry), 0);
1522
1523 let mut deleted_group = Group::new("deleted_group");
1524 deleted_group.uuid = deleted_group_uuid;
1525 deleted_group.add_child(rc_refcell_node(deleted_subgroup), 0);
1526
1527 group_add_child(&destination_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1528
1529 thread::sleep(time::Duration::from_secs(1));
1530 source_db.deleted_objects.insert(deleted_entry_uuid, Some(Times::now()));
1531 source_db.deleted_objects.insert(deleted_subgroup_uuid, Some(Times::now()));
1532 source_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1533
1534 let entry_count_before = get_all_entries(&destination_db.root).len();
1535 let group_count_before = get_all_groups(&destination_db.root).len();
1536
1537 let merge_result = destination_db.merge(&source_db).unwrap();
1538 assert_eq!(merge_result.warnings.len(), 0);
1539 assert_eq!(merge_result.events.len(), 3);
1540
1541 let entry_count_after = get_all_entries(&destination_db.root).len();
1542 let group_count_after = get_all_groups(&destination_db.root).len();
1543 assert_eq!(entry_count_after, entry_count_before - 1);
1544 assert_eq!(group_count_after, group_count_before - 2);
1545
1546 let deleted_entry = Group::find_node_location(&destination_db.root, deleted_entry_uuid);
1547 assert!(deleted_entry.is_none());
1548 let deleted_subgroup = Group::find_node_location(&destination_db.root, deleted_subgroup_uuid);
1549 assert!(deleted_subgroup.is_none());
1550 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1551 assert!(deleted_group.is_none());
1552
1553 assert!(destination_db.deleted_objects.contains_key(&deleted_entry_uuid));
1554 assert!(destination_db.deleted_objects.contains_key(&deleted_subgroup_uuid));
1555 assert!(destination_db.deleted_objects.contains_key(&deleted_group_uuid));
1556 }
1557
1558 #[test]
1559 fn test_group_subtree_partial_deletion() {
1560 let mut destination_db = create_test_database();
1561 let mut source_db = destination_db.clone();
1562
1563 let deleted_entry_uuid = Uuid::new_v4();
1564 let deleted_group_uuid = Uuid::new_v4();
1565 let deleted_subgroup_uuid = Uuid::new_v4();
1566
1567 thread::sleep(time::Duration::from_secs(1));
1568 let mut deleted_entry = Entry::default();
1569 deleted_entry.set_uuid(deleted_entry_uuid);
1570 deleted_entry.set_field_and_commit("Title", "deleted_entry");
1571
1572 let mut deleted_subgroup = Group::new("deleted_subgroup");
1573 deleted_subgroup.uuid = deleted_subgroup_uuid;
1574 deleted_subgroup.add_child(rc_refcell_node(deleted_entry), 0);
1575
1576 thread::sleep(time::Duration::from_secs(1));
1577 source_db.deleted_objects.insert(deleted_entry_uuid, Some(Times::now()));
1578 source_db.deleted_objects.insert(deleted_subgroup_uuid, Some(Times::now()));
1579 source_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1580
1581 thread::sleep(time::Duration::from_secs(1));
1582 let mut deleted_group = Group::new("deleted_group");
1583 deleted_group.uuid = deleted_group_uuid;
1584 deleted_group.add_child(rc_refcell_node(deleted_subgroup), 0);
1585
1586 group_add_child(&destination_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1587
1588 let entry_count_before = get_all_entries(&destination_db.root).len();
1589 let group_count_before = get_all_groups(&destination_db.root).len();
1590
1591 let merge_result = destination_db.merge(&source_db).unwrap();
1592 assert_eq!(merge_result.warnings.len(), 0);
1593 assert_eq!(merge_result.events.len(), 2);
1594
1595 let entry_count_after = get_all_entries(&destination_db.root).len();
1596 let group_count_after = get_all_groups(&destination_db.root).len();
1597 assert_eq!(entry_count_after, entry_count_before - 1);
1598 assert_eq!(group_count_after, group_count_before - 1);
1599
1600 let deleted_entry = Group::find_node_location(&destination_db.root, deleted_entry_uuid);
1601 assert!(deleted_entry.is_none());
1602 let deleted_subgroup = Group::find_node_location(&destination_db.root, deleted_subgroup_uuid);
1603 assert!(deleted_subgroup.is_none());
1604 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1605 assert!(deleted_group.is_some());
1606
1607 assert!(destination_db.deleted_objects.contains_key(&deleted_entry_uuid));
1608 assert!(destination_db.deleted_objects.contains_key(&deleted_subgroup_uuid));
1609 assert!(!destination_db.deleted_objects.contains_key(&deleted_group_uuid));
1610 }
1611
1612 #[test]
1613 fn test_deleted_group_in_source_modified_in_destination() {
1614 let mut destination_db = create_test_database();
1615 let mut source_db = destination_db.clone();
1616
1617 let deleted_group_uuid = Uuid::new_v4();
1618
1619 thread::sleep(time::Duration::from_secs(1));
1620 source_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1621
1622 thread::sleep(time::Duration::from_secs(1));
1623 let mut deleted_group = Group::new("deleted_group");
1624 deleted_group.uuid = deleted_group_uuid;
1625 group_add_child(&destination_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1626
1627 let entry_count_before = get_all_entries(&destination_db.root).len();
1628 let group_count_before = get_all_groups(&destination_db.root).len();
1629
1630 let merge_result = destination_db.merge(&source_db).unwrap();
1631 assert_eq!(merge_result.warnings.len(), 0);
1632 assert_eq!(merge_result.events.len(), 0);
1633
1634 let entry_count_after = get_all_entries(&destination_db.root).len();
1635 let group_count_after = get_all_groups(&destination_db.root).len();
1636 assert_eq!(entry_count_after, entry_count_before);
1637 assert_eq!(group_count_after, group_count_before);
1638
1639 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1640 assert!(deleted_group.is_some());
1641
1642 assert!(!destination_db.deleted_objects.contains_key(&deleted_group_uuid));
1643 }
1644
1645 #[test]
1646 fn test_deleted_group_has_new_entries() {
1647 let mut destination_db = create_test_database();
1648 let mut source_db = destination_db.clone();
1649
1650 let mut deleted_group = Group::new("deleted_group");
1651 let deleted_group_uuid = deleted_group.uuid;
1652
1653 let mut new_entry = Entry::default();
1654 let new_entry_uuid = new_entry.uuid;
1655 new_entry.set_field_and_commit("Title", "new_entry");
1656 deleted_group.add_child(rc_refcell_node(new_entry), 0);
1657 group_add_child(&destination_db.root, rc_refcell_node(deleted_group), 0).unwrap();
1658
1659 let entry_count_before = get_all_entries(&destination_db.root).len();
1660 let group_count_before = get_all_groups(&destination_db.root).len();
1661
1662 thread::sleep(time::Duration::from_secs(1));
1663 source_db.deleted_objects.insert(deleted_group_uuid, Some(Times::now()));
1664
1665 let merge_result = destination_db.merge(&source_db).unwrap();
1666 assert_eq!(merge_result.warnings.len(), 0);
1667 assert_eq!(merge_result.events.len(), 0);
1668
1669 let entry_count_after = get_all_entries(&destination_db.root).len();
1670 let group_count_after = get_all_groups(&destination_db.root).len();
1671 assert_eq!(entry_count_after, entry_count_before);
1672 assert_eq!(group_count_after, group_count_before);
1673
1674 let deleted_group = Group::find_node_location(&destination_db.root, deleted_group_uuid);
1675 assert!(deleted_group.is_some());
1676 let new_entry = Group::find_node_location(&destination_db.root, new_entry_uuid);
1677 assert!(new_entry.is_some());
1678
1679 assert!(!destination_db.deleted_objects.contains_key(&deleted_group_uuid));
1680 assert!(!destination_db.deleted_objects.contains_key(&new_entry_uuid));
1681 }
1682
1683 #[test]
1684 fn test_add_new_non_root_entry() {
1685 let mut destination_db = create_test_database();
1686 let source_db = destination_db.clone();
1687
1688 let entry_count_before = get_all_entries(&destination_db.root).len();
1689 let group_count_before = get_all_groups(&destination_db.root).len();
1690
1691 let source_sub_group = with_node::<Group, _, _>(&source_db.root, |group| group.groups()).unwrap()[0].clone();
1692
1693 let mut new_entry = Entry::default();
1694 let new_entry_uuid = new_entry.uuid;
1695 new_entry.set_field_and_commit("Title", "new_entry");
1696 group_add_child(&source_sub_group, rc_refcell_node(new_entry), 0).unwrap();
1698
1699 let merge_result = destination_db.merge(&source_db).unwrap();
1700 assert_eq!(merge_result.warnings.len(), 0);
1701 assert_eq!(merge_result.events.len(), 1);
1702
1703 let entry_count_after = get_all_entries(&destination_db.root).len();
1704 let group_count_after = get_all_groups(&destination_db.root).len();
1705 assert_eq!(entry_count_after, entry_count_before + 1);
1706 assert_eq!(group_count_after, group_count_before);
1707
1708 let created_entry_location = Group::find_node_location(&destination_db.root, new_entry_uuid).unwrap();
1709 assert_eq!(created_entry_location.len(), 2);
1710 }
1711
1712 #[test]
1713 fn test_add_new_entry_new_group() {
1714 let mut destination_db = create_test_database();
1715 let source_db = destination_db.clone();
1716
1717 let group_count_before = get_all_groups(&destination_db.root).len();
1718 let entry_count_before = get_all_entries(&destination_db.root).len();
1719
1720 let mut source_group = Group::new("new_group");
1721 let mut source_sub_group = Group::new("new_subgroup");
1722
1723 let mut new_entry = Entry::default();
1724 let new_entry_uuid = new_entry.uuid;
1725 new_entry.set_field_and_commit("Title", "new_entry");
1726 source_sub_group.add_child(rc_refcell_node(new_entry), 0);
1727 source_group.add_child(rc_refcell_node(source_sub_group), 0);
1728 group_add_child(&source_db.root, rc_refcell_node(source_group), 0).unwrap();
1729
1730 let merge_result = destination_db.merge(&source_db).unwrap();
1731 assert_eq!(merge_result.warnings.len(), 0);
1732 assert_eq!(merge_result.events.len(), 3);
1733
1734 let group_count_after = get_all_groups(&destination_db.root).len();
1735 let entry_count_after = get_all_entries(&destination_db.root).len();
1736 assert_eq!(entry_count_after, entry_count_before + 1);
1737 assert_eq!(group_count_after, group_count_before + 2);
1738
1739 let created_entry_location = Group::find_node_location(&destination_db.root, new_entry_uuid).unwrap();
1740 assert_eq!(created_entry_location.len(), 3);
1741 }
1742
1743 #[test]
1744 fn test_entry_relocation_existing_group() {
1745 let mut destination_db = create_test_database();
1746 let source_db = destination_db.clone();
1747
1748 let group_count_before = get_all_groups(&destination_db.root).len();
1749 let entry_count_before = get_all_entries(&destination_db.root).len();
1750
1751 thread::sleep(time::Duration::from_secs(1));
1752 let new_location_changed_timestamp = Times::now();
1753
1754 source_db
1755 .relocate_node(
1756 Uuid::parse_str(ENTRY2_ID).unwrap(),
1757 &[Uuid::parse_str(GROUP1_ID).unwrap(), Uuid::parse_str(SUBGROUP1_ID).unwrap()],
1758 &[Uuid::parse_str(GROUP2_ID).unwrap()],
1759 new_location_changed_timestamp,
1760 )
1761 .unwrap();
1762
1763 let merge_result = destination_db.merge(&source_db).unwrap();
1764 assert_eq!(merge_result.warnings.len(), 0);
1765 assert_eq!(merge_result.events.len(), 1);
1766
1767 let group_count_after = get_all_groups(&destination_db.root).len();
1768 let entry_count_after = get_all_entries(&destination_db.root).len();
1769 assert_eq!(group_count_after, group_count_before);
1770 assert_eq!(entry_count_after, entry_count_before);
1771
1772 let moved_entry_location = Group::find_node_location(&destination_db.root, Uuid::parse_str(ENTRY2_ID).unwrap()).unwrap();
1773 assert_eq!(moved_entry_location.len(), 2);
1774 assert_eq!(&moved_entry_location[0].to_string(), ROOT_GROUP_ID);
1775 assert_eq!(&moved_entry_location[1].to_string(), GROUP2_ID);
1776
1777 let moved_entry = get_entry(&destination_db, &["group2", "entry2"]);
1778 let ts = moved_entry.borrow().get_times().get_location_changed().unwrap();
1779 assert_eq!(ts, new_location_changed_timestamp);
1780 }
1781
1782 #[test]
1783 fn test_entry_relocation_and_update() {
1784 let mut destination_db = create_test_database();
1785 let source_db = destination_db.clone();
1786
1787 let group_count_before = get_all_groups(&destination_db.root).len();
1788 let entry_count_before = get_all_entries(&destination_db.root).len();
1789
1790 let entry2 = Group::find_entry(
1791 &source_db.root,
1792 &[
1793 Uuid::parse_str(GROUP1_ID).unwrap(),
1794 Uuid::parse_str(SUBGROUP1_ID).unwrap(),
1795 Uuid::parse_str(ENTRY2_ID).unwrap(),
1796 ],
1797 )
1798 .unwrap();
1799
1800 with_node_mut::<Entry, _, _>(&entry2, |entry2| {
1801 entry2.set_field_and_commit("Title", "entry2_modified_in_source");
1802 });
1803
1804 thread::sleep(time::Duration::from_secs(1));
1805 let new_location_changed_timestamp = Times::now();
1806
1807 source_db
1808 .relocate_node(
1809 Uuid::parse_str(ENTRY2_ID).unwrap(),
1810 &[Uuid::parse_str(GROUP1_ID).unwrap(), Uuid::parse_str(SUBGROUP1_ID).unwrap()],
1811 &[Uuid::parse_str(GROUP2_ID).unwrap()],
1812 new_location_changed_timestamp,
1813 )
1814 .unwrap();
1815
1816 let entry2 = Group::find_entry(
1817 &destination_db.root,
1818 &[
1819 Uuid::parse_str(GROUP1_ID).unwrap(),
1820 Uuid::parse_str(SUBGROUP1_ID).unwrap(),
1821 Uuid::parse_str(ENTRY2_ID).unwrap(),
1822 ],
1823 )
1824 .unwrap();
1825 with_node_mut::<Entry, _, _>(&entry2, |entry2| {
1826 entry2.set_field_and_commit("Title", "entry2_modified_in_destination");
1827 });
1828 let entry_modified_timestamp = entry2.borrow().get_times().get_last_modification().unwrap();
1829
1830 let merge_result = destination_db.merge(&source_db).unwrap();
1831 assert_eq!(merge_result.warnings.len(), 0);
1832 assert_eq!(merge_result.events.len(), 2);
1833
1834 let group_count_after = get_all_groups(&destination_db.root).len();
1835 let entry_count_after = get_all_entries(&destination_db.root).len();
1836 assert_eq!(group_count_after, group_count_before);
1837 assert_eq!(entry_count_after, entry_count_before);
1838
1839 let moved_entry_location = Group::find_node_location(&destination_db.root, Uuid::parse_str(ENTRY2_ID).unwrap()).unwrap();
1840 assert_eq!(moved_entry_location.len(), 2);
1841 assert_eq!(&moved_entry_location[0].to_string(), ROOT_GROUP_ID);
1842 assert_eq!(&moved_entry_location[1].to_string(), GROUP2_ID);
1843
1844 let moved_entry = get_entry(&destination_db, &["group2", "entry2_modified_in_destination"]);
1845 let ts1 = moved_entry.borrow().get_times().get_last_modification().unwrap();
1846 assert_eq!(ts1, entry_modified_timestamp,);
1847 let ts2 = moved_entry.borrow().get_times().get_location_changed().unwrap();
1848 assert_eq!(ts2, new_location_changed_timestamp);
1849 }
1850
1851 #[test]
1852 fn test_entry_relocation_in_destination_and_update() {
1853 let mut destination_db = create_test_database();
1854 let source_db = destination_db.clone();
1855
1856 let group_count_before = get_all_groups(&destination_db.root).len();
1857 let entry_count_before = get_all_entries(&destination_db.root).len();
1858
1859 let entry2 = Group::find_entry(
1860 &source_db.root,
1861 &[
1862 Uuid::parse_str(GROUP1_ID).unwrap(),
1863 Uuid::parse_str(SUBGROUP1_ID).unwrap(),
1864 Uuid::parse_str(ENTRY2_ID).unwrap(),
1865 ],
1866 )
1867 .unwrap();
1868 with_node_mut::<Entry, _, _>(&entry2, |entry2| {
1869 entry2.set_field_and_commit("Title", "entry2_modified_in_source");
1870 });
1871 let entry_modified_timestamp = entry2.borrow().get_times().get_last_modification().unwrap();
1872
1873 thread::sleep(time::Duration::from_secs(1));
1874 let new_location_changed_timestamp = Times::now();
1875
1876 destination_db
1877 .relocate_node(
1878 Uuid::parse_str(ENTRY2_ID).unwrap(),
1879 &[Uuid::parse_str(GROUP1_ID).unwrap(), Uuid::parse_str(SUBGROUP1_ID).unwrap()],
1880 &[Uuid::parse_str(GROUP2_ID).unwrap()],
1881 new_location_changed_timestamp,
1882 )
1883 .unwrap();
1884
1885 let merge_result = destination_db.merge(&source_db).unwrap();
1886 assert_eq!(merge_result.warnings.len(), 0);
1887 assert_eq!(merge_result.events.len(), 1);
1888
1889 let group_count_after = get_all_groups(&destination_db.root).len();
1890 let entry_count_after = get_all_entries(&destination_db.root).len();
1891 assert_eq!(group_count_after, group_count_before);
1892 assert_eq!(entry_count_after, entry_count_before);
1893
1894 let moved_entry_location = Group::find_node_location(&destination_db.root, Uuid::parse_str(ENTRY2_ID).unwrap()).unwrap();
1895 assert_eq!(moved_entry_location.len(), 2);
1896 assert_eq!(&moved_entry_location[0].to_string(), ROOT_GROUP_ID);
1897 assert_eq!(&moved_entry_location[1].to_string(), GROUP2_ID);
1898
1899 let moved_entry = get_entry(&destination_db, &["group2", "entry2_modified_in_source"]);
1900 let ts1 = moved_entry.borrow().get_times().get_last_modification().unwrap();
1901 assert_eq!(ts1, entry_modified_timestamp,);
1902 let ts2 = moved_entry.borrow().get_times().get_location_changed().unwrap();
1903 assert_eq!(ts2, new_location_changed_timestamp);
1904 }
1905
1906 #[test]
1907 fn test_entry_relocation_new_group() {
1908 let mut destination_db = create_test_database();
1909
1910 let entry_count_before = get_all_entries(&destination_db.root).len();
1911 let group_count_before = get_all_groups(&destination_db.root).len();
1912
1913 let source_db = destination_db.clone();
1914 let mut new_group = Group::new("new_group");
1915 let new_group_uuid = new_group.uuid;
1916
1917 let mut new_entry = Entry::default();
1918 let entry_uuid = new_entry.uuid;
1919 new_entry.set_field_and_commit("Title", "entry1");
1920
1921 thread::sleep(time::Duration::from_secs(1));
1922 new_entry.times.set_location_changed(Some(Times::now()));
1923 new_entry.update_history();
1926 new_group.add_child(rc_refcell_node(new_entry), 0);
1927 group_add_child(&source_db.root, rc_refcell_node(new_group), 0).unwrap();
1928
1929 let merge_result = destination_db.merge(&source_db).unwrap();
1930 assert_eq!(merge_result.warnings.len(), 0);
1931 assert_eq!(merge_result.events.len(), 2);
1932
1933 let entry_count_after = get_all_entries(&destination_db.root).len();
1934 let group_count_after = get_all_groups(&destination_db.root).len();
1935 assert_eq!(entry_count_after, entry_count_before + 1);
1936 assert_eq!(group_count_after, group_count_before + 1);
1937
1938 let created_entry_location = Group::find_node_location(&destination_db.root, entry_uuid).unwrap();
1939 assert_eq!(created_entry_location.len(), 2);
1940 assert_eq!(&created_entry_location[0].to_string(), ROOT_GROUP_ID);
1941 assert_eq!(created_entry_location[1], new_group_uuid);
1942 }
1943
1944 #[test]
1945 fn test_group_relocation() {
1946 let mut destination_db = create_test_database();
1947 let source_db = destination_db.clone();
1948
1949 let entry_count_before = get_all_entries(&destination_db.root).len();
1950 let group_count_before = get_all_groups(&destination_db.root).len();
1951
1952 let source_group_1 = get_group(&source_db, &["group1"]);
1953 let source_sub_group_1 = with_node_mut::<Group, _, _>(&source_group_1, |g| g.remove_node(Uuid::parse_str(SUBGROUP1_ID).unwrap()))
1954 .unwrap()
1955 .unwrap();
1956 assert!(node_is_group(&source_sub_group_1));
1957 thread::sleep(time::Duration::from_secs(1));
1958 let new_location_changed_timestamp = Times::now();
1959 source_sub_group_1
1960 .borrow_mut()
1961 .get_times_mut()
1962 .set_location_changed(Some(new_location_changed_timestamp));
1963
1964 let source_group_2 = get_group(&source_db, &["group2"]);
1965 group_add_child(&source_group_2, source_sub_group_1, 0).unwrap();
1966
1967 let merge_result = destination_db.merge(&source_db).unwrap();
1968 assert_eq!(merge_result.warnings.len(), 0);
1969 assert_eq!(merge_result.events.len(), 1);
1970
1971 let entry_count_after = get_all_entries(&destination_db.root).len();
1972 let group_count_after = get_all_groups(&destination_db.root).len();
1973 assert_eq!(entry_count_after, entry_count_before);
1974 assert_eq!(group_count_after, group_count_before);
1975
1976 let created_entry_location = Group::find_node_location(&destination_db.root, Uuid::parse_str(ENTRY2_ID).unwrap()).unwrap();
1977 assert_eq!(created_entry_location.len(), 3);
1978 assert_eq!(created_entry_location[0], destination_db.root.borrow().get_uuid());
1979 assert_eq!(&created_entry_location[1].to_string(), GROUP2_ID);
1980 assert_eq!(&created_entry_location[2].to_string(), SUBGROUP1_ID);
1981
1982 let relocated_group = get_group(&destination_db, &["group2", "subgroup1"]);
1983 let ts = relocated_group.borrow().get_times().get_location_changed().unwrap();
1984 assert_eq!(ts, new_location_changed_timestamp);
1985 }
1986
1987 #[test]
1988 fn test_update_in_destination_no_conflict() {
1989 let mut destination_db = create_test_database();
1990 let source_db = destination_db.clone();
1991
1992 let entry_count_before = get_all_entries(&destination_db.root).len();
1993 let group_count_before = get_all_groups(&destination_db.root).len();
1994
1995 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
1996 with_node_mut::<Entry, _, _>(&entry, |entry| entry.set_field_and_commit("Title", "entry1_updated")).unwrap();
1997
1998 let merge_result = destination_db.merge(&source_db).unwrap();
1999 assert_eq!(merge_result.warnings.len(), 0);
2000 assert_eq!(merge_result.events.len(), 0);
2001
2002 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2003 let merged_history = with_node::<Entry, _, _>(&entry, |entry| entry.history.clone()).unwrap().unwrap();
2004 assert!(merged_history.is_ordered());
2005 assert_eq!(merged_history.entries.len(), 2);
2006 let merged_entry = &merged_history.entries[1];
2007 assert_eq!(merged_entry.get_title(), Some("entry1"));
2008
2009 let entry_count_after = get_all_entries(&destination_db.root).len();
2010 let group_count_after = get_all_groups(&destination_db.root).len();
2011 assert_eq!(entry_count_after, entry_count_before);
2012 assert_eq!(group_count_after, group_count_before);
2013
2014 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2015 assert_eq!(entry.borrow().get_title(), Some("entry1_updated"));
2016 }
2017
2018 #[test]
2019 fn test_update_in_source_no_conflict() {
2020 let mut destination_db = create_test_database();
2021 let source_db = destination_db.clone();
2022
2023 let entry_count_before = get_all_entries(&destination_db.root).len();
2024 let group_count_before = get_all_groups(&destination_db.root).len();
2025
2026 let entry = with_node::<Group, _, _>(&source_db.root, |group| group.entries()).unwrap()[0].clone();
2027 with_node_mut::<Entry, _, _>(&entry, |entry| entry.set_field_and_commit("Title", "entry1_updated")).unwrap();
2028
2029 let merge_result = destination_db.merge(&source_db).unwrap();
2030 assert_eq!(merge_result.warnings.len(), 0);
2031 assert_eq!(merge_result.events.len(), 1);
2032
2033 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2034 let merged_history = with_node::<Entry, _, _>(&entry, |entry| entry.history.clone()).unwrap().unwrap();
2035 assert!(merged_history.is_ordered());
2036 assert_eq!(merged_history.entries.len(), 2);
2037 let merged_entry = &merged_history.entries[1];
2038 assert_eq!(merged_entry.get_title(), Some("entry1"));
2039
2040 let entry_count_after = get_all_entries(&destination_db.root).len();
2041 let group_count_after = get_all_groups(&destination_db.root).len();
2042 assert_eq!(entry_count_after, entry_count_before);
2043 assert_eq!(group_count_after, group_count_before);
2044
2045 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2046 assert_eq!(entry.borrow().get_title(), Some("entry1_updated"));
2047 }
2048
2049 #[test]
2050 fn test_update_with_conflicts() {
2051 let mut destination_db = create_test_database();
2052 let source_db = destination_db.clone();
2053
2054 let entry_count_before = get_all_entries(&destination_db.root).len();
2055 let group_count_before = get_all_groups(&destination_db.root).len();
2056
2057 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2058 with_node_mut::<Entry, _, _>(&entry, |e| e.set_field_and_commit("Title", "entry1_updated_from_destination")).unwrap();
2059
2060 let entry = with_node::<Group, _, _>(&source_db.root, |group| group.entries()).unwrap()[0].clone();
2061 with_node_mut::<Entry, _, _>(&entry, |entry| entry.set_field_and_commit("Title", "entry1_updated_from_source")).unwrap();
2062
2063 let merge_result = destination_db.merge(&source_db).unwrap();
2064 assert_eq!(merge_result.warnings.len(), 0);
2065 assert_eq!(merge_result.events.len(), 1);
2066
2067 let entry_count_after = get_all_entries(&destination_db.root).len();
2068 let group_count_after = get_all_groups(&destination_db.root).len();
2069 assert_eq!(entry_count_after, entry_count_before);
2070 assert_eq!(group_count_after, group_count_before);
2071
2072 let entry = with_node::<Group, _, _>(&destination_db.root, |group| group.entries()).unwrap()[0].clone();
2073 assert_eq!(entry.borrow().get_title(), Some("entry1_updated_from_source"));
2074
2075 let merged_history = with_node::<Entry, _, _>(&entry, |entry| entry.history.clone()).unwrap().unwrap();
2076 assert!(merged_history.is_ordered());
2077 assert_eq!(merged_history.entries.len(), 3);
2078 let merged_entry = &merged_history.entries[1];
2079 assert_eq!(merged_entry.get_title(), Some("entry1_updated_from_destination"));
2080
2081 let merge_result = destination_db.merge(&destination_db.clone()).unwrap();
2083 assert_eq!(merge_result.warnings.len(), 0);
2084 assert_eq!(merge_result.events.len(), 0);
2085 }
2086
2087 #[test]
2088 fn test_group_update_in_source() {
2089 let mut destination_db = create_test_database();
2090 let source_db = destination_db.clone();
2091
2092 let entry_count_before = get_all_entries(&destination_db.root).len();
2093 let group_count_before = get_all_groups(&destination_db.root).len();
2094
2095 let group = get_group(&source_db, &["group1", "subgroup1"]);
2096 group.borrow_mut().set_title(Some("subgroup1_updated_name"));
2097 thread::sleep(time::Duration::from_secs(1));
2100 let new_modification_timestamp = Times::now();
2101 group
2102 .borrow_mut()
2103 .get_times_mut()
2104 .set_last_modification(Some(new_modification_timestamp));
2105
2106 let merge_result = destination_db.merge(&source_db).unwrap();
2107 assert_eq!(merge_result.warnings.len(), 0);
2108 assert_eq!(merge_result.events.len(), 1);
2109
2110 let entry_count_after = get_all_entries(&destination_db.root).len();
2111 let group_count_after = get_all_groups(&destination_db.root).len();
2112 assert_eq!(entry_count_after, entry_count_before);
2113 assert_eq!(group_count_after, group_count_before);
2114
2115 let modified_group = get_group(&destination_db, &["group1", "subgroup1_updated_name"]);
2116 assert_eq!(modified_group.borrow().get_title().unwrap(), "subgroup1_updated_name");
2117 let ts = modified_group.borrow().get_times().get_last_modification();
2118 assert_eq!(ts, Some(new_modification_timestamp));
2119 }
2120
2121 #[test]
2122 fn test_group_update_in_destination() {
2123 let mut destination_db = create_test_database();
2124 let source_db = destination_db.clone();
2125
2126 let entry_count_before = get_all_entries(&destination_db.root).len();
2127 let group_count_before = get_all_groups(&destination_db.root).len();
2128
2129 let group = get_group(&destination_db, &["group1", "subgroup1"]);
2130 group.borrow_mut().set_title(Some("subgroup1_updated_name"));
2131 thread::sleep(time::Duration::from_secs(1));
2134 let new_modification_timestamp = Times::now();
2135 group
2136 .borrow_mut()
2137 .get_times_mut()
2138 .set_last_modification(Some(new_modification_timestamp));
2139
2140 let merge_result = destination_db.merge(&source_db).unwrap();
2141 assert_eq!(merge_result.warnings.len(), 0);
2142 assert_eq!(merge_result.events.len(), 0);
2143
2144 let entry_count_after = get_all_entries(&destination_db.root).len();
2145 let group_count_after = get_all_groups(&destination_db.root).len();
2146 assert_eq!(entry_count_after, entry_count_before);
2147 assert_eq!(group_count_after, group_count_before);
2148
2149 let modified_group = get_group(&destination_db, &["group1", "subgroup1_updated_name"]);
2150 assert_eq!(modified_group.borrow().get_title(), Some("subgroup1_updated_name"));
2151 assert_eq!(
2152 modified_group.borrow().get_times().get_last_modification(),
2153 Some(new_modification_timestamp),
2154 );
2155 }
2156
2157 #[test]
2158 fn test_group_update_and_relocation() {
2159 let mut destination_db = create_test_database();
2160 let source_db = destination_db.clone();
2161
2162 let entry_count_before = get_all_entries(&destination_db.root).len();
2163 let group_count_before = get_all_groups(&destination_db.root).len();
2164
2165 let group = get_group(&source_db, &["group1", "subgroup1"]);
2166 group.borrow_mut().set_title(Some("subgroup1_updated_name"));
2167 thread::sleep(time::Duration::from_secs(1));
2170 let new_modification_timestamp = Times::now();
2171 group
2172 .borrow_mut()
2173 .get_times_mut()
2174 .set_last_modification(Some(new_modification_timestamp));
2175
2176 source_db
2177 .relocate_node(
2178 Uuid::parse_str(SUBGROUP1_ID).unwrap(),
2179 &[Uuid::parse_str(GROUP1_ID).unwrap()],
2180 &[Uuid::parse_str(GROUP2_ID).unwrap()],
2181 new_modification_timestamp,
2182 )
2183 .unwrap();
2184
2185 let merge_result = destination_db.merge(&source_db).unwrap();
2186 assert_eq!(merge_result.warnings.len(), 0);
2187 assert_eq!(merge_result.events.len(), 2);
2188
2189 let entry_count_after = get_all_entries(&destination_db.root).len();
2190 let group_count_after = get_all_groups(&destination_db.root).len();
2191 assert_eq!(entry_count_after, entry_count_before);
2192 assert_eq!(group_count_after, group_count_before);
2193
2194 let modified_group = get_group(&destination_db, &["group2", "subgroup1_updated_name"]);
2195 assert_eq!(modified_group.borrow().get_title(), Some("subgroup1_updated_name"));
2196 assert_eq!(
2197 modified_group.borrow().get_times().get_last_modification(),
2198 Some(new_modification_timestamp),
2199 );
2200 }
2201
2202 #[test]
2203 fn test_group_update_in_destination_and_relocation_in_source() {
2204 let mut destination_db = create_test_database();
2205 let source_db = destination_db.clone();
2206
2207 let entry_count_before = get_all_entries(&destination_db.root).len();
2208 let group_count_before = get_all_groups(&destination_db.root).len();
2209
2210 let group = get_group(&source_db, &["group1", "subgroup1"]);
2211 group.borrow_mut().set_title(Some("subgroup1_updated_name"));
2212 thread::sleep(time::Duration::from_secs(1));
2215 let new_modification_timestamp = Times::now();
2216 group
2217 .borrow_mut()
2218 .get_times_mut()
2219 .set_last_modification(Some(new_modification_timestamp));
2220
2221 thread::sleep(time::Duration::from_secs(1));
2222 let new_location_changed_timestamp = Times::now();
2223 destination_db
2224 .relocate_node(
2225 Uuid::parse_str(SUBGROUP1_ID).unwrap(),
2226 &[Uuid::parse_str(GROUP1_ID).unwrap()],
2227 &[Uuid::parse_str(GROUP2_ID).unwrap()],
2228 new_location_changed_timestamp,
2229 )
2230 .unwrap();
2231
2232 let merge_result = destination_db.merge(&source_db).unwrap();
2233 assert_eq!(merge_result.warnings.len(), 0);
2234 assert_eq!(merge_result.events.len(), 1);
2235
2236 let entry_count_after = get_all_entries(&destination_db.root).len();
2237 let group_count_after = get_all_groups(&destination_db.root).len();
2238 assert_eq!(entry_count_after, entry_count_before);
2239 assert_eq!(group_count_after, group_count_before);
2240
2241 let modified_group = get_group(&destination_db, &["group2", "subgroup1_updated_name"]);
2242 assert_eq!(modified_group.borrow().get_title(), Some("subgroup1_updated_name"));
2243 assert_eq!(
2244 modified_group.borrow().get_times().get_last_modification(),
2245 Some(new_modification_timestamp)
2246 );
2247 assert_eq!(
2248 modified_group.borrow().get_times().get_location_changed(),
2249 Some(new_location_changed_timestamp)
2250 );
2251 }
2252
2253 #[test]
2254 fn test_icon_added_in_source() {
2255 let mut destination_db = create_test_database();
2256 let mut source_db = destination_db.clone();
2257
2258 let new_icon_id = Uuid::new_v4();
2259 source_db.meta.custom_icons.insert(
2260 new_icon_id,
2261 CustomIcon::new(new_icon_id, None, Some(Times::now()), vec![1, 2, 3, 4]),
2262 );
2263 let entry = search_node_by_uuid_with_specific_type::<Entry>(&source_db.root, Uuid::parse_str(ENTRY1_ID).unwrap()).unwrap();
2264 with_node_mut::<Entry, _, _>(&entry, |entry| {
2265 entry.icon = Icon::Custom(new_icon_id);
2266 entry.get_times_mut().set_last_modification(Some(Times::now()));
2267 });
2268
2269 let merge_result = destination_db.merge(&source_db).unwrap();
2270 assert_eq!(merge_result.warnings.len(), 0);
2271 assert_eq!(merge_result.events.len(), 2);
2272
2273 assert!(destination_db.meta.custom_icon(new_icon_id).is_some());
2274 }
2275
2276 #[test]
2277 fn test_icon_updated_in_source() {
2278 let mut destination_db = create_test_database();
2279
2280 let icon_id = Uuid::new_v4();
2281 destination_db
2282 .meta
2283 .custom_icons
2284 .insert(icon_id, CustomIcon::new(icon_id, None, Some(Times::epoch()), vec![1, 2, 3, 4]));
2285
2286 let mut source_db = destination_db.clone();
2287
2288 let source_icon = source_db.meta.custom_icons.get_mut(&icon_id).unwrap();
2289 source_icon.data = vec![5, 6, 7, 8];
2290 source_icon.last_modification_time = Some(Times::now());
2291
2292 let merge_result = destination_db.merge(&source_db).unwrap();
2293 assert_eq!(merge_result.warnings.len(), 0);
2294 assert_eq!(merge_result.events.len(), 1);
2295
2296 let icon = destination_db.meta.custom_icon(icon_id).unwrap();
2297 assert_eq!(icon.data, vec![5, 6, 7, 8]);
2298 }
2299
2300 #[test]
2301 fn test_icon_updated_in_destination() {
2302 let mut destination_db = create_test_database();
2303
2304 let icon_id = Uuid::new_v4();
2305 destination_db
2306 .meta
2307 .custom_icons
2308 .insert(icon_id, CustomIcon::new(icon_id, None, Some(Times::epoch()), vec![1, 2, 3, 4]));
2309
2310 let source_db = destination_db.clone();
2311
2312 let destination_icon = destination_db.meta.custom_icons.get_mut(&icon_id).unwrap();
2313 destination_icon.data = vec![5, 6, 7, 8];
2314 destination_icon.last_modification_time = Some(Times::now());
2315
2316 let merge_result = destination_db.merge(&source_db).unwrap();
2317 assert_eq!(merge_result.warnings.len(), 0);
2318 assert_eq!(merge_result.events.len(), 0);
2319
2320 let icon = destination_db.meta.custom_icon(icon_id).unwrap();
2321 assert_eq!(icon.data, vec![5, 6, 7, 8]);
2322 }
2323}