use std::fmt;
use std::hash::Hash;
pub type NodeId = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ListOrdering {
Ordered,
Unordered,
}
#[derive(Debug, Clone)]
pub enum CstNode {
Leaf {
id: NodeId,
kind: String,
value: String,
},
Constructed {
id: NodeId,
kind: String,
children: Vec<CstNode>,
},
List {
id: NodeId,
kind: String,
ordering: ListOrdering,
children: Vec<CstNode>,
},
}
impl CstNode {
pub fn id(&self) -> NodeId {
match self {
CstNode::Leaf { id, .. } => *id,
CstNode::Constructed { id, .. } => *id,
CstNode::List { id, .. } => *id,
}
}
pub fn kind(&self) -> &str {
match self {
CstNode::Leaf { kind, .. } => kind,
CstNode::Constructed { kind, .. } => kind,
CstNode::List { kind, .. } => kind,
}
}
pub fn children(&self) -> &[CstNode] {
match self {
CstNode::Leaf { .. } => &[],
CstNode::Constructed { children, .. } => children,
CstNode::List { children, .. } => children,
}
}
pub fn children_mut(&mut self) -> &mut Vec<CstNode> {
match self {
CstNode::Leaf { .. } => panic!("leaf nodes have no children"),
CstNode::Constructed { children, .. } => children,
CstNode::List { children, .. } => children,
}
}
pub fn is_leaf(&self) -> bool {
matches!(self, CstNode::Leaf { .. })
}
pub fn leaf_value(&self) -> Option<&str> {
match self {
CstNode::Leaf { value, .. } => Some(value),
_ => None,
}
}
pub fn size(&self) -> usize {
1 + self.children().iter().map(|c| c.size()).sum::<usize>()
}
pub fn collect_leaves(&self) -> Vec<&str> {
let mut leaves = Vec::new();
self.collect_leaves_inner(&mut leaves);
leaves
}
fn collect_leaves_inner<'a>(&'a self, out: &mut Vec<&'a str>) {
match self {
CstNode::Leaf { value, .. } => out.push(value),
CstNode::Constructed { children, .. } | CstNode::List { children, .. } => {
for c in children {
c.collect_leaves_inner(out);
}
}
}
}
pub fn to_source(&self) -> String {
self.collect_leaves().join("")
}
pub fn structurally_equal(&self, other: &CstNode) -> bool {
if self.kind() != other.kind() {
return false;
}
match (self, other) {
(CstNode::Leaf { value: v1, .. }, CstNode::Leaf { value: v2, .. }) => v1 == v2,
(
CstNode::Constructed {
children: c1,
kind: k1,
..
},
CstNode::Constructed {
children: c2,
kind: k2,
..
},
) => {
k1 == k2
&& c1.len() == c2.len()
&& c1
.iter()
.zip(c2.iter())
.all(|(a, b)| a.structurally_equal(b))
}
(
CstNode::List {
children: c1,
ordering: o1,
kind: k1,
..
},
CstNode::List {
children: c2,
ordering: o2,
kind: k2,
..
},
) => {
k1 == k2
&& o1 == o2
&& c1.len() == c2.len()
&& c1
.iter()
.zip(c2.iter())
.all(|(a, b)| a.structurally_equal(b))
}
_ => false,
}
}
}
impl fmt::Display for CstNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_source())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MatchPair {
pub left: NodeId,
pub right: NodeId,
pub score: usize,
}
#[derive(Debug, Clone)]
pub struct MergeScenario<T> {
pub base: T,
pub left: T,
pub right: T,
}
impl<T> MergeScenario<T> {
pub fn new(base: T, left: T, right: T) -> Self {
Self { base, left, right }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MergeResult {
Resolved(String),
Conflict {
base: String,
left: String,
right: String,
},
}
impl MergeResult {
pub fn is_resolved(&self) -> bool {
matches!(self, MergeResult::Resolved(_))
}
pub fn is_conflict(&self) -> bool {
matches!(self, MergeResult::Conflict { .. })
}
pub fn to_string_with_markers(&self) -> String {
match self {
MergeResult::Resolved(s) => s.clone(),
MergeResult::Conflict { base, left, right } => {
let mut out = String::new();
out.push_str("<<<<<<< LEFT\n");
out.push_str(left);
if !left.ends_with('\n') {
out.push('\n');
}
out.push_str("||||||| BASE\n");
out.push_str(base);
if !base.ends_with('\n') {
out.push('\n');
}
out.push_str("=======\n");
out.push_str(right);
if !right.ends_with('\n') {
out.push('\n');
}
out.push_str(">>>>>>> RIGHT\n");
out
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Language {
Rust,
JavaScript,
TypeScript,
Python,
Java,
Go,
C,
Cpp,
Kotlin,
Toml,
Yaml,
}
impl Language {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext {
"rs" => Some(Language::Rust),
"js" | "mjs" | "cjs" => Some(Language::JavaScript),
"ts" | "tsx" => Some(Language::TypeScript),
"py" => Some(Language::Python),
"java" => Some(Language::Java),
"go" => Some(Language::Go),
"c" | "h" => Some(Language::C),
"cpp" | "cc" | "cxx" | "hpp" | "hxx" => Some(Language::Cpp),
"kt" | "kts" => Some(Language::Kotlin),
"toml" => Some(Language::Toml),
"yml" | "yaml" => Some(Language::Yaml),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Diff3Hunk {
Stable(Vec<String>),
LeftChanged(Vec<String>),
RightChanged(Vec<String>),
Conflict {
base: Vec<String>,
left: Vec<String>,
right: Vec<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Confidence {
Low,
Medium,
High,
}
#[derive(Debug, Clone)]
pub struct ResolutionCandidate {
pub content: String,
pub confidence: Confidence,
pub strategy: ResolutionStrategy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolutionStrategy {
Diff3,
StructuredMerge,
VersionSpaceAlgebra,
PatternRule,
SearchBased,
}
impl fmt::Display for ResolutionStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ResolutionStrategy::Diff3 => write!(f, "diff3"),
ResolutionStrategy::StructuredMerge => write!(f, "structured-merge"),
ResolutionStrategy::VersionSpaceAlgebra => write!(f, "version-space-algebra"),
ResolutionStrategy::PatternRule => write!(f, "pattern-rule"),
ResolutionStrategy::SearchBased => write!(f, "search-based"),
}
}
}