use crate::command_commit_table::{
ALL, ALLOW_EMPTY, AMEND, COMMIT_TABLE, DRY_RUN, FIXUP, INCLUDE, INTERACTIVE, LONG, NULL, ONLY,
PATCH, PATHSPEC_FILE_NUL, PATHSPEC_FROM_FILE, PORCELAIN, SHORT,
};
use crate::command_options::{
DEFAULT_MODE, Occurrence, OptionError, OptionValue, ParsedOptions, parse_options,
};
use crate::command_options_query::{
WrapperFlags, is_enabled, last_occurrence, positional_tokens, split_wrapper_flags, value_bytes,
};
use std::ffi::OsString;
pub const COMMIT_ESCAPE_HATCH: &str = "--no-enforce-only";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FixupKind {
Plain,
Amend,
Reword,
UnknownSuboption,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommitRegion {
pub all: bool,
pub only: Option<bool>,
pub include: bool,
pub interactive: bool,
pub patch: bool,
pub dry_run: bool,
pub amend: bool,
pub allow_empty: bool,
pub fixup: Option<FixupKind>,
pub pathspec_from_file: Option<OptionValue>,
pub pathspec_file_nul: bool,
pub pathspecs: Vec<usize>,
pub wrapper: WrapperFlags,
}
fn fixup_kind(value: &[u8]) -> FixupKind {
let mut letters: usize = 0;
while letters < value.len() && value[letters].is_ascii_alphabetic() {
letters += 1;
}
if letters == 0 || letters == value.len() || value[letters] != b':' {
return FixupKind::Plain;
}
let suboption: &[u8] = &value[..letters];
if suboption == b"amend" {
return FixupKind::Amend;
}
if suboption == b"reword" {
return FixupKind::Reword;
}
return FixupKind::UnknownSuboption;
}
fn has_status_format(parsed: &ParsedOptions) -> bool {
let mut format: bool = false;
for occurrence in &parsed.occurrences {
if occurrence.id == SHORT || occurrence.id == PORCELAIN || occurrence.id == LONG {
format = !occurrence.negated;
}
}
return format;
}
fn final_value(parsed: &ParsedOptions, id: u16) -> Option<OptionValue> {
let last: Option<Occurrence> = last_occurrence(parsed, id);
if let Some(found) = last {
if found.negated {
return None;
}
return found.value;
}
return None;
}
pub fn parse_commit_region(
region: &[OsString],
wrapper_flags: &[&[u8]],
) -> Result<CommitRegion, OptionError> {
let mut flags: Vec<&[u8]> = Vec::<&[u8]>::with_capacity(wrapper_flags.len() + 1);
flags.push(COMMIT_ESCAPE_HATCH.as_bytes());
for flag in wrapper_flags {
flags.push(*flag);
}
let parsed: ParsedOptions =
parse_options(region, COMMIT_TABLE, DEFAULT_MODE, flags.as_slice())?;
let mut only: Option<bool> = None;
if let Some(stated) = last_occurrence(&parsed, ONLY) {
only = Some(!stated.negated);
}
let mut fixup: Option<FixupKind> = None;
if let Some(value) = final_value(&parsed, FIXUP) {
fixup = Some(fixup_kind(value_bytes(region, value)));
}
return Ok(CommitRegion {
all: is_enabled(&parsed, ALL),
only,
include: is_enabled(&parsed, INCLUDE),
interactive: is_enabled(&parsed, INTERACTIVE),
patch: is_enabled(&parsed, PATCH),
dry_run: is_enabled(&parsed, DRY_RUN)
|| has_status_format(&parsed)
|| is_enabled(&parsed, NULL),
amend: is_enabled(&parsed, AMEND),
allow_empty: is_enabled(&parsed, ALLOW_EMPTY),
fixup,
pathspec_from_file: final_value(&parsed, PATHSPEC_FROM_FILE),
pathspec_file_nul: is_enabled(&parsed, PATHSPEC_FILE_NUL),
pathspecs: positional_tokens(&parsed, region.len()),
wrapper: split_wrapper_flags(parsed.wrapper.as_slice()),
});
}
#[cfg(test)]
#[path = "command_commit_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "command_commit_git_tests.rs"]
mod git_tests;