use super::Observed;
use crate::index;
use crate::status::Change;
use crate::status::types::value;
#[derive(Clone, Copy)]
pub(super) struct Slots {
pub(super) entry: Option<usize>,
pub(super) directory: Option<usize>,
}
pub(super) struct Tracked<'a> {
pub(super) entries: &'a [index::Entry],
pub(super) directories: Vec<&'a [u8]>,
}
impl Tracked<'_> {
fn stages(&self, path: &[u8]) -> std::ops::Range<usize> {
let start = self
.entries
.partition_point(|entry| entry.path.as_slice() < path);
let count = self.entries[start..]
.iter()
.take_while(|entry| entry.path == path)
.count();
start..start + count
}
pub(super) fn entry(&self, path: &[u8]) -> Option<(usize, &index::Entry)> {
let stages = self.stages(path);
let last = stages.end.checked_sub(1).filter(|_| !stages.is_empty())?;
Some((stages.start, &self.entries[last]))
}
pub(super) fn directory(&self, path: &[u8]) -> Option<usize> {
self.directories
.binary_search_by(|directory| (*directory).cmp(path))
.ok()
}
pub(super) fn slots(&self, path: &[u8]) -> Slots {
let stages = self.stages(path);
Slots {
entry: (!stages.is_empty()).then_some(stages.start),
directory: self
.directory(path)
.map(|directory| self.entries.len() + directory),
}
}
pub(super) fn collect(&self, observations: Vec<(usize, Observed)>) -> Vec<Option<Observed>> {
let mut slots = vec![None; self.entries.len() + self.directories.len()];
for (slot, observed) in observations {
slots[slot] = Some(observed);
}
slots
}
pub(super) fn directory_states(&self, observed: &[Option<Observed>]) -> Vec<Option<Change>> {
let mut states: Vec<Option<Change>> = Vec::with_capacity(self.directories.len());
for (position, directory) in self.directories.iter().enumerate() {
let parent = directory
.iter()
.rposition(|byte| *byte == b'/')
.and_then(|end| self.directory(&directory[..end]))
.and_then(|parent| states[parent].clone());
let state = parent.or_else(|| match observed[self.entries.len() + position] {
Some(Observed::Directory) => None,
Some(_) => Some(Change::Obstructed {
path: directory.to_vec(),
}),
None => Some(Change::Deleted),
});
states.push(state);
}
states
}
pub(super) fn difference(
&self,
position: usize,
observed: &[Option<Observed>],
directories: &[Option<Change>],
) -> Option<Change> {
let entry = &self.entries[position];
if let Some(end) = entry.path.iter().rposition(|byte| *byte == b'/') {
let parent = self.directory(&entry.path[..end])?;
if let Some(change) = &directories[parent] {
return Some(change.clone());
}
}
match observed[position] {
Some(Observed::Leaf(actual)) if actual == value(entry) => None,
Some(Observed::Leaf(actual)) => Some(Change::Modified(actual)),
Some(_) => Some(Change::Obstructed {
path: entry.path.clone(),
}),
None => Some(Change::Deleted),
}
}
}