use crate::app::AppState;
use std::path::PathBuf;
use std::fs;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct ConflictSuggestion {
pub file_path: String,
pub suggestion: String,
pub confidence: ConflictConfidence,
pub resolution_type: ResolutionType,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ConflictConfidence {
Low,
Medium,
High,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum ResolutionType {
UseOurs,
UseTheirs,
Merge,
Manual,
}
impl ConflictSuggestion {
pub fn analyze_conflict(repo_path: &str, file_path: &str) -> Option<Self> {
let full_path = PathBuf::from(repo_path).join(file_path);
let content = match fs::read_to_string(&full_path) {
Ok(c) => c,
Err(_) => return None,
};
let ours_markers = content.matches("<<<<<<<").count();
let theirs_markers = content.matches(">>>>>>>").count();
let _separator_markers = content.matches("=======").count();
if ours_markers == 0 || theirs_markers == 0 {
return None; }
let (suggestion, confidence, resolution_type) = Self::analyze_patterns(&content);
Some(Self {
file_path: file_path.to_string(),
suggestion,
confidence,
resolution_type,
})
}
fn analyze_patterns(content: &str) -> (String, ConflictConfidence, ResolutionType) {
if Self::is_whitespace_only(content) {
return (
"Conflict appears to be whitespace-only. Consider using 'theirs' or 'ours' based on your project's whitespace policy.".to_string(),
ConflictConfidence::High,
ResolutionType::UseTheirs, );
}
if Self::has_empty_side(content) {
return (
"One side deleted the content while the other modified it. Review carefully - you may want to keep the modified version or accept the deletion.".to_string(),
ConflictConfidence::Medium,
ResolutionType::Manual,
);
}
if Self::is_simple_addition(content) {
return (
"Both sides added different content. You can likely keep both sections.".to_string(),
ConflictConfidence::Medium,
ResolutionType::Merge,
);
}
if Self::is_import_conflict(content) {
return (
"Conflict in imports/headers. You may need to merge both import lists and remove duplicates.".to_string(),
ConflictConfidence::High,
ResolutionType::Merge,
);
}
if Self::is_version_conflict(content) {
return (
"Version number conflict detected. Usually you want to keep the higher version number.".to_string(),
ConflictConfidence::High,
ResolutionType::UseTheirs, );
}
(
"Complex conflict detected. Manual resolution required. Review both versions carefully.".to_string(),
ConflictConfidence::Low,
ResolutionType::Manual,
)
}
fn is_whitespace_only(content: &str) -> bool {
let sections: Vec<&str> = content
.split("=======")
.collect();
if sections.len() < 2 {
return false;
}
for section in sections {
let cleaned: String = section
.chars()
.filter(|c| !c.is_whitespace())
.collect();
if !cleaned.is_empty() && cleaned.len() < 10 {
continue;
}
}
false }
fn has_empty_side(content: &str) -> bool {
let parts: Vec<&str> = content.split("=======").collect();
if parts.len() < 2 {
return false;
}
parts.iter().any(|part| {
part.trim_matches(|c: char| c == '\n' || c == '\r' || c.is_whitespace())
.is_empty()
})
}
fn is_simple_addition(content: &str) -> bool {
if let Some(ours_pos) = content.find("<<<<<<<") {
if let Some(sep_pos) = content[ours_pos..].find("=======") {
if let Some(theirs_pos) = content[ours_pos + sep_pos..].find(">>>>>>>") {
let ours_section = &content[ours_pos + 7..ours_pos + sep_pos];
let theirs_section = &content[ours_pos + sep_pos + 7..ours_pos + sep_pos + theirs_pos];
return ours_section.lines().count() > 0
&& theirs_section.lines().count() > 0
&& !ours_section.trim().is_empty()
&& !theirs_section.trim().is_empty();
}
}
}
false
}
fn is_import_conflict(content: &str) -> bool {
content.contains("import")
|| content.contains("include")
|| content.contains("#include")
|| content.contains("use ")
|| content.contains("package ")
}
fn is_version_conflict(content: &str) -> bool {
content.contains("version") || content.contains("Version")
}
}
#[allow(dead_code)]
pub fn get_conflict_suggestions(state: &AppState) -> Vec<ConflictSuggestion> {
let conflicts: Vec<&crate::git::parsers::status::StatusEntry> = state
.status_entries
.iter()
.filter(|e| e.conflict)
.collect();
conflicts
.iter()
.filter_map(|entry| {
ConflictSuggestion::analyze_conflict(&state.repo_path, &entry.path)
})
.collect()
}