use std::sync::Arc;
use crate::sync::{mode::Mode, status::StatusVersions};
use super::{
analysis::{
ExistingItem, ExistingItemPresence, ItemAnalysis, ItemSource, NewItem, PropertyChange,
ResolvedMapping, SideState, StatusAction,
},
operation::{DeleteB, ItemOp, MappingUidSource, PropertyOp, StatusOnly, WriteInB, WriteMode},
ordering::{DeletionBarrier, DeletionCompletionHandle},
status::{MappingUid, Side},
};
pub struct OneWaySync;
impl Mode for OneWaySync {
fn decide_item_action(
&self,
analysis: ItemAnalysis,
mapping: &Arc<ResolvedMapping>,
mapping_uid_source: MappingUidSource,
on_complete: Option<&DeletionBarrier>,
) -> Option<ItemOp> {
let on_complete_handle = on_complete.map(DeletionBarrier::completion_handle);
match analysis {
ItemAnalysis::New(new_item) => Some(Self::plan_new_item(
new_item,
mapping,
mapping_uid_source,
on_complete_handle,
)),
ItemAnalysis::Existing(existing) => {
Self::plan_existing_item(existing, mapping, on_complete_handle)
}
}
}
fn decide_property_action(
&self,
change: PropertyChange,
mapping: &Arc<ResolvedMapping>,
mapping_uid: MappingUid,
on_complete: Option<&DeletionBarrier>,
) -> Option<PropertyOp> {
let deletion_completion = on_complete.map(DeletionBarrier::completion_handle);
match change {
PropertyChange::OnNeither { property, .. } => Some(PropertyOp::ClearStatus {
property,
mapping: mapping.clone(),
mapping_uid: MappingUidSource::Immediate(mapping_uid),
deletion_completion,
}),
PropertyChange::OnlyA {
property, value_a, ..
} => Some(PropertyOp::Write {
property,
value: value_a,
side: Side::B,
mapping: mapping.clone(),
mapping_uid: MappingUidSource::Immediate(mapping_uid),
deletion_completion,
}),
PropertyChange::OnlyB { property, .. } => Some(PropertyOp::Delete {
property,
side: Side::B,
mapping: mapping.clone(),
mapping_uid: MappingUidSource::Immediate(mapping_uid),
deletion_completion,
}),
PropertyChange::OnBoth {
property,
value_a,
value_b,
previous,
} => {
if value_a == value_b {
match previous {
Some(prev) if prev == value_a => None, _ => Some(PropertyOp::UpdateStatus {
property,
value: value_a,
mapping: mapping.clone(),
mapping_uid: MappingUidSource::Immediate(mapping_uid),
deletion_completion,
}),
}
} else {
Some(PropertyOp::Write {
property,
value: value_a,
side: Side::B,
mapping: mapping.clone(),
mapping_uid: MappingUidSource::Immediate(mapping_uid),
deletion_completion,
})
}
}
}
}
}
impl OneWaySync {
fn plan_new_item(
item: NewItem,
mapping: &Arc<ResolvedMapping>,
mapping_uid_source: MappingUidSource,
on_complete: Option<DeletionCompletionHandle>,
) -> ItemOp {
match item {
NewItem::OnlyA { a } => ItemOp::WriteInB(WriteInB {
source_a: ItemSource::from(a),
mode: WriteMode::CreateNew {
collection: mapping.b().href().clone(),
},
mapping_uid: mapping_uid_source,
on_complete,
}),
NewItem::OnlyB { b } => ItemOp::DeleteB(DeleteB {
uid: b.state.uid.clone(),
target_b: b.state.version,
mapping_uid: mapping_uid_source,
on_complete,
}),
NewItem::OnBoth { a, b } => {
if a.state.hash == b.state.hash {
ItemOp::StatusOnly(StatusOnly {
action: StatusAction::Insert {
uid: a.state.uid.clone(),
hash: a.state.hash.clone(),
versions: StatusVersions {
a: a.state.version,
b: b.state.version,
},
},
mapping_uid: mapping_uid_source,
on_complete,
})
} else {
ItemOp::WriteInB(WriteInB {
source_a: ItemSource::from(a),
mode: WriteMode::UpdateNew {
target: b.state.version,
},
mapping_uid: mapping_uid_source,
on_complete,
})
}
}
}
}
fn plan_existing_item(
item: ExistingItem,
mapping: &Arc<ResolvedMapping>,
on_complete: Option<DeletionCompletionHandle>,
) -> Option<ItemOp> {
let mapping_uid = MappingUidSource::Immediate(item.mapping_uid());
let status = item.status;
match item.current {
ExistingItemPresence::OnNeither => Some(ItemOp::StatusOnly(StatusOnly {
action: StatusAction::Clear { uid: status.uid },
mapping_uid,
on_complete,
})),
ExistingItemPresence::OnlyA { a } => {
let (state, data) = match a {
SideState::Changed { state, data } => (state, Some(data)),
SideState::Unchanged { state } => (state, None),
};
Some(ItemOp::WriteInB(WriteInB {
source_a: ItemSource { state, data },
mode: WriteMode::CreateRefresh {
collection: mapping.b().href().clone(),
old: status.into(),
},
mapping_uid,
on_complete,
}))
}
ExistingItemPresence::OnlyB { b } => Some(ItemOp::DeleteB(DeleteB {
uid: b.state().uid.clone(),
target_b: b.into_item_ver(),
mapping_uid,
on_complete,
})),
ExistingItemPresence::OnBoth { a, b } => match (a, b) {
(SideState::Unchanged { state }, SideState::Unchanged { state: b }) => {
let ver_b = b.version;
if ver_b == status.b {
None
} else {
Some(ItemOp::WriteInB(WriteInB {
source_a: ItemSource { state, data: None },
mode: WriteMode::UpdateExisting { old: status.into() },
mapping_uid,
on_complete,
}))
}
}
(SideState::Unchanged { state }, SideState::Changed { state: b, .. }) => {
Some(ItemOp::WriteInB(WriteInB {
source_a: ItemSource { state, data: None },
mode: WriteMode::UpdateForce {
target: b.version,
old: status.into(),
},
mapping_uid,
on_complete,
}))
}
(
SideState::Changed { state: a, .. },
SideState::Changed { state: b, .. } | SideState::Unchanged { state: b },
) if a.hash == b.hash => Some(ItemOp::StatusOnly(StatusOnly {
action: StatusAction::Update {
hash: a.hash.clone(),
old: status.into(),
new: StatusVersions {
a: a.version,
b: b.version,
},
},
mapping_uid,
on_complete,
})),
(SideState::Changed { state, data }, SideState::Unchanged { .. }) => {
Some(ItemOp::WriteInB(WriteInB {
source_a: ItemSource {
state,
data: Some(data),
},
mode: WriteMode::UpdateExisting { old: status.into() },
mapping_uid,
on_complete,
}))
}
(SideState::Changed { state, data }, SideState::Changed { state: b, .. }) => {
Some(ItemOp::WriteInB(WriteInB {
source_a: ItemSource {
state,
data: Some(data),
},
mode: WriteMode::UpdateForce {
target: b.version,
old: status.into(),
},
mapping_uid,
on_complete,
}))
}
},
}
}
}