1use std::collections::BTreeMap;
7
8use crate::error::{Error, Result};
9use crate::index::IndexEntry;
10use crate::objects::ObjectId;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct ResolveUndoRecord {
15 pub modes: [u32; 3],
17 pub oids: [ObjectId; 3],
19}
20
21impl Default for ResolveUndoRecord {
22 fn default() -> Self {
23 Self {
24 modes: [0; 3],
25 oids: [ObjectId::zero(); 3],
26 }
27 }
28}
29
30pub fn parse_resolve_undo_payload(data: &[u8]) -> Result<BTreeMap<Vec<u8>, ResolveUndoRecord>> {
32 let mut out: BTreeMap<Vec<u8>, ResolveUndoRecord> = BTreeMap::new();
33 let mut pos = 0usize;
34 while pos < data.len() {
35 let Some(nul) = data[pos..].iter().position(|&b| b == 0) else {
36 return Err(Error::IndexError("truncated resolve-undo path".to_owned()));
37 };
38 let path = data[pos..pos + nul].to_vec();
39 pos += nul + 1;
40 let mut modes = [0u32; 3];
41 for m in &mut modes {
42 let Some(term) = data[pos..].iter().position(|&b| b == 0) else {
43 return Err(Error::IndexError("truncated resolve-undo mode".to_owned()));
44 };
45 let slice = &data[pos..pos + term];
46 let s = std::str::from_utf8(slice)
47 .map_err(|_| Error::IndexError("invalid UTF-8 in resolve-undo mode".to_owned()))?;
48 *m = u32::from_str_radix(s, 8)
49 .map_err(|_| Error::IndexError(format!("invalid resolve-undo mode '{s}'")))?;
50 pos += term + 1;
51 }
52 let mut oids = [ObjectId::zero(); 3];
53 for i in 0..3 {
54 if modes[i] == 0 {
55 continue;
56 }
57 if pos + 20 > data.len() {
58 return Err(Error::IndexError(
59 "truncated resolve-undo object id".to_owned(),
60 ));
61 }
62 oids[i] = ObjectId::from_bytes(&data[pos..pos + 20])
63 .map_err(|e| Error::IndexError(e.to_string()))?;
64 pos += 20;
65 }
66 out.insert(path, ResolveUndoRecord { modes, oids });
67 }
68 Ok(out)
69}
70
71pub fn write_resolve_undo_payload(map: &BTreeMap<Vec<u8>, ResolveUndoRecord>) -> Vec<u8> {
73 let mut sb = Vec::new();
74 for (path, ru) in map {
75 if !ru.modes.iter().any(|m| *m != 0) {
76 continue;
77 }
78 sb.extend_from_slice(path);
79 sb.push(0);
80 for m in &ru.modes {
81 let s = format!("{:o}", m);
82 sb.extend_from_slice(s.as_bytes());
83 sb.push(0);
84 }
85 for i in 0..3 {
86 if ru.modes[i] != 0 {
87 sb.extend_from_slice(ru.oids[i].as_bytes());
88 }
89 }
90 }
91 sb
92}
93
94pub fn record_resolve_undo_for_entry(
96 map: &mut Option<BTreeMap<Vec<u8>, ResolveUndoRecord>>,
97 entry: &IndexEntry,
98) {
99 let stage = entry.stage();
100 if stage == 0 || stage > 3 {
101 return;
102 }
103 let idx = (stage - 1) as usize;
104 let m = map.get_or_insert_with(BTreeMap::new);
105 let ru = m.entry(entry.path.clone()).or_default();
106 ru.modes[idx] = entry.mode;
107 ru.oids[idx] = entry.oid;
108}