use crate::repo_read_layout::ObjectFormat;
use crate::repo_read_object::{ObjectKind, from_hex, name_length};
use crate::repo_read_odb::ObjectDatabase;
use crate::repo_read_tree::{FlatEntry, TreeEntry, flatten_pair, flatten_tree, is_tree_mode, parse_commit, parse_tree};
pub fn root_tree(database: &ObjectDatabase, hex: &str, format: ObjectFormat) -> Option<Vec<u8>> {
let name: Vec<u8> = from_hex(hex.as_bytes(), format)?;
let object = database.read(name.as_slice())?;
return match object.kind {
ObjectKind::Tree => Some(name),
ObjectKind::Commit => Some(parse_commit(object.bytes.as_slice(), format)?.tree),
ObjectKind::Blob | ObjectKind::Tag => None,
};
}
fn entries_of(database: &ObjectDatabase, tree: &[u8], format: ObjectFormat) -> Option<Vec<TreeEntry>> {
let object = database.read(tree)?;
if object.kind != ObjectKind::Tree {
return None;
}
return parse_tree(object.bytes.as_slice(), name_length(format));
}
fn entries_at(database: &ObjectDatabase, root: &[u8], name: &[u8], format: ObjectFormat) -> Option<Vec<FlatEntry>> {
let components: Vec<&[u8]> = name.split(|byte| { return *byte == b'/'; }).collect();
let mut tree: Vec<u8> = root.to_vec();
for (position, component) in components.iter().enumerate() {
let entries: Vec<TreeEntry> = entries_of(database, tree.as_slice(), format)?;
let Some(found) = entries.into_iter().find(|entry| { return entry.name.as_slice() == *component; }) else {
return Some(Vec::new());
};
let last: bool = position + 1 == components.len();
if !last {
if !is_tree_mode(found.mode) {
return Some(Vec::new());
}
tree = found.id;
continue;
}
if !is_tree_mode(found.mode) {
return Some(vec![FlatEntry { path: name.to_vec(), mode: found.mode, id: found.id }]);
}
let below: Vec<FlatEntry> = flatten_tree(database, found.id.as_slice(), format)?;
return Some(below.into_iter().map(|entry| {
let mut path: Vec<u8> = name.to_vec();
path.push(b'/');
path.extend_from_slice(entry.path.as_slice());
return FlatEntry { path, mode: entry.mode, id: entry.id };
}).collect());
}
return Some(Vec::new());
}
pub fn tree_entries_at(database: &ObjectDatabase, root: &[u8], names: &[Vec<u8>], format: ObjectFormat) -> Option<Vec<FlatEntry>> {
let mut found: Vec<FlatEntry> = Vec::new();
for name in names {
found.extend(entries_at(database, root, name.as_slice(), format)?);
}
found.sort_by(|left, right| { return left.path.cmp(&right.path); });
found.dedup_by(|later, earlier| { return later.path == earlier.path; });
return Some(found);
}
pub fn changed_paths(database: &ObjectDatabase, old: Option<&[u8]>, new: &[u8], format: ObjectFormat) -> Option<Vec<Vec<u8>>> {
let (before, after): (Vec<FlatEntry>, Vec<FlatEntry>) = match old {
Some(tree) => flatten_pair(database, tree, new, format)?,
None => (Vec::new(), flatten_tree(database, new, format)?),
};
let mut paths: Vec<Vec<u8>> = before.into_iter().chain(after).map(|entry| { return entry.path; }).collect();
paths.sort();
paths.dedup();
return Some(paths);
}
#[cfg(test)]
#[path = "index_reset_tree_tests.rs"]
mod tests;