use crate::action::{Action, ENGINE_FAILURE_EXIT_CODE, failure};
use crate::index_writer_forward::CoordinatedForward;
use crate::index_writer_commands::is_index_writer;
use crate::forwarded_command::{ResolvedCommand, Route, creates_worktrees, resolve_forwarded_command};
use crate::invocation_config::{config_invalid_event, load_identity_config};
use crate::repository_location::RepositoryLocation;
use crate::transaction_git::GitContext;
use crate::worktree_identity::WorktreeIdentity;
use crate::policy_checks::ShippedChecks;
use crate::repository_facts::{GitFacts, RepositoryFacts};
use crate::worktree_add_plan::{AddPlan, copy_start_directory, parse_add_plan, parse_copy_destination, start_directory};
use crate::worktree_remove_plan::{RemovePlan, parse_remove_plan};
use crate::worktree_copy_forward::{CopyFacts, copy_applies_with};
use std::ffi::OsString;
use std::path::PathBuf;
pub(crate) fn forward_action(
real_git: PathBuf,
arguments: Vec<OsString>,
overlay: Vec<(OsString, OsString)>,
stderr: String,
environment: &[(OsString, OsString)],
copy_bypassed: bool,
checks: &mut ShippedChecks<GitFacts>,
) -> Action {
let context: GitContext = GitContext {
real_git: real_git.clone(),
overlay: overlay.clone(),
global_prefix: Vec::new(),
common_dir: None,
};
let command: ResolvedCommand = resolve_forwarded_command(&context, arguments.as_slice());
let forwarded_arguments: Vec<OsString> = match command.route {
Route::Alias => command.arguments.clone(),
Route::Direct | Route::ShellAlias | Route::Unresolved => arguments,
};
let writes_index: bool = is_index_writer(&command);
if !writes_index && copy_bypassed {
return Action::Forward {
real_git,
arguments: forwarded_arguments,
overlay,
stderr,
};
}
let location: RepositoryLocation = match checks.facts.location() {
Ok(found) => found,
Err(message) => return failure(message.as_str()),
};
if location.identity == WorktreeIdentity::OutsideWorktree {
return Action::Forward {
real_git,
arguments: forwarded_arguments,
overlay,
stderr,
};
}
if configuration_needed(writes_index)
&& let Err(error) = load_identity_config(&location.identity)
{
return Action::Exit {
code: ENGINE_FAILURE_EXIT_CODE,
stdout: String::new(),
stderr: config_invalid_event(&error),
};
}
let creates: bool = creates_worktrees(&command);
let add_plan: Option<AddPlan> = if creates { plan_worktree_add(&command) } else { None };
let destination: Option<PathBuf> = if creates { plan_copy_destination(&command) } else { None };
let remove_plan: Option<RemovePlan> = if command.word() == b"worktree" { plan_worktree_remove(&command) } else { None };
let serves_ladder: bool = add_plan.is_some() || remove_plan.is_some();
let copy: Option<CopyFacts> =
if !copy_bypassed && copy_applies_with(&location.identity, creates, serves_ladder) {
Some(CopyFacts {
identity: location.identity.clone(),
creates,
destination,
add_plan,
remove_plan,
})
} else {
None
};
if !writes_index {
let Some(facts) = copy else {
return Action::Forward {
real_git,
arguments: forwarded_arguments,
overlay,
stderr,
};
};
return Action::ForwardSettled {
real_git,
arguments: forwarded_arguments,
overlay,
copy: Box::new(facts),
stderr,
};
}
return Action::ForwardCoordinated {
forward: Box::new(CoordinatedForward {
real_git,
arguments: forwarded_arguments,
overlay,
identity: location.identity,
command,
environment: environment.to_vec(),
copy,
}),
stderr,
};
}
fn plan_worktree_add(command: &ResolvedCommand) -> Option<AddPlan> {
let current: PathBuf = std::env::current_dir().ok()?;
let start: PathBuf = start_directory(&command.arguments[..command.subcommand_index], current.as_path())?;
return parse_add_plan(command.arguments.as_slice(), command.subcommand_index, start.as_path());
}
fn plan_copy_destination(command: &ResolvedCommand) -> Option<PathBuf> {
let current: PathBuf = std::env::current_dir().ok()?;
let start: PathBuf = copy_start_directory(&command.arguments[..command.subcommand_index], current.as_path())?;
return parse_copy_destination(command.arguments.as_slice(), command.subcommand_index, start.as_path());
}
fn plan_worktree_remove(command: &ResolvedCommand) -> Option<RemovePlan> {
let current: PathBuf = std::env::current_dir().ok()?;
let start: PathBuf = start_directory(&command.arguments[..command.subcommand_index], current.as_path())?;
return parse_remove_plan(command.arguments.as_slice(), command.subcommand_index, start.as_path());
}
fn configuration_needed(writes_index: bool) -> bool {
return writes_index;
}