use std::sync::Arc;
use crate::Href;
use crate::base::{ItemVersion, Property};
use crate::sync::analysis::{ItemSource, ResolvedMapping, StatusAction};
use crate::sync::conflict::ConflictInfo;
use crate::sync::ordering::CompletionDroppedError;
use crate::sync::status::StatusVersions;
use super::ordering::{CompletionHandle, DeletionCompletionHandle, DeletionWaitHandle, WaitHandle};
use super::status::{MappingUid, Side};
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum Operation {
FlushStaleMappings { stale_uids: Vec<MappingUid> },
Collection(CollectionOp),
Item(ItemOp),
Property(PropertyOp),
}
impl Operation {
#[must_use]
pub fn is_conflict(&self) -> bool {
matches!(
self,
Operation::Item(ItemOp::Conflict { .. })
| Operation::Property(PropertyOp::PropertyConflict { .. })
)
}
}
#[derive(Debug, Clone)]
pub enum CollectionOp {
SaveToStatus {
mapping: Arc<ResolvedMapping>,
completion: CompletionHandle,
},
CreateInOne {
mapping: Arc<ResolvedMapping>,
side: Side,
completion: CompletionHandle,
},
CreateInBoth {
mapping: Arc<ResolvedMapping>,
completion: CompletionHandle,
},
Delete {
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUid,
side: Side,
wait_for_items: DeletionWaitHandle,
},
NoAction { mapping_uid: MappingUid },
StoreSyncToken {
mapping_uid: MappingUidSource,
side: Side,
token: String,
wait_for_items: DeletionWaitHandle,
},
}
#[derive(Debug, Clone)]
pub enum MappingUidSource {
Immediate(MappingUid),
Deferred(WaitHandle),
}
impl MappingUidSource {
#[must_use]
pub fn immediate(&self) -> Option<MappingUid> {
match self {
MappingUidSource::Immediate(uid) => Some(*uid),
MappingUidSource::Deferred(_) => None,
}
}
#[must_use]
pub fn wait_handle(&self) -> Option<&WaitHandle> {
match self {
MappingUidSource::Immediate(_) => None,
MappingUidSource::Deferred(handle) => Some(handle),
}
}
pub async fn resolve(self) -> Result<MappingUid, CompletionDroppedError> {
match self {
MappingUidSource::Immediate(uid) => Ok(uid),
MappingUidSource::Deferred(mut handle) => handle.wait().await,
}
}
}
#[derive(Debug, Clone)]
pub enum WriteMode {
CreateNew { collection: Href },
CreateRefresh {
collection: Href,
old: StatusVersions,
},
UpdateNew { target: ItemVersion },
UpdateExisting { old: StatusVersions },
UpdateForce {
target: ItemVersion,
old: StatusVersions,
},
}
#[derive(Debug, Clone)]
pub struct WriteInA {
pub source_b: ItemSource,
pub mode: WriteMode,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<WriteInA> for ItemOp {
fn from(inner: WriteInA) -> Self {
ItemOp::WriteInA(inner)
}
}
#[derive(Debug, Clone)]
pub struct WriteInB {
pub source_a: ItemSource,
pub mode: WriteMode,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<WriteInB> for ItemOp {
fn from(inner: WriteInB) -> Self {
ItemOp::WriteInB(inner)
}
}
#[derive(Debug, Clone)]
pub struct DeleteA {
pub target_a: ItemVersion,
pub uid: String,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<DeleteA> for ItemOp {
fn from(inner: DeleteA) -> Self {
ItemOp::DeleteA(inner)
}
}
#[derive(Debug, Clone)]
pub struct DeleteB {
pub target_b: ItemVersion,
pub uid: String,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<DeleteB> for ItemOp {
fn from(inner: DeleteB) -> Self {
ItemOp::DeleteB(inner)
}
}
#[derive(Debug, Clone)]
pub struct StatusOnly {
pub action: StatusAction,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<StatusOnly> for ItemOp {
fn from(inner: StatusOnly) -> Self {
ItemOp::StatusOnly(inner)
}
}
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum ItemOp {
WriteInA(WriteInA),
WriteInB(WriteInB),
DeleteA(DeleteA),
DeleteB(DeleteB),
StatusOnly(StatusOnly),
Conflict {
info: ConflictInfo,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
}
impl ItemOp {
#[must_use]
pub fn wait_handle(&self) -> Option<&WaitHandle> {
match self {
ItemOp::WriteInA(inner) => inner.mapping_uid.wait_handle(),
ItemOp::WriteInB(inner) => inner.mapping_uid.wait_handle(),
ItemOp::StatusOnly(inner) => inner.mapping_uid.wait_handle(),
_ => None,
}
}
#[must_use]
pub fn uid(&self) -> Option<&str> {
match self {
ItemOp::WriteInA(inner) => Some(&inner.source_b.state.uid),
ItemOp::WriteInB(inner) => Some(&inner.source_a.state.uid),
ItemOp::DeleteA(inner) => Some(&inner.uid),
ItemOp::DeleteB(inner) => Some(&inner.uid),
ItemOp::StatusOnly(inner) => match &inner.action {
StatusAction::Insert { uid, .. } | StatusAction::Clear { uid } => Some(uid),
StatusAction::Update { .. } => None,
},
ItemOp::Conflict { info, .. } => Some(&info.a.state.uid),
}
}
#[must_use]
pub fn mapping_uid(&self) -> MappingUidSource {
match self {
ItemOp::WriteInA(inner) => inner.mapping_uid.clone(),
ItemOp::WriteInB(inner) => inner.mapping_uid.clone(),
ItemOp::DeleteA(inner) => inner.mapping_uid.clone(),
ItemOp::DeleteB(inner) => inner.mapping_uid.clone(),
ItemOp::StatusOnly(inner) => inner.mapping_uid.clone(),
ItemOp::Conflict { mapping_uid, .. } => mapping_uid.clone(),
}
}
}
#[derive(Debug, Clone)]
pub enum PropertyOp {
Write {
property: Property,
value: String,
side: Side,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
Delete {
property: Property,
side: Side,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
ClearStatus {
property: Property,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
UpdateStatus {
property: Property,
value: String,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
PropertyConflict {
property: Property,
value_a: String,
value_b: String,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
deletion_completion: Option<DeletionCompletionHandle>,
},
}
impl std::fmt::Display for CollectionOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CollectionOp::SaveToStatus { mapping, .. } => {
write!(f, "save collection '{}' to status", mapping.alias())
}
CollectionOp::CreateInOne { mapping, side, .. } => {
let alias = mapping.alias();
write!(f, "create collection '{alias}' in storage {side}",)
}
CollectionOp::CreateInBoth { mapping, .. } => {
let alias = mapping.alias();
write!(f, "create collection '{alias}' in both storages",)
}
CollectionOp::Delete { mapping, side, .. } => {
let alias = mapping.alias();
write!(f, "delete collection '{alias}' from storage {side}",)
}
CollectionOp::NoAction { mapping_uid } => {
write!(f, "no action for collection (uid: {mapping_uid:?})")
}
CollectionOp::StoreSyncToken {
mapping_uid, side, ..
} => {
write!(
f,
"store sync token for collection (uid: {mapping_uid:?}) side {side}"
)
}
}
}
}
impl std::fmt::Display for ItemOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemOp::WriteInA(inner) => {
let uid = &inner.source_b.state.uid;
match &inner.mode {
WriteMode::CreateNew { .. } | WriteMode::CreateRefresh { .. } => {
write!(f, "create in storage A (uid: {uid})")
}
WriteMode::UpdateNew { .. }
| WriteMode::UpdateExisting { .. }
| WriteMode::UpdateForce { .. } => {
write!(f, "update in storage A (uid: {uid})")
}
}
}
ItemOp::WriteInB(inner) => {
let uid = &inner.source_a.state.uid;
match &inner.mode {
WriteMode::CreateNew { .. } | WriteMode::CreateRefresh { .. } => {
write!(f, "create in storage B (uid: {uid})")
}
WriteMode::UpdateNew { .. }
| WriteMode::UpdateExisting { .. }
| WriteMode::UpdateForce { .. } => {
write!(f, "update in storage B (uid: {uid})")
}
}
}
ItemOp::DeleteA(inner) => {
write!(f, "delete from storage A (uid: {})", inner.uid)
}
ItemOp::DeleteB(inner) => {
write!(f, "delete from storage B (uid: {})", inner.uid)
}
ItemOp::StatusOnly(inner) => {
write!(f, "status only: {}", inner.action)
}
ItemOp::Conflict { info, .. } => {
write!(f, "conflict (uid: {})", info.a.state.uid)
}
}
}
}
impl std::fmt::Display for PropertyOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PropertyOp::Write {
property,
value,
side,
mapping,
..
} => {
let alias = mapping.alias();
write!(
f,
"write property {property:?} to storage {side} on collection '{alias}': {value}",
)
}
PropertyOp::Delete {
property,
side,
mapping,
..
} => {
let alias = mapping.alias();
write!(
f,
"delete property {property:?} from storage {side} on collection '{alias}'",
)
}
PropertyOp::ClearStatus {
property, mapping, ..
} => {
let alias = mapping.alias();
write!(
f,
"clear property {property:?} from status for collection '{alias}'",
)
}
PropertyOp::UpdateStatus {
property, mapping, ..
} => {
let alias = mapping.alias();
write!(
f,
"update property {property:?} in status for collection '{alias}'",
)
}
PropertyOp::PropertyConflict {
property, mapping, ..
} => {
let alias = mapping.alias();
write!(f, "property {property:?} conflict on collection '{alias}'")
}
}
}
}