use crate::line_sink::LineSink;
use crate::parallel_budget::{BudgetRequest, HostResources, worker_budget};
use crate::parallel_runner::{Event, OrderedEvents, OrderedRun, OrderedWork, run_ordered};
use crate::subvolume_index_files::{IndexLock, RefreshOptions, linked_worktrees_inside, refresh_index_file_with, repository_hash_length};
use std::path::{Path, PathBuf};
const INDEX_REWRITE_COPIES: u64 = 4;
struct IndexTarget {
index: PathBuf,
tree: PathBuf,
references: Vec<PathBuf>,
}
pub type RewriteOne<'a> = dyn Fn(&Path, &Path, &RefreshOptions<'_>) -> String + Sync + 'a;
pub struct AllIndexes<'a> {
pub root: &'a Path,
pub lock: IndexLock<'a>,
pub references: &'a [PathBuf],
pub host: &'a HostResources,
pub rewrite: &'a RewriteOne<'a>,
}
pub fn refresh_all_indexes(root: &Path, lock: IndexLock<'_>, references: &[PathBuf], say: &mut dyn LineSink) -> usize {
return refresh_all_indexes_with(&AllIndexes { root, lock, references, host: &HostResources::read(), rewrite: &refresh_index_file_with }, say);
}
struct RewriteEachIndex<'a> {
rewrite: &'a RewriteOne<'a>,
lock: IndexLock<'a>,
hash_length: Option<usize>,
}
impl OrderedWork<IndexTarget, String> for RewriteEachIndex<'_> {
fn run(&self, _index: usize, target: &IndexTarget, _say: &mut dyn LineSink) -> String {
return (self.rewrite)(target.index.as_path(), target.tree.as_path(), &RefreshOptions { lock: self.lock, references: target.references.as_slice(), hash_length: self.hash_length });
}
}
struct SayEachIndex<'a> {
say: &'a mut dyn LineSink,
targets: &'a [IndexTarget],
}
impl OrderedEvents<String> for SayEachIndex<'_> {
fn on_event(&mut self, event: Event<'_, String>) {
if let Event::End(index, line) = event {
self.say.say(format!("index {}: {line}", self.targets[index].index.display()).as_str());
}
}
}
pub fn refresh_all_indexes_with(request: &AllIndexes<'_>, say: &mut dyn LineSink) -> usize {
let AllIndexes { root, lock, references, host, rewrite } = *request;
let hash_length: Option<usize> = repository_hash_length(root.join(".git").as_path());
let mut targets: Vec<IndexTarget> = vec![IndexTarget { index: root.join(".git").join("index"), tree: root.to_path_buf(), references: references.to_vec() }];
for linked in linked_worktrees_inside(root) {
let relative: &Path = linked.worktree.strip_prefix(root).unwrap_or(Path::new(""));
targets.push(IndexTarget {
index: linked.admin_directory.join("index"),
tree: linked.worktree.clone(),
references: joined_below(references, relative),
});
}
let mut largest: u64 = 0;
for target in &targets {
let size: u64 = match std::fs::metadata(&target.index) {
Ok(found) => found.len(),
Err(_) => 0,
};
largest = largest.max(size);
}
let workers: usize = worker_budget(&BudgetRequest { items: targets.len(), per_item_memory: largest.saturating_mul(INDEX_REWRITE_COPIES), ceiling: None, cap: None, host });
let mut lanes: Vec<Vec<usize>> = Vec::new();
for index in 0..targets.len() {
lanes.push(vec![index]);
}
run_ordered(OrderedRun {
items: targets.as_slice(),
lanes: lanes.as_slice(),
workers,
work: &RewriteEachIndex { rewrite, lock, hash_length },
on_event: &mut SayEachIndex { say, targets: targets.as_slice() },
});
return targets.len();
}
fn joined_below(references: &[PathBuf], relative: &Path) -> Vec<PathBuf> {
let mut joined: Vec<PathBuf> = Vec::new();
for reference in references {
joined.push(reference.join(relative));
}
return joined;
}