Skip to content

Commit 3bc8e35

Browse files
committed
add conflict resolver
1 parent c111eaa commit 3bc8e35

6 files changed

Lines changed: 545 additions & 8 deletions

File tree

src/agent/persistence.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -692,8 +692,6 @@ mod tests {
692692
backend_path
693693
};
694694

695-
let start_time = std::time::Instant::now();
696-
697695
let mut store = match backend_name {
698696
"Git" => BaseMemoryStore::init_with_thread_safe_git(
699697
&actual_path,
@@ -732,8 +730,6 @@ mod tests {
732730
.await
733731
.unwrap();
734732

735-
let duration = start_time.elapsed();
736-
737733
// Verify all memories were stored
738734
for i in 0..10 {
739735
let retrieved = store.get(&format!("perf_test_{}", i)).await.unwrap();

src/diff.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,66 @@ pub enum DiffResult {
1919
Modified(Vec<u8>, Vec<u8>, Vec<u8>),
2020
}
2121

22-
#[derive(Debug, PartialEq)]
22+
#[derive(Debug, PartialEq, Clone)]
2323
pub enum MergeResult {
2424
Added(Vec<u8>, Vec<u8>),
2525
Removed(Vec<u8>),
2626
Modified(Vec<u8>, Vec<u8>),
2727
Conflict(MergeConflict),
2828
}
2929

30-
#[derive(Debug, PartialEq)]
30+
#[derive(Debug, PartialEq, Clone)]
3131
pub struct MergeConflict {
3232
pub key: Vec<u8>,
3333
pub base_value: Option<Vec<u8>>,
3434
pub source_value: Option<Vec<u8>>,
3535
pub destination_value: Option<Vec<u8>>,
3636
}
37+
38+
/// Trait for resolving merge conflicts
39+
pub trait ConflictResolver {
40+
/// Resolve a conflict by returning the desired MergeResult
41+
/// Returns None if the conflict cannot be resolved and should remain as a conflict
42+
fn resolve_conflict(&self, conflict: &MergeConflict) -> Option<MergeResult>;
43+
}
44+
45+
/// Default conflict resolver that ignores all conflicts (treats them as no-ops)
46+
#[derive(Debug, Clone, Default)]
47+
pub struct IgnoreConflictsResolver;
48+
49+
impl ConflictResolver for IgnoreConflictsResolver {
50+
fn resolve_conflict(&self, conflict: &MergeConflict) -> Option<MergeResult> {
51+
// Ignore conflicts by keeping the destination value (no change applied)
52+
// This effectively means "do nothing" for this key
53+
match &conflict.destination_value {
54+
Some(value) => Some(MergeResult::Modified(conflict.key.clone(), value.clone())),
55+
None => Some(MergeResult::Removed(conflict.key.clone())),
56+
}
57+
}
58+
}
59+
60+
/// Conflict resolver that always takes the source value
61+
#[derive(Debug, Clone, Default)]
62+
pub struct TakeSourceResolver;
63+
64+
impl ConflictResolver for TakeSourceResolver {
65+
fn resolve_conflict(&self, conflict: &MergeConflict) -> Option<MergeResult> {
66+
match &conflict.source_value {
67+
Some(value) => Some(MergeResult::Modified(conflict.key.clone(), value.clone())),
68+
None => Some(MergeResult::Removed(conflict.key.clone())),
69+
}
70+
}
71+
}
72+
73+
/// Conflict resolver that always takes the destination value
74+
#[derive(Debug, Clone, Default)]
75+
pub struct TakeDestinationResolver;
76+
77+
impl ConflictResolver for TakeDestinationResolver {
78+
fn resolve_conflict(&self, conflict: &MergeConflict) -> Option<MergeResult> {
79+
match &conflict.destination_value {
80+
Some(value) => Some(MergeResult::Modified(conflict.key.clone(), value.clone())),
81+
None => Some(MergeResult::Removed(conflict.key.clone())),
82+
}
83+
}
84+
}

src/git/storage.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use std::sync::{Arc, Mutex};
2727
/// This storage implementation uses Git blobs to store serialized ProllyNode instances.
2828
/// Each node is stored as a Git blob object, with the blob's SHA-1 hash serving as the
2929
/// node's content-addressable identifier.
30+
#[derive(Debug)]
3031
pub struct GitNodeStorage<const N: usize> {
3132
_repository: Arc<Mutex<gix::Repository>>,
3233
cache: Mutex<LruCache<ValueDigest<N>, ProllyNode<N>>>,

src/rocksdb/storage.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const NODE_PREFIX: &[u8] = b"node:";
3030
///
3131
/// This storage implementation uses RocksDB as the persistent storage backend,
3232
/// with an LRU cache for frequently accessed nodes to improve performance.
33+
#[derive(Debug)]
3334
pub struct RocksDBNodeStorage<const N: usize> {
3435
db: Arc<DB>,
3536
cache: Arc<Mutex<LruCache<ValueDigest<N>, ProllyNode<N>>>>,

src/storage.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::sync::RwLock;
3131
/// # Type Parameters
3232
///
3333
/// - `N`: The size of the value digest.
34-
pub trait NodeStorage<const N: usize>: Send + Sync {
34+
pub trait NodeStorage<const N: usize>: Send + Sync + Clone {
3535
/// Retrieves a node from storage by its hash.
3636
///
3737
/// # Arguments
@@ -67,6 +67,7 @@ pub trait NodeStorage<const N: usize>: Send + Sync {
6767
/// # Type Parameters
6868
///
6969
/// - `N`: The size of the value digest.
70+
#[derive(Debug)]
7071
pub struct InMemoryNodeStorage<const N: usize> {
7172
map: HashMap<ValueDigest<N>, ProllyNode<N>>,
7273
configs: RwLock<HashMap<String, Vec<u8>>>,
@@ -127,6 +128,7 @@ impl<const N: usize> NodeStorage<N> for InMemoryNodeStorage<N> {
127128
}
128129
}
129130

131+
#[derive(Clone, Debug)]
130132
pub struct FileNodeStorage<const N: usize> {
131133
storage_dir: PathBuf,
132134
}

0 commit comments

Comments
 (0)