use prikk_object::{NodeId, NodeKind};
use super::create::classify_create_then_mutate;
use super::delete::classify_mutate_then_delete;
use super::evidence_types::{ClassificationResult, NoPatchAlgebraEvidence, PatchAlgebraEvidence};
use super::facts::{deferred_reason, operation_facts};
use super::preimage::{
baseline_file_matches, invalid_preimage_class, is_create_after_delete_valid,
is_delete_preimage_valid,
};
use super::text_pair::classify_mode_and_text_edit;
use super::types::{
Action, ConflictWitnessKind, OperationFacts, PairClass, RequiredOrder, UnknownReason,
};
use super::witness::{
common_node, conflict, conflict_with_span, first_intersection, ordered, unknown,
unknown_from_facts,
};
use crate::node_lifecycle::NodeLifecycleState;
use crate::patch_replay::decode::DecodedPatchOperation;
pub(crate) fn classify_pair(
baseline: &NodeLifecycleState,
left: &DecodedPatchOperation,
right: &DecodedPatchOperation,
) -> ClassificationResult {
classify_pair_with_text_resolver(baseline, &NoPatchAlgebraEvidence, left, right)
}
pub(crate) fn classify_pair_with_text_resolver<R: PatchAlgebraEvidence>(
baseline: &NodeLifecycleState,
text_resolver: &R,
left: &DecodedPatchOperation,
right: &DecodedPatchOperation,
) -> ClassificationResult {
let left_facts = match operation_facts(left) {
Ok(facts) => facts,
Err(reason) => return Ok(unknown(reason, left, right, None, None)),
};
let right_facts = match operation_facts(right) {
Ok(facts) => facts,
Err(reason) => return Ok(unknown(reason, left, right, None, None)),
};
if let Some(reason) =
deferred_reason(&left_facts.action).or(deferred_reason(&right_facts.action))
{
return Ok(unknown(
reason,
left,
right,
common_node(&left_facts, &right_facts),
None,
));
}
if let Some(class) = classify_path_relation(baseline, &left_facts, &right_facts) {
return Ok(class);
}
match (left_facts.node_id, right_facts.node_id) {
(Some(left_node), Some(right_node)) if left_node == right_node => classify_same_node(
baseline,
text_resolver,
&left_facts,
&right_facts,
left_node,
),
_ => classify_cross_node(baseline, text_resolver, &left_facts, &right_facts),
}
}
fn classify_path_relation(
baseline: &NodeLifecycleState,
left: &OperationFacts,
right: &OperationFacts,
) -> Option<PairClass> {
if !left
.path_effects
.newly_occupied
.is_disjoint(&right.path_effects.newly_occupied)
{
let path = first_intersection(
&left.path_effects.newly_occupied,
&right.path_effects.newly_occupied,
);
return Some(conflict(
ConflictWitnessKind::SamePathCreate,
left,
right,
common_node(left, right),
path,
));
}
if !left
.path_effects
.freed
.is_disjoint(&right.path_effects.required_free)
{
let path = first_intersection(&left.path_effects.freed, &right.path_effects.required_free);
if !is_delete_preimage_valid(baseline, left)
|| !is_create_after_delete_valid(baseline, left, right)
{
return Some(conflict(
ConflictWitnessKind::LiveStateMismatch,
left,
right,
common_node(left, right),
path,
));
}
return Some(ordered(
RequiredOrder::LeftBeforeRight,
ConflictWitnessKind::LiveStateMismatch,
left,
right,
common_node(left, right),
path,
));
}
if !right
.path_effects
.freed
.is_disjoint(&left.path_effects.required_free)
{
let path = first_intersection(&right.path_effects.freed, &left.path_effects.required_free);
if !is_delete_preimage_valid(baseline, right)
|| !is_create_after_delete_valid(baseline, right, left)
{
return Some(conflict(
ConflictWitnessKind::LiveStateMismatch,
left,
right,
common_node(left, right),
path,
));
}
return Some(ordered(
RequiredOrder::RightBeforeLeft,
ConflictWitnessKind::LiveStateMismatch,
left,
right,
common_node(left, right),
path,
));
}
if !left
.path_effects
.freed
.is_disjoint(&right.path_effects.freed)
{
let path = first_intersection(&left.path_effects.freed, &right.path_effects.freed);
return Some(conflict(
ConflictWitnessKind::DeleteMutationConflict,
left,
right,
common_node(left, right),
path,
));
}
None
}
fn classify_cross_node<R: PatchAlgebraEvidence>(
baseline: &NodeLifecycleState,
text_resolver: &R,
left: &OperationFacts,
right: &OperationFacts,
) -> ClassificationResult {
if let Some(class) = invalid_preimage_class(baseline, text_resolver, left, right)? {
return Ok(class);
}
if let Some(class) = invalid_preimage_class(baseline, text_resolver, right, left)? {
return Ok(class);
}
Ok(PairClass::Independent)
}
fn classify_same_node<R: PatchAlgebraEvidence>(
baseline: &NodeLifecycleState,
text_resolver: &R,
left: &OperationFacts,
right: &OperationFacts,
node_id: NodeId,
) -> ClassificationResult {
if let Some(class) = classify_create_then_mutate(baseline, text_resolver, left, right, node_id)
{
return class;
}
if let Some(class) = classify_mutate_then_delete(baseline, left, right, node_id) {
return Ok(class);
}
match (&left.action, &right.action) {
(
Action::EditText {
span_id: left_span, ..
},
Action::EditText {
span_id: right_span,
..
},
) if left_span == right_span => Ok(conflict_with_span(
ConflictWitnessKind::TextSpanOverlap,
left,
right,
node_id,
*left_span,
)),
(Action::EditText { .. }, Action::EditText { .. }) => Ok(unknown_from_facts(
UnknownReason::SameNodeTextCommutationDeferred,
left,
right,
Some(node_id),
None,
)),
(Action::ChangePerm { old_mode, .. }, Action::ReplaceBinary { old_blob_id, .. })
| (Action::ReplaceBinary { old_blob_id, .. }, Action::ChangePerm { old_mode, .. }) => {
if baseline_file_matches(
baseline,
node_id,
NodeKind::BinaryFile,
*old_blob_id,
*old_mode,
) {
Ok(PairClass::Independent)
} else {
Ok(conflict(
ConflictWitnessKind::LiveStateMismatch,
left,
right,
Some(node_id),
None,
))
}
}
(Action::ChangePerm { old_mode, .. }, edit @ Action::EditText { .. })
| (edit @ Action::EditText { .. }, Action::ChangePerm { old_mode, .. }) => {
classify_mode_and_text_edit(
baseline,
text_resolver,
left,
right,
node_id,
*old_mode,
edit,
)
}
_ => Ok(conflict(
ConflictWitnessKind::UnknownRelation,
left,
right,
Some(node_id),
None,
)),
}
}