use alloc::{borrow::Cow, string::String};
use core::cmp::Reverse;
use crate::{
prop::{IcalPropKind, IcalPropName},
tree::merge::{IcalComponentPath, IcalMergeAction, IcalPropPath},
};
pub(super) struct Op<'a> {
pub(super) action: IcalMergeAction<'a>,
pub(super) source: Option<IcalPropPath<'a>>,
pub(super) slot: Slot,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) enum Slot {
Component,
Prop,
Value,
Items,
Param { name: String, at: usize },
}
impl<'a> Op<'a> {
pub(super) fn path(&self) -> &IcalComponentPath<'a> {
match &self.action {
IcalMergeAction::ComponentAdded { at } | IcalMergeAction::ComponentRemoved { at } => at,
IcalMergeAction::PropAdded { at, .. }
| IcalMergeAction::PropRemoved { at, .. }
| IcalMergeAction::ValueChanged { at, .. }
| IcalMergeAction::ValueItemAdded { at, .. }
| IcalMergeAction::ValueItemRemoved { at, .. }
| IcalMergeAction::ParamAdded { at, .. }
| IcalMergeAction::ParamRemoved { at, .. }
| IcalMergeAction::ParamChanged { at, .. } => &at.component,
}
}
pub(super) fn prop(&self) -> Option<&IcalPropPath<'a>> {
self.action.prop_path()
}
pub(super) fn is_addition(&self) -> bool {
matches!(self.action, IcalMergeAction::PropAdded { .. })
}
pub(super) fn reaches(&self, below: &Op<'_>) -> bool {
below.path().0.starts_with(&self.path().0)
&& (self.path() == below.path() || !below.action.is_removal())
}
pub(super) fn scraps(&self, other: &Op<'_>) -> bool {
if !self.action.is_removal() {
return false;
}
match (&self.slot, &other.slot) {
(Slot::Component, Slot::Component) | (Slot::Prop, Slot::Prop) => {
!other.action.is_removal()
}
(Slot::Component, _) | (Slot::Prop, _) => true,
_ => !other.action.is_removal(),
}
}
pub(super) fn across_the_series(&self, other: &Op<'_>) -> bool {
let (Some(one), Some(two)) = (self.path().0.last(), other.path().0.last()) else {
return false;
};
let (Some(our_uid), Some(their_uid)) =
(one.key.split('/').next(), two.key.split('/').next())
else {
return false;
};
if one.name != two.name
|| our_uid != their_uid
|| one.key.contains('/') == two.key.contains('/')
{
return false;
}
let series = if one.key.contains('/') { other } else { self };
series.defines_the_set()
}
fn defines_the_set(&self) -> bool {
let Some(at) = self.prop() else {
return true;
};
matches!(
IcalPropName::from(Cow::Owned(at.name.to_ascii_uppercase())),
IcalPropName::Kind(
IcalPropKind::DtStart
| IcalPropKind::DtEnd
| IcalPropKind::Duration
| IcalPropKind::RRule
| IcalPropKind::RDate
| IcalPropKind::ExDate
)
)
}
pub(super) fn replay_order(&self) -> (u8, Reverse<usize>) {
let last = match &self.action {
IcalMergeAction::ComponentRemoved { at } => {
at.0.last()
.and_then(|step| step.key.parse().ok())
.unwrap_or(0)
}
IcalMergeAction::PropRemoved { at, .. } => at.index,
_ => return (0, Reverse(0)),
};
(1, Reverse(last))
}
}