git_policy_cli/index/
index_resolve_undo.rs1use std::collections::BTreeMap;
14
15#[derive(Clone, Debug, Default, Eq, PartialEq)]
17pub struct Sides {
18 pub mode: [u32; 3],
20 pub id: [Vec<u8>; 3],
22}
23
24#[derive(Clone, Debug, Default, Eq, PartialEq)]
26pub struct ResolveUndo {
27 pub paths: BTreeMap<Vec<u8>, Sides>,
29}
30
31fn split_nul(bytes: &[u8]) -> Option<(&[u8], &[u8])> {
33 let nul: usize = bytes.iter().position(|byte| { return *byte == 0; })?;
34 return Some((&bytes[..nul], &bytes[nul + 1..]));
35}
36
37impl ResolveUndo {
38 pub fn read(bytes: &[u8], hash_length: usize) -> Option<ResolveUndo> {
40 let mut paths: BTreeMap<Vec<u8>, Sides> = BTreeMap::new();
41 let mut rest: &[u8] = bytes;
42 while !rest.is_empty() {
43 let (path, after) = split_nul(rest)?;
44 rest = after;
45 let mut sides: Sides = paths.remove(path).unwrap_or_default();
46 for mode in &mut sides.mode {
47 let (text, following) = split_nul(rest)?;
48 let digits: &str = std::str::from_utf8(text).ok()?;
49 if digits.is_empty() || !digits.bytes().all(|byte| { return (b'0'..=b'7').contains(&byte); }) {
51 return None;
52 }
53 *mode = u32::from_str_radix(digits, 8).ok()?;
54 rest = following;
55 }
56 for (mode, id) in sides.mode.iter().zip(sides.id.iter_mut()) {
57 if *mode != 0 {
58 *id = rest.get(..hash_length)?.to_vec();
59 rest = &rest[hash_length..];
60 }
61 }
62 paths.insert(path.to_vec(), sides);
63 }
64 return Some(ResolveUndo { paths });
65 }
66
67 pub fn record(&mut self, path: &[u8], stage: u8, mode: u32, id: &[u8]) {
69 if stage == 0 {
70 return;
71 }
72 let sides: &mut Sides = self.paths.entry(path.to_vec()).or_default();
73 sides.mode[usize::from(stage) - 1] = mode;
74 sides.id[usize::from(stage) - 1] = id.to_vec();
75 }
76
77 pub fn write(&self) -> Vec<u8> {
79 let mut out: Vec<u8> = Vec::new();
80 for (path, sides) in &self.paths {
81 out.extend_from_slice(path.as_slice());
82 out.push(0);
83 for mode in sides.mode {
84 out.extend_from_slice(format!("{mode:o}").as_bytes());
85 out.push(0);
86 }
87 for (mode, id) in sides.mode.iter().zip(sides.id.iter()) {
88 if *mode != 0 {
89 out.extend_from_slice(id.as_slice());
90 }
91 }
92 }
93 return out;
94 }
95}
96
97#[cfg(test)]
98#[path = "index_resolve_undo_tests.rs"]
99mod tests;