use crate::git_metadata::MetadataOutput;
#[cfg(unix)]
use crate::repo_read_config_scan::ScopeEnvironment;
#[cfg(unix)]
use crate::repo_read_query::{Answer, Query, answer_with};
use crate::transaction_git::{GitContext, GitOutput, GitRequest};
use std::ffi::OsString;
#[cfg(unix)]
use std::path::Path;
pub const SWITCH_VARIABLE: &str = "CLI_GIT_IN_PROCESS_READS";
pub fn enabled(variables: &[(OsString, OsString)]) -> bool {
let added_off: bool = variables.iter().any(|(name, value)| { return name == SWITCH_VARIABLE && value == "off"; });
return !added_off && std::env::var_os(SWITCH_VARIABLE).is_none_or(|value| { return value != "off"; });
}
#[cfg(test)]
pub fn switch_off() -> [(OsString, OsString); 1] {
return [(OsString::from(SWITCH_VARIABLE), OsString::from("off"))];
}
pub fn guarded<T>(read: impl FnOnce() -> Option<T>) -> Option<T> {
return match std::panic::catch_unwind(std::panic::AssertUnwindSafe(read)) {
Ok(found) => found,
Err(payload) => {
let message: &str = payload.downcast_ref::<&str>().copied().or_else(|| { return payload.downcast_ref::<String>().map(String::as_str); }).unwrap_or("a panic without text");
crate::diagnostic_log::warn("repo-read-guard", format!("an in-process read panicked and Git answers instead: {message}").as_str());
None
}
};
}
#[cfg(unix)]
fn attempt(
arguments: &[OsString],
directory: Option<&Path>,
base: Vec<(OsString, OsString)>,
variables: &[(OsString, OsString)],
input: Option<&[u8]>,
) -> Option<Answer> {
if !enabled(variables) {
return None;
}
let mut effective: Vec<(OsString, OsString)> = base.clone();
effective.extend(variables.iter().cloned());
let scope: ScopeEnvironment = ScopeEnvironment::from_variables(effective.as_slice());
return guarded(|| { return answer_with(&Query { arguments, directory, variables, input }, base, &scope); });
}
#[cfg(unix)]
pub fn try_metadata(arguments: &[OsString], overlay: &[(OsString, OsString)]) -> Option<MetadataOutput> {
let found: Answer = attempt(arguments, None, std::env::vars_os().collect(), overlay, None)?;
return Some(MetadataOutput { success: found.code == 0, code: Some(found.code), stdout: found.stdout, stderr: found.stderr });
}
#[cfg(not(unix))]
pub fn try_metadata(_arguments: &[OsString], _overlay: &[(OsString, OsString)]) -> Option<MetadataOutput> {
return None;
}
#[cfg(unix)]
pub fn try_complete(arguments: &[OsString], directory: &Path, environment: &[(OsString, OsString)]) -> Option<(Option<i32>, Vec<u8>, Vec<u8>)> {
let found: Answer = attempt(arguments, Some(directory), Vec::new(), environment, None)?;
return Some((Some(found.code), found.stdout, found.stderr));
}
#[cfg(not(unix))]
pub fn try_complete(_arguments: &[OsString], _directory: &std::path::Path, _environment: &[(OsString, OsString)]) -> Option<(Option<i32>, Vec<u8>, Vec<u8>)> {
return None;
}
#[cfg(unix)]
pub fn try_request(context: &GitContext, request: &GitRequest) -> Option<GitOutput> {
let mut arguments: Vec<OsString> = Vec::new();
if !request.without_prefix {
arguments.extend(context.global_prefix.iter().cloned());
}
arguments.extend(request.arguments.iter().cloned());
let mut variables: Vec<(OsString, OsString)> = crate::git_system_config::with_real_git(context.overlay.as_slice(), context.real_git.as_path());
variables.extend(request.environment.iter().cloned());
if let Some(index) = &request.index {
variables.push((OsString::from("GIT_INDEX_FILE"), index.clone().into_os_string()));
}
if let Some(objects) = &request.object_directory {
variables.push((OsString::from("GIT_OBJECT_DIRECTORY"), objects.clone().into_os_string()));
}
let base: Vec<(OsString, OsString)> = std::env::vars_os()
.filter(|(name, _)| { return !request.unset.contains(name); })
.collect();
let found: Answer = attempt(arguments.as_slice(), Some(request.cwd.as_path()), base, variables.as_slice(), request.input.as_deref())?;
return Some(GitOutput { code: Some(found.code), stdout: found.stdout, stderr: found.stderr });
}
#[cfg(not(unix))]
pub fn try_request(_context: &GitContext, _request: &GitRequest) -> Option<GitOutput> {
return None;
}
#[cfg(test)]
#[path = "repo_read_hook_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "repo_read_batch_tests.rs"]
mod batch_tests;