use super::cid::Cid;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq)]
pub enum Mutation {
Upsert { key: Vec<u8>, val: Vec<u8> },
Delete { key: Vec<u8> },
}
impl Mutation {
pub fn key(&self) -> &[u8] {
match self {
Mutation::Upsert { key, .. } => key,
Mutation::Delete { key } => key,
}
}
pub fn is_delete(&self) -> bool {
matches!(self, Mutation::Delete { .. })
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Diff {
Added { key: Vec<u8>, val: Vec<u8> },
Removed { key: Vec<u8>, val: Vec<u8> },
Changed {
key: Vec<u8>,
old: Vec<u8>,
new: Vec<u8>,
},
}
impl Diff {
pub fn key(&self) -> &[u8] {
match self {
Diff::Added { key, .. } | Diff::Removed { key, .. } | Diff::Changed { key, .. } => key,
}
}
}
#[derive(Clone, Debug)]
pub struct Conflict {
pub key: Vec<u8>,
pub base: Option<Vec<u8>>,
pub left: Option<Vec<u8>>,
pub right: Option<Vec<u8>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Resolution {
Value(Vec<u8>),
Delete,
Unresolved,
}
impl Resolution {
pub fn value(value: impl Into<Vec<u8>>) -> Self {
Self::Value(value.into())
}
pub fn delete() -> Self {
Self::Delete
}
pub fn unresolved() -> Self {
Self::Unresolved
}
}
pub type Resolver = Box<dyn Fn(&Conflict) -> Resolution>;
pub mod resolver {
use super::{Conflict, Resolution};
pub fn prefer_left(conflict: &Conflict) -> Resolution {
match &conflict.left {
Some(value) => Resolution::value(value.clone()),
None => Resolution::delete(),
}
}
pub fn prefer_right(conflict: &Conflict) -> Resolution {
match &conflict.right {
Some(value) => Resolution::value(value.clone()),
None => Resolution::delete(),
}
}
pub fn delete_wins(conflict: &Conflict) -> Resolution {
if conflict.left.is_none() || conflict.right.is_none() {
Resolution::delete()
} else {
Resolution::unresolved()
}
}
pub fn update_wins(conflict: &Conflict) -> Resolution {
match (&conflict.left, &conflict.right) {
(Some(value), None) | (None, Some(value)) => Resolution::value(value.clone()),
_ => Resolution::unresolved(),
}
}
}
use super::transaction::TransactionConflict;
#[derive(Debug)]
pub enum Error {
NotFound(Cid),
InvalidNode,
Deserialize(String),
Serialize(String),
Store(Box<dyn std::error::Error + Send + Sync>),
CidMismatch { expected: Cid, actual: Cid },
Conflict(Conflict),
BufferFull,
UnsortedInput { previous: Vec<u8>, next: Vec<u8> },
MissingNamedRoots { names: Vec<Vec<u8>> },
InvalidSnapshotBundle(String),
UnsupportedTransactions { store: &'static str },
TransactionConflict(TransactionConflict),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::NotFound(cid) => write!(f, "node not found: {:?}", cid),
Error::InvalidNode => write!(f, "invalid node structure"),
Error::Deserialize(e) => write!(f, "deserialize error: {}", e),
Error::Serialize(e) => write!(f, "serialize error: {}", e),
Error::Store(e) => write!(f, "storage error: {}", e),
Error::CidMismatch { expected, actual } => {
write!(
f,
"content CID mismatch: expected {:?}, got {:?}",
expected, actual
)
}
Error::Conflict(c) => write!(f, "merge conflict at key: {:?}", c.key),
Error::BufferFull => write!(f, "mutation buffer is full"),
Error::UnsortedInput { previous, next } => write!(
f,
"sorted input keys are out of order: previous={:?} next={:?}",
previous, next
),
Error::MissingNamedRoots { names } => {
write!(f, "missing named roots for retention policy: {:?}", names)
}
Error::InvalidSnapshotBundle(message) => {
write!(f, "invalid snapshot bundle: {message}")
}
Error::UnsupportedTransactions { store } => {
write!(f, "store does not support strict transactions: {store}")
}
Error::TransactionConflict(conflict) => {
write!(
f,
"transaction conflict for named root: {:?}",
conflict.name
)
}
}
}
}
impl std::error::Error for Error {}