common/mount/conflict/
last_write_wins.rs1use crate::crypto::PublicKey;
4
5use super::types::{Conflict, Resolution};
6use super::ConflictResolver;
7
8#[derive(Debug, Clone, Default)]
16pub struct LastWriteWins;
17
18impl LastWriteWins {
19 pub fn new() -> Self {
21 Self
22 }
23}
24
25impl ConflictResolver for LastWriteWins {
26 fn resolve(&self, conflict: &Conflict, _local_peer: &PublicKey) -> Resolution {
27 if conflict.incoming.id > conflict.base.id {
28 Resolution::UseIncoming
29 } else {
30 Resolution::UseBase
31 }
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::crypto::SecretKey;
39 use crate::mount::path_ops::{OpId, OpType, PathOperation};
40 use std::path::PathBuf;
41
42 fn make_peer_id(seed: u8) -> PublicKey {
43 let mut seed_bytes = [0u8; 32];
44 seed_bytes[0] = seed;
45 let secret = SecretKey::from(seed_bytes);
46 secret.public()
47 }
48
49 fn make_op(peer_id: PublicKey, timestamp: u64, op_type: OpType, path: &str) -> PathOperation {
50 PathOperation {
51 id: OpId { timestamp, peer_id },
52 op_type,
53 path: PathBuf::from(path),
54 content_link: None,
55 is_dir: false,
56 }
57 }
58
59 #[test]
60 fn test_last_write_wins_incoming() {
61 let peer1 = make_peer_id(1);
62 let peer2 = make_peer_id(2);
63
64 let base = make_op(peer1, 1, OpType::Add, "file.txt");
65 let incoming = make_op(peer2, 2, OpType::Remove, "file.txt");
66
67 let conflict = Conflict::new(PathBuf::from("file.txt"), base, incoming);
68 let resolver = LastWriteWins::new();
69
70 assert_eq!(resolver.resolve(&conflict, &peer1), Resolution::UseIncoming);
72 }
73
74 #[test]
75 fn test_last_write_wins_base() {
76 let peer1 = make_peer_id(1);
77 let peer2 = make_peer_id(2);
78
79 let base = make_op(peer1, 2, OpType::Add, "file.txt");
80 let incoming = make_op(peer2, 1, OpType::Remove, "file.txt");
81
82 let conflict = Conflict::new(PathBuf::from("file.txt"), base, incoming);
83 let resolver = LastWriteWins::new();
84
85 assert_eq!(resolver.resolve(&conflict, &peer1), Resolution::UseBase);
87 }
88
89 #[test]
90 fn test_last_write_wins_tiebreak_by_peer_id() {
91 let peer1 = make_peer_id(1);
92 let peer2 = make_peer_id(2);
93
94 let base = make_op(peer1, 1, OpType::Add, "file.txt");
95 let incoming = make_op(peer2, 1, OpType::Remove, "file.txt");
96
97 let conflict = Conflict::new(PathBuf::from("file.txt"), base, incoming);
98 let resolver = LastWriteWins::new();
99
100 let resolution = resolver.resolve(&conflict, &peer1);
101
102 if peer2 > peer1 {
104 assert_eq!(resolution, Resolution::UseIncoming);
105 } else {
106 assert_eq!(resolution, Resolution::UseBase);
107 }
108 }
109}