use std::sync::Arc;
use crate::{
Href,
base::ItemVersion,
property::Property,
sync::{
analysis::{ItemSource, ResolvedMapping, StatusAction},
conflict::ConflictInfo,
ordering::{
CompletionDroppedError, CompletionHandle, DeletionCompletionHandle, DeletionWaitHandle,
WaitHandle,
},
status::{MappingUid, Side, StatusVersions},
},
};
#[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 {
kind: PropertyOpKind::Conflict { .. },
..
})
)
}
}
#[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,
},
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 StorageWrite {
Create {
collection: Href,
resource_name: Option<Href>,
},
Update { target: ItemVersion },
}
pub(super) fn resource_name(href: &str) -> String {
href.rsplit('/').next().unwrap_or(href).to_string()
}
#[derive(Debug, Clone)]
pub enum StatusWrite {
Insert,
Update { old: StatusVersions },
}
#[derive(Debug, Clone)]
pub struct WriteItem {
pub source: ItemSource,
pub target_side: Side,
pub storage_write: StorageWrite,
pub status_write: StatusWrite,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<WriteItem> for ItemOp {
fn from(inner: WriteItem) -> Self {
ItemOp::Write(inner)
}
}
#[derive(Debug, Clone)]
pub struct DeleteItem {
pub target: ItemVersion,
pub side: Side,
pub uid: String,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
}
impl From<DeleteItem> for ItemOp {
fn from(inner: DeleteItem) -> Self {
ItemOp::Delete(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 {
Write(WriteItem),
Delete(DeleteItem),
StatusOnly(StatusOnly),
Conflict {
info: ConflictInfo,
mapping: Arc<ResolvedMapping>,
mapping_uid: MappingUidSource,
on_complete: Option<DeletionCompletionHandle>,
},
}
impl ItemOp {
#[must_use]
pub fn wait_handle(&self) -> Option<&WaitHandle> {
match self {
ItemOp::Write(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::Write(inner) => Some(&inner.source.state.uid),
ItemOp::Delete(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::Write(inner) => inner.mapping_uid.clone(),
ItemOp::Delete(inner) => inner.mapping_uid.clone(),
ItemOp::StatusOnly(inner) => inner.mapping_uid.clone(),
ItemOp::Conflict { mapping_uid, .. } => mapping_uid.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct PropertyOp {
pub property: Property,
pub mapping: Arc<ResolvedMapping>,
pub mapping_uid: MappingUidSource,
pub on_complete: Option<DeletionCompletionHandle>,
pub kind: PropertyOpKind,
}
#[derive(Debug, Clone)]
pub enum PropertyOpKind {
Write { value: String, side: Side },
Delete { side: Side },
ClearStatus,
UpdateStatus { value: String },
Conflict { value_a: String, value_b: String },
}
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::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::Write(inner) => {
let side = inner.target_side;
match &inner.storage_write {
StorageWrite::Create { .. } => write!(f, "write to {side} (create new)"),
StorageWrite::Update { .. } => write!(f, "write to {side} (update existing)"),
}
}
ItemOp::Delete(inner) => {
write!(f, "delete from {}", inner.side)
}
ItemOp::StatusOnly(_) => {
write!(f, "status only")
}
ItemOp::Conflict { .. } => {
write!(f, "conflict")
}
}
}
}
impl std::fmt::Display for PropertyOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.kind {
PropertyOpKind::Write { value, side } => {
write!(f, "write to {side} (value: {value:?})")
}
PropertyOpKind::Delete { side } => {
write!(f, "delete from {side}")
}
PropertyOpKind::ClearStatus => {
write!(f, "clear from status")
}
PropertyOpKind::UpdateStatus { value } => {
write!(f, "update in status (value: {value:?})")
}
PropertyOpKind::Conflict { value_a, value_b } => {
write!(f, "conflict (a: {value_a:?}, b: {value_b:?})")
}
}
}
}