use prikk_object::ObjectId;
use crate::patch_algebra::{
MergeEvidenceItem, MergeEvidenceOperationKind, MergeEvidenceOutcome, MergeEvidenceProofPhase,
MergeEvidenceReasonCode, MergeEvidenceReport, MergeEvidenceScope, MergeEvidenceSide,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MergeEvidenceDisplay {
pub baseline_block_id: ObjectId,
pub left_selector: MergeEvidenceDisplaySelector,
pub right_selector: MergeEvidenceDisplaySelector,
pub outcome: &'static str,
pub reason: Option<&'static str>,
pub left_operation_count: usize,
pub right_operation_count: usize,
pub items: Vec<MergeEvidenceDisplayItem>,
}
impl MergeEvidenceDisplay {
#[must_use]
pub(crate) fn is_confluent(&self) -> bool {
self.outcome == outcome_name(MergeEvidenceOutcome::Confluent)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MergeEvidenceDisplaySelector {
pub selector: String,
pub target_block_id: ObjectId,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MergeEvidenceDisplayItem {
pub side: &'static str,
pub operation: Option<MergeEvidenceDisplayOperation>,
pub peer_operation: Option<MergeEvidenceDisplayOperation>,
pub outcome: &'static str,
pub evidence_scope: Option<&'static str>,
pub proof_phase: &'static str,
pub reason_code: &'static str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MergeEvidenceDisplayOperation {
pub index: usize,
pub op_seq: Option<u32>,
pub kind: Option<&'static str>,
pub path: Option<String>,
}
impl MergeEvidenceDisplay {
pub(crate) fn from_report(
report: MergeEvidenceReport,
left_selector: MergeEvidenceDisplaySelector,
right_selector: MergeEvidenceDisplaySelector,
) -> Self {
let reason = report
.items
.first()
.map(|item| reason_code_name(item.reason_code));
let items = report
.items
.iter()
.map(|item| item_from_report(&report, item))
.collect();
Self {
baseline_block_id: report.baseline_block_id,
left_selector,
right_selector,
outcome: outcome_name(report.outcome),
reason,
left_operation_count: report.left_sequence.operation_count,
right_operation_count: report.right_sequence.operation_count,
items,
}
}
pub fn displayed_item_count(&self) -> usize {
self.items.len()
}
pub fn total_item_count(&self) -> usize {
self.items.len()
}
}
fn item_from_report(
report: &MergeEvidenceReport,
item: &MergeEvidenceItem,
) -> MergeEvidenceDisplayItem {
MergeEvidenceDisplayItem {
side: side_name(item.side),
operation: operation_for_item(
report,
operation_side(item.side),
item.operation_index,
item.op_seq,
),
peer_operation: operation_for_item(
report,
peer_side(item.side),
item.peer_operation_index,
item.peer_op_seq,
),
outcome: outcome_name(item.outcome),
evidence_scope: item.evidence_scope.map(scope_name),
proof_phase: proof_phase_name(item.proof_phase),
reason_code: reason_code_name(item.reason_code),
}
}
fn operation_side(side: MergeEvidenceSide) -> MergeEvidenceSide {
match side {
MergeEvidenceSide::Cross => MergeEvidenceSide::Left,
MergeEvidenceSide::Left | MergeEvidenceSide::Right | MergeEvidenceSide::Report => side,
}
}
fn operation_for_item(
report: &MergeEvidenceReport,
side: MergeEvidenceSide,
index: Option<usize>,
fallback_op_seq: Option<u32>,
) -> Option<MergeEvidenceDisplayOperation> {
let index = index?;
let sequence = match side {
MergeEvidenceSide::Left => &report.left_sequence,
MergeEvidenceSide::Right => &report.right_sequence,
MergeEvidenceSide::Cross | MergeEvidenceSide::Report => return None,
};
let operation = sequence.operations.get(index);
Some(MergeEvidenceDisplayOperation {
index,
op_seq: operation.map_or(fallback_op_seq, |op| Some(op.op_seq)),
kind: operation.map(|op| operation_kind_name(op.operation_kind)),
path: operation.and_then(|op| op.path.as_ref().map(|path| path.as_str().to_string())),
})
}
fn peer_side(side: MergeEvidenceSide) -> MergeEvidenceSide {
match side {
MergeEvidenceSide::Left => MergeEvidenceSide::Right,
MergeEvidenceSide::Right | MergeEvidenceSide::Cross | MergeEvidenceSide::Report => {
MergeEvidenceSide::Right
}
}
}
fn outcome_name(outcome: MergeEvidenceOutcome) -> &'static str {
match outcome {
MergeEvidenceOutcome::Confluent => "Confluent",
MergeEvidenceOutcome::Conflict => "Conflict",
MergeEvidenceOutcome::OrderedDependency => "OrderedDependency",
MergeEvidenceOutcome::Unsupported => "Unsupported",
MergeEvidenceOutcome::Deferred => "Deferred",
MergeEvidenceOutcome::NotConfluent => "NotConfluent",
MergeEvidenceOutcome::EvidenceFailure => "EvidenceFailure",
MergeEvidenceOutcome::InvalidCandidate => "InvalidCandidate",
}
}
fn side_name(side: MergeEvidenceSide) -> &'static str {
match side {
MergeEvidenceSide::Left => "left",
MergeEvidenceSide::Right => "right",
MergeEvidenceSide::Cross => "cross",
MergeEvidenceSide::Report => "report",
}
}
fn scope_name(scope: MergeEvidenceScope) -> &'static str {
match scope {
MergeEvidenceScope::SealedBaseline => "sealed-baseline",
MergeEvidenceScope::SealedCandidate => "sealed-candidate",
MergeEvidenceScope::UnsealedCandidate => "unsealed-candidate",
}
}
fn proof_phase_name(phase: MergeEvidenceProofPhase) -> &'static str {
match phase {
MergeEvidenceProofPhase::Classification => "classification",
MergeEvidenceProofPhase::ReplayBothOrders => "replay-both-orders",
MergeEvidenceProofPhase::Flatness => "flatness",
#[cfg(test)]
MergeEvidenceProofPhase::ComposedReplay => "composed-replay",
MergeEvidenceProofPhase::FinalStateComparison => "final-state-comparison",
}
}
fn operation_kind_name(kind: MergeEvidenceOperationKind) -> &'static str {
match kind {
MergeEvidenceOperationKind::CreateFile => "CreateFile",
MergeEvidenceOperationKind::DeleteNode => "DeleteNode",
MergeEvidenceOperationKind::EditText => "EditText",
MergeEvidenceOperationKind::ReplaceBinary => "ReplaceBinary",
MergeEvidenceOperationKind::RenamePath => "RenamePath",
MergeEvidenceOperationKind::ChangePerm => "ChangePerm",
MergeEvidenceOperationKind::CreateSymlink => "CreateSymlink",
}
}
fn reason_code_name(reason_code: MergeEvidenceReasonCode) -> &'static str {
match reason_code {
MergeEvidenceReasonCode::ProvenConfluent => "proven_confluent",
MergeEvidenceReasonCode::PairConflict => "pair_conflict",
MergeEvidenceReasonCode::OrderedDependency => "ordered_dependency",
MergeEvidenceReasonCode::UnsupportedOperation => "unsupported_operation",
MergeEvidenceReasonCode::SameNodeTextTransformDeferred => {
"same_node_text_transform_deferred"
}
MergeEvidenceReasonCode::SequenceInternalDependencyDeferred => {
"sequence_internal_dependency_deferred"
}
MergeEvidenceReasonCode::PairReplayFailed => "pair_replay_failed",
#[cfg(test)]
MergeEvidenceReasonCode::ComposedReplayFailed => "composed_replay_failed",
MergeEvidenceReasonCode::FinalStateMismatch => "final_state_mismatch",
MergeEvidenceReasonCode::MissingRequiredEvidence => "missing_required_evidence",
MergeEvidenceReasonCode::MalformedRequiredEvidence => "malformed_required_evidence",
MergeEvidenceReasonCode::WrongTypeRequiredEvidence => "wrong_type_required_evidence",
MergeEvidenceReasonCode::UnreadableRequiredEvidence => "unreadable_required_evidence",
MergeEvidenceReasonCode::InvalidUnsealedCandidate => "invalid_unsealed_candidate",
MergeEvidenceReasonCode::InsufficientUnsealedCandidateEvidence => {
"insufficient_unsealed_candidate_evidence"
}
}
}