use std::collections::BTreeMap;
const BASE_STAGE: u8 = 1;
const OURS_STAGE: u8 = 2;
const THEIRS_STAGE: u8 = 3;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Stage {
pub mode: u32,
pub oid: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Conflict {
pub path: Vec<u8>,
pub base: Option<Stage>,
pub ours: Option<Stage>,
pub theirs: Option<Stage>,
}
pub fn is_nul_byte(byte: &u8) -> bool {
return *byte == 0;
}
fn is_space_byte(byte: &u8) -> bool {
return *byte == b' ';
}
pub fn conflicts_of_merge_tree(stdout: &[u8]) -> (String, Vec<Conflict>) {
let mut fields = stdout.split(is_nul_byte);
let tree: String = String::from_utf8_lossy(fields.next().unwrap_or_default()).into_owned();
let mut by_path: BTreeMap<Vec<u8>, Conflict> = BTreeMap::new();
let mut order: Vec<Vec<u8>> = Vec::new();
for field in fields {
if field.is_empty() {
break;
}
let Some((facts, path)) = split_once(field, b'\t') else { continue };
let words: Vec<&[u8]> = facts.split(is_space_byte).collect();
let [mode_bytes, oid_bytes, stage_bytes] = words.as_slice() else { continue };
let (Ok(mode_text), Ok(oid_text), Ok(stage_text)) = (std::str::from_utf8(mode_bytes), std::str::from_utf8(oid_bytes), std::str::from_utf8(stage_bytes)) else { continue };
let (Ok(mode), Ok(stage)) = (u32::from_str_radix(mode_text, 8), stage_text.parse::<u8>()) else { continue };
if !by_path.contains_key(path) {
order.push(path.to_vec());
}
let conflict: &mut Conflict = match by_path.entry(path.to_vec()) {
std::collections::btree_map::Entry::Occupied(known) => known.into_mut(),
std::collections::btree_map::Entry::Vacant(slot) => slot.insert(Conflict { path: path.to_vec(), base: None, ours: None, theirs: None }),
};
let side: Option<Stage> = Some(Stage { mode, oid: oid_text.to_owned() });
match stage {
BASE_STAGE => conflict.base = side,
OURS_STAGE => conflict.ours = side,
THEIRS_STAGE => conflict.theirs = side,
_ => {}
}
}
let mut ordered: Vec<Conflict> = Vec::new();
for path in order {
if let Some(conflict) = by_path.remove(&path) {
ordered.push(conflict);
}
}
return (tree, ordered);
}
pub fn split_once(bytes: &[u8], separator: u8) -> Option<(&[u8], &[u8])> {
let mut found: Option<usize> = None;
for (position, byte) in bytes.iter().enumerate() {
if *byte == separator {
found = Some(position);
break;
}
}
let at: usize = found?;
return Some((&bytes[..at], &bytes[at + 1..]));
}