use crate::auto_push_single_flight::{PushGit, SingleFlightError};
use crate::action::{Action, ENGINE_FAILURE_EXIT_CODE};
use crate::diagnostic_log::debug;
use crate::entry::plan_invocation;
use crate::push_stacking::{STACKED_PUSH_MARKER, run_stacked_push};
use crate::real_git::process_resolution_inputs;
use crate::worktree_copy_forward::{ForwardedRun, run_forwarded_git};
use std::ffi::OsString;
use std::io::Write;
const TAG: &str = "monochromatic-git-policy-cli";
fn marked_environment(environment: &[(OsString, OsString)]) -> Vec<(OsString, OsString)> {
let mut marked: Vec<(OsString, OsString)> = Vec::new();
for pair in environment {
if pair.0.as_os_str() != STACKED_PUSH_MARKER {
marked.push(pair.clone());
}
}
marked.push((OsString::from(STACKED_PUSH_MARKER), OsString::from("1")));
return marked;
}
fn write_to_standard_error(text: &str) {
let _written = std::io::stderr().write_all(text.as_bytes());
}
fn status_of_forwarded(run: ForwardedRun) -> i32 {
return match run {
ForwardedRun::Ran(code) => code,
ForwardedRun::Failed { stderr, code } => {
write_to_standard_error(stderr.as_str());
code
}
};
}
fn run_planned(action: Action) -> i32 {
let marker = (OsString::from(STACKED_PUSH_MARKER), OsString::from("1"));
match action {
Action::Forward { real_git, arguments, mut overlay, stderr } => {
write_to_standard_error(stderr.as_str());
overlay.push(marker);
return status_of_forwarded(run_forwarded_git(real_git.as_path(), arguments.as_slice(), overlay.as_slice(), None));
}
Action::ForwardSettled { real_git, arguments, mut overlay, copy, stderr } => {
write_to_standard_error(stderr.as_str());
overlay.push(marker);
return status_of_forwarded(run_forwarded_git(real_git.as_path(), arguments.as_slice(), overlay.as_slice(), Some(&*copy)));
}
Action::ForwardPush { mut forward, stderr } => {
write_to_standard_error(stderr.as_str());
forward.overlay.push(marker);
return run_stacked_push(&forward);
}
Action::Exit { code, stdout, stderr } => {
let _written = std::io::stdout().write_all(stdout.as_bytes());
write_to_standard_error(stderr.as_str());
return code;
}
other => {
debug(TAG, format!("a merge push planned an action it cannot run: {other:?}").as_str());
write_to_standard_error("monochromatic-git-policy-cli: the merge push could not be planned; nothing was pushed.\n");
return ENGINE_FAILURE_EXIT_CODE;
}
}
}
pub fn gated_merge_push(
git: &PushGit,
global_prefix: &[OsString],
tokens: &[OsString],
destination: &[String],
) -> Result<i32, SingleFlightError> {
let mut arguments: Vec<OsString> = global_prefix.to_vec();
arguments.push(OsString::from("push"));
arguments.extend(tokens.iter().cloned());
arguments.extend(destination.iter().map(OsString::from));
let environment: Vec<(OsString, OsString)> = marked_environment(git.environment.as_slice());
let inputs = process_resolution_inputs(environment.as_slice()).map_err(SingleFlightError::Io)?;
let action: Action = plan_invocation(arguments.as_slice(), environment.as_slice(), &inputs);
return Ok(run_planned(action));
}