use std::collections::BTreeMap;
use crate::error::{Error, Result};
use crate::index::IndexEntry;
use crate::objects::ObjectId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolveUndoRecord {
pub modes: [u32; 3],
pub oids: [ObjectId; 3],
}
impl Default for ResolveUndoRecord {
fn default() -> Self {
Self {
modes: [0; 3],
oids: [ObjectId::zero(); 3],
}
}
}
pub fn parse_resolve_undo_payload(data: &[u8]) -> Result<BTreeMap<Vec<u8>, ResolveUndoRecord>> {
let mut out: BTreeMap<Vec<u8>, ResolveUndoRecord> = BTreeMap::new();
let mut pos = 0usize;
while pos < data.len() {
let Some(nul) = data[pos..].iter().position(|&b| b == 0) else {
return Err(Error::IndexError("truncated resolve-undo path".to_owned()));
};
let path = data[pos..pos + nul].to_vec();
pos += nul + 1;
let mut modes = [0u32; 3];
for m in &mut modes {
let Some(term) = data[pos..].iter().position(|&b| b == 0) else {
return Err(Error::IndexError("truncated resolve-undo mode".to_owned()));
};
let slice = &data[pos..pos + term];
let s = std::str::from_utf8(slice)
.map_err(|_| Error::IndexError("invalid UTF-8 in resolve-undo mode".to_owned()))?;
*m = u32::from_str_radix(s, 8)
.map_err(|_| Error::IndexError(format!("invalid resolve-undo mode '{s}'")))?;
pos += term + 1;
}
let mut oids = [ObjectId::zero(); 3];
for i in 0..3 {
if modes[i] == 0 {
continue;
}
if pos + 20 > data.len() {
return Err(Error::IndexError(
"truncated resolve-undo object id".to_owned(),
));
}
oids[i] = ObjectId::from_bytes(&data[pos..pos + 20])
.map_err(|e| Error::IndexError(e.to_string()))?;
pos += 20;
}
out.insert(path, ResolveUndoRecord { modes, oids });
}
Ok(out)
}
pub fn write_resolve_undo_payload(map: &BTreeMap<Vec<u8>, ResolveUndoRecord>) -> Vec<u8> {
let mut sb = Vec::new();
for (path, ru) in map {
if !ru.modes.iter().any(|m| *m != 0) {
continue;
}
sb.extend_from_slice(path);
sb.push(0);
for m in &ru.modes {
let s = format!("{:o}", m);
sb.extend_from_slice(s.as_bytes());
sb.push(0);
}
for i in 0..3 {
if ru.modes[i] != 0 {
sb.extend_from_slice(ru.oids[i].as_bytes());
}
}
}
sb
}
pub fn record_resolve_undo_for_entry(
map: &mut Option<BTreeMap<Vec<u8>, ResolveUndoRecord>>,
entry: &IndexEntry,
) {
let stage = entry.stage();
if stage == 0 || stage > 3 {
return;
}
let idx = (stage - 1) as usize;
let m = map.get_or_insert_with(BTreeMap::new);
let ru = m.entry(entry.path.clone()).or_default();
ru.modes[idx] = entry.mode;
ru.oids[idx] = entry.oid;
}