use std::collections::HashSet;
use color_eyre::eyre::{Report, Result, WrapErr as _, eyre};
use girt::{MissingObjectsError, ObjectId, Repository};
use crate::Context;
pub fn retry<T, E>(
context: &Context,
repository: &Repository,
mut operation: impl FnMut() -> Result<T, E>,
) -> Result<T>
where
E: Into<Report>,
{
let mut fetched = HashSet::new();
loop {
let error = match operation() {
Ok(value) => return Ok(value),
Err(error) => error.into(),
};
let Some(missing) = missing_objects(&error) else {
return Err(error);
};
let wanted: Vec<ObjectId> = missing.ids().filter(|id| !fetched.contains(id)).collect();
if wanted.is_empty() || disabled(context)? {
return Err(error);
}
let mut callbacks = crate::progress::Progress::new(false);
girt::workflow::fetch_promised(repository, &wanted, &context.environment, &mut callbacks)
.wrap_err_with(|| {
format!(
"could not fetch {} missing objects from promisor remote",
wanted.len()
)
})?;
fetched.extend(wanted);
}
}
fn missing_objects(error: &Report) -> Option<&MissingObjectsError> {
error.chain().find_map(|cause| cause.downcast_ref())
}
pub fn disabled(context: &Context) -> Result<bool> {
let Some(value) = context.environment.get("GIT_NO_LAZY_FETCH") else {
return Ok(false);
};
let bytes = value.as_encoded_bytes();
girt::config::parse_boolean(bytes).ok_or_else(|| {
eyre!(
"bad boolean environment value '{}' for 'GIT_NO_LAZY_FETCH'",
String::from_utf8_lossy(bytes)
)
})
}