Coverage Summary for Class: ConflictResolution (com.georgeci.moneysurfer.sync.repository)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| ConflictResolution$Merged |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(7/7)
|
| ConflictResolution$Skip |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(2/2)
|
| ConflictResolution$TakeLocal |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(7/7)
|
| ConflictResolution$TakeRemote |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(7/7)
|
| Total |
100%
(4/4)
|
|
100%
(4/4)
|
100%
(23/23)
|
package com.georgeci.moneysurfer.sync.repository
import kotlin.time.Instant
/**
* Strategy for combining a local and a remote version of the same entity.
*
* Phase 2 ships only LWW (last-write-wins) by `updatedAt`. Future strategies
* (`Skip` for blocking inbox, `Merged` for field-level merges) plug in here.
*
* See SyncCoordinatorFAQ.md №5, sync.md §4.2.
*/
interface ConflictResolver {
fun <T : Any> resolve(
local: T?,
remote: T,
metadata: ConflictMetadata,
): ConflictResolution<T>
}
data class ConflictMetadata(
val entityType: String,
val entityId: String,
val localUpdatedAt: Instant?,
val remoteUpdatedAt: Instant,
)
sealed interface ConflictResolution<out T> {
data class TakeRemote<T>(val value: T) : ConflictResolution<T>
data class TakeLocal<T>(val value: T) : ConflictResolution<T>
data class Merged<T>(val value: T) : ConflictResolution<T>
/** Defer decision to a manual-resolution inbox (not implemented in Phase 2). */
data object Skip : ConflictResolution<Nothing>
}