use serde::Serialize;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct ConflictReport {
pub conflicts: Vec<Conflict>,
}
impl ConflictReport {
pub fn is_clean(&self) -> bool {
self.conflicts.is_empty()
}
pub fn has_conflicts(&self) -> bool {
!self.is_clean()
}
pub fn to_json_pretty(&self) -> String {
let mut out =
serde_json::to_string_pretty(self).expect("conflict report JSON serialization");
out.push('\n');
out
}
}
impl fmt::Display for ConflictReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_json_pretty())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Conflict {
pub class: ConflictClass,
pub code: ConflictCode,
pub owner: Option<ConflictOwner>,
pub left_regions: Vec<ConflictRegion>,
pub right_regions: Vec<ConflictRegion>,
#[serde(skip_serializing)]
pub summary: String,
#[serde(skip_serializing)]
pub detail: String,
pub left: Vec<ConflictSide>,
pub right: Vec<ConflictSide>,
#[serde(skip_serializing)]
pub remediation: Option<String>,
}
impl fmt::Display for Conflict {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "[{:?}:{:?}] {}", self.class, self.code, self.summary)?;
writeln!(f, "{}", self.detail)?;
if !self.left.is_empty() {
writeln!(f, "left:")?;
for side in &self.left {
writeln!(f, "- {}", side)?;
}
}
if !self.right.is_empty() {
writeln!(f, "right:")?;
for side in &self.right {
writeln!(f, "- {}", side)?;
}
}
if let Some(remediation) = &self.remediation {
write!(f, "next: {remediation}")?;
}
Ok(())
}
}
impl Conflict {
pub fn to_json_pretty(&self) -> String {
let mut out = serde_json::to_string_pretty(self).expect("conflict JSON serialization");
out.push('\n');
out
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConflictClass {
Hard,
Semantic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConflictCode {
SameNodeWrite,
SameScalarPathWrite,
SameSingleSlotWrite,
SameRankedPosition,
ReplayFailure,
NonConvergentReplay,
BindingRenameVsInitializerChange,
ParameterTypeVsBodyInterpretationChange,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ConflictOwner {
Binding {
let_id: String,
binding_id: String,
},
Parameter {
fn_id: String,
param_id: String,
param_name: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConflictRegion {
BindingName,
BindingInitializer,
ParameterTypeContract,
ParameterBodyInterpretation,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ConflictSide {
pub op_index: usize,
pub op_kind: &'static str,
pub target: String,
#[serde(skip_serializing)]
pub description: String,
}
impl fmt::Display for ConflictSide {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"op {} `{}` on {}: {}",
self.op_index, self.op_kind, self.target, self.description
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplayOrder {
LeftThenRight,
RightThenLeft,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplayStage {
LeftOp(usize),
RightOp(usize),
Validation,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ReplayFailure {
pub order: ReplayOrder,
pub stage: ReplayStage,
pub message: String,
}
pub type HardConflictReport = ConflictReport;