use std::sync::Arc;
use crate::sync::{mode::Mode, status::StatusVersions};
use super::{
analysis::{
ExistingItem, ExistingItemPresence, ItemAnalysis, ItemSource, NewItem, PropertyChange,
ResolvedMapping, SideState, StatusAction,
},
operation::{
DeleteItem, ItemOp, MappingUidSource, PropertyOp, PropertyOpKind, StatusOnly, StatusWrite,
StorageWrite, WriteItem, resource_name,
},
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 on_complete = on_complete.map(DeletionBarrier::completion_handle);
let mapping = mapping.clone();
let mapping_uid = MappingUidSource::Immediate(mapping_uid);
let (property, kind) = match change {
PropertyChange::OnNeither { property, .. } => (property, PropertyOpKind::ClearStatus),
PropertyChange::OnlyA {
property, value_a, ..
} => (
property,
PropertyOpKind::Write {
value: value_a,
side: Side::B,
},
),
PropertyChange::OnlyB { property, .. } => {
(property, PropertyOpKind::Delete { side: Side::B })
}
PropertyChange::OnBoth {
property,
value_a,
value_b,
previous,
} => {
if value_a == value_b {
return match previous {
Some(prev) if prev == value_a => None, _ => Some(PropertyOp {
property,
mapping,
mapping_uid,
on_complete,
kind: PropertyOpKind::UpdateStatus { value: value_a },
}),
};
}
(
property,
PropertyOpKind::Write {
value: value_a,
side: Side::B,
},
)
}
};
Some(PropertyOp {
property,
mapping,
mapping_uid,
on_complete,
kind,
})
}
}
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 } => {
let name = resource_name(&a.state.version.href);
ItemOp::Write(WriteItem {
source: ItemSource::from(a),
target_side: Side::B,
storage_write: StorageWrite::Create {
collection: mapping.b().href().clone(),
resource_name: Some(name),
},
status_write: StatusWrite::Insert,
mapping_uid: mapping_uid_source,
on_complete,
})
}
NewItem::OnlyB { b } => ItemOp::Delete(DeleteItem {
uid: b.state.uid.clone(),
target: b.state.version,
side: Side::B,
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::Write(WriteItem {
source: ItemSource::from(a),
target_side: Side::B,
storage_write: StorageWrite::Update {
target: b.state.version,
},
status_write: StatusWrite::Insert,
mapping_uid: mapping_uid_source,
on_complete,
})
}
}
}
}
#[allow(clippy::too_many_lines)]
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 name = resource_name(&a.state().version.href);
Some(ItemOp::Write(WriteItem {
source: a.into_item_source(),
target_side: Side::B,
storage_write: StorageWrite::Create {
collection: mapping.b().href().clone(),
resource_name: Some(name),
},
status_write: StatusWrite::Update { old: status.into() },
mapping_uid,
on_complete,
}))
}
ExistingItemPresence::OnlyB { b } => Some(ItemOp::Delete(DeleteItem {
uid: b.state().uid.clone(),
target: b.into_item_ver(),
side: Side::B,
mapping_uid,
on_complete,
})),
ExistingItemPresence::OnBoth { a, b } => match (a, b) {
(a @ SideState::Unchanged { .. }, SideState::Unchanged { state: b }) => {
if b.version == status.b {
None
} else {
let old: StatusVersions = status.into();
let target = old.b.clone();
Some(ItemOp::Write(WriteItem {
source: a.into_item_source(),
target_side: Side::B,
storage_write: StorageWrite::Update { target },
status_write: StatusWrite::Update { old },
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,
})),
(
a @ (SideState::Unchanged { .. } | SideState::Changed { .. }),
SideState::Changed { state: b, .. },
) => Some(ItemOp::Write(WriteItem {
source: a.into_item_source(),
target_side: Side::B,
storage_write: StorageWrite::Update { target: b.version },
status_write: StatusWrite::Update { old: status.into() },
mapping_uid,
on_complete,
})),
(a @ SideState::Changed { .. }, SideState::Unchanged { .. }) => {
let old: StatusVersions = status.into();
let target = old.b.clone();
Some(ItemOp::Write(WriteItem {
source: a.into_item_source(),
target_side: Side::B,
storage_write: StorageWrite::Update { target },
status_write: StatusWrite::Update { old },
mapping_uid,
on_complete,
}))
}
},
}
}
}