use crate::command_commit::{CommitRegion, FixupKind, parse_commit_region};
use crate::command_options::OptionError;
use crate::command_options_query::without_tokens;
use crate::global_arguments::{GlobalLayout, GlobalOutcome, global_layout};
use crate::rule_argument_rewrite::insert_tokens;
use crate::rule_commit_index::IndexVsHead;
use crate::rule_commit_only_message::{
ALL_FLAG_MESSAGE, NO_PATHSPEC_MESSAGE, ignored_index_message,
};
use crate::rule_commit_sequencer::SequencerState;
use std::ffi::OsString;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CommitOnlyViolationCode {
AllFlag,
PathspecRequired,
StagedChangesIgnored,
}
pub fn violation_code_text(code: CommitOnlyViolationCode) -> &'static str {
return match code {
CommitOnlyViolationCode::AllFlag => "all-flag",
CommitOnlyViolationCode::PathspecRequired => "pathspec-required",
CommitOnlyViolationCode::StagedChangesIgnored => "staged-changes-ignored",
};
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommitOnlyViolation {
pub code: CommitOnlyViolationCode,
pub message: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PendingInjection {
pub command_index: usize,
pub flag_text: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CommitOnlyDecision {
Unchanged,
Rewritten(Vec<OsString>),
Rejected(CommitOnlyViolation),
NeedsSequencerState,
NeedsIndexState(PendingInjection),
}
fn rejected(code: CommitOnlyViolationCode, message: &str) -> CommitOnlyDecision {
return CommitOnlyDecision::Rejected(CommitOnlyViolation {
code,
message: String::from(message),
});
}
fn pathless_flag_text(region: &CommitRegion) -> String {
let mut parts: Vec<&str> = Vec::<&str>::new();
if region.amend {
parts.push("--amend");
}
if region.allow_empty {
parts.push("--allow-empty");
}
if region.fixup == Some(FixupKind::Amend) {
parts.push("--fixup=amend:<commit>");
}
return parts.join(" ");
}
pub fn decide_commit_only(
arguments: &[OsString],
wrapper_flags: &[&[u8]],
) -> Result<CommitOnlyDecision, OptionError> {
let layout: GlobalLayout = global_layout(arguments);
if layout.outcome != GlobalOutcome::Command
|| arguments[layout.prefix_len].as_encoded_bytes() != b"commit"
{
return Ok(CommitOnlyDecision::Unchanged);
}
let region_start: usize = layout.prefix_len + 1;
let region: CommitRegion = parse_commit_region(&arguments[region_start..], wrapper_flags)?;
if !region.wrapper.escape.is_empty() {
return Ok(CommitOnlyDecision::Rewritten(without_tokens(
arguments,
region_start,
region.wrapper.escape.as_slice(),
)));
}
let fixup_owns_only: bool = region.fixup == Some(FixupKind::Reword)
|| region.fixup == Some(FixupKind::UnknownSuboption);
let pathless_allowed: bool =
region.amend || region.allow_empty || region.fixup == Some(FixupKind::Amend);
let has_paths: bool = !region.pathspecs.is_empty() || region.pathspec_from_file.is_some();
if region.only != Some(false) {
if region.all {
return Ok(rejected(CommitOnlyViolationCode::AllFlag, ALL_FLAG_MESSAGE));
}
let has_source: bool =
has_paths || pathless_allowed || fixup_owns_only || region.interactive || region.patch;
if !has_source {
return Ok(CommitOnlyDecision::NeedsSequencerState);
}
}
if region.only.is_some() {
return Ok(CommitOnlyDecision::Unchanged);
}
if region.include || region.interactive || region.patch || fixup_owns_only {
return Ok(CommitOnlyDecision::Unchanged);
}
if pathless_allowed && !has_paths {
return Ok(CommitOnlyDecision::NeedsIndexState(PendingInjection {
command_index: layout.prefix_len,
flag_text: pathless_flag_text(®ion),
}));
}
return Ok(CommitOnlyDecision::Rewritten(insert_tokens(
arguments,
region_start,
&["-o"],
)));
}
pub fn resolve_sequencer_state(state: SequencerState) -> CommitOnlyDecision {
if state == SequencerState::InProgress {
return CommitOnlyDecision::Unchanged;
}
return rejected(
CommitOnlyViolationCode::PathspecRequired,
NO_PATHSPEC_MESSAGE,
);
}
pub fn resolve_index_state(
arguments: &[OsString],
pending: &PendingInjection,
state: IndexVsHead,
) -> CommitOnlyDecision {
if state == IndexVsHead::Differs {
return rejected(
CommitOnlyViolationCode::StagedChangesIgnored,
ignored_index_message(pending.flag_text.as_str()).as_str(),
);
}
return CommitOnlyDecision::Rewritten(insert_tokens(
arguments,
pending.command_index + 1,
&["-o"],
));
}
#[cfg(test)]
#[path = "rule_commit_only_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "rule_commit_only_divergence_tests.rs"]
mod divergence_tests;
#[cfg(test)]
#[path = "rule_commit_only_git_tests.rs"]
mod git_tests;