Skip to main content

common/mount/conflict/
fork_on_conflict.rs

1//! Fork-on-conflict resolver
2
3use crate::crypto::PublicKey;
4
5use super::types::{Conflict, Resolution};
6use super::ConflictResolver;
7
8/// Fork-on-conflict resolution
9///
10/// Keeps both operations in the log when a conflict is detected.
11/// Conflicts are tracked and returned for manual resolution later.
12///
13/// This is useful when automatic conflict resolution is not acceptable
14/// and users need to manually choose which version to keep.
15#[derive(Debug, Clone, Default)]
16pub struct ForkOnConflict;
17
18impl ForkOnConflict {
19    /// Create a new ForkOnConflict resolver
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25impl ConflictResolver for ForkOnConflict {
26    fn resolve(&self, _conflict: &Conflict, _local_peer: &PublicKey) -> Resolution {
27        Resolution::KeepBoth
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34    use crate::crypto::SecretKey;
35    use crate::mount::path_ops::{OpId, OpType, PathOperation};
36    use std::path::PathBuf;
37
38    fn make_peer_id(seed: u8) -> PublicKey {
39        let mut seed_bytes = [0u8; 32];
40        seed_bytes[0] = seed;
41        let secret = SecretKey::from(seed_bytes);
42        secret.public()
43    }
44
45    fn make_op(peer_id: PublicKey, timestamp: u64, op_type: OpType, path: &str) -> PathOperation {
46        PathOperation {
47            id: OpId { timestamp, peer_id },
48            op_type,
49            path: PathBuf::from(path),
50            content_link: None,
51            is_dir: false,
52        }
53    }
54
55    #[test]
56    fn test_fork_on_conflict() {
57        let peer1 = make_peer_id(1);
58        let peer2 = make_peer_id(2);
59
60        let base = make_op(peer1, 1, OpType::Add, "file.txt");
61        let incoming = make_op(peer2, 2, OpType::Remove, "file.txt");
62
63        let conflict = Conflict::new(PathBuf::from("file.txt"), base, incoming);
64        let resolver = ForkOnConflict::new();
65
66        // ForkOnConflict always returns KeepBoth
67        assert_eq!(resolver.resolve(&conflict, &peer1), Resolution::KeepBoth);
68    }
69}