use crate::command_add::parse_add_region;
use crate::command_branch_create::{branch_creation_command, parse_branch_creation_region};
use crate::command_clean::parse_clean_region;
use crate::command_commit::parse_commit_region;
use crate::command_config::parse_config_region;
use crate::command_options::{OptionError, WrapperOccurrence};
use crate::command_options_query::{WrapperFlags, without_tokens};
use crate::command_push::parse_push_region;
use crate::command_reset::parse_reset_region;
use crate::command_stash::parse_stash_region;
use crate::command_status::parse_status_region;
use crate::command_worktree::changes_worktree_registrations;
use crate::global_arguments::{GlobalLayout, GlobalOutcome, global_layout};
use crate::policy_registry::PolicyId;
use crate::wrapper_controls::{
CONTROL_SPELLINGS, ControlMeaning, Controls, control_flags, control_meaning, no_controls,
record_control, strip_global_controls,
};
use std::ffi::OsString;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RegionReading {
NoCommand,
Table,
LeadingOnly,
Refused(OptionError),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StrippedInvocation {
pub arguments: Vec<OsString>,
pub layout: GlobalLayout,
pub controls: Controls,
pub reading: RegionReading,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum OwnHatch {
None,
CommitOnly,
Policy(PolicyId),
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct TableControls {
flags: WrapperFlags,
own: OwnHatch,
}
fn without_own_hatch(other: Vec<WrapperOccurrence>) -> TableControls {
return TableControls {
flags: WrapperFlags {
escape: Vec::<usize>::new(),
other,
},
own: OwnHatch::None,
};
}
fn read_table(
word: &[u8],
region: &[OsString],
flags: &[&[u8]],
) -> Result<Option<TableControls>, OptionError> {
if word == b"commit" {
return Ok(Some(TableControls {
flags: parse_commit_region(region, flags)?.wrapper,
own: OwnHatch::CommitOnly,
}));
}
if word == b"add" {
return Ok(Some(TableControls {
flags: parse_add_region(region, flags)?.wrapper,
own: OwnHatch::Policy(PolicyId::AddExplicit),
}));
}
if word == b"reset" {
return Ok(Some(TableControls {
flags: parse_reset_region(region, flags)?.wrapper,
own: OwnHatch::Policy(PolicyId::LinkedWorktreeOnly),
}));
}
if word == b"clean" {
return Ok(Some(TableControls {
flags: parse_clean_region(region, flags)?.wrapper,
own: OwnHatch::Policy(PolicyId::LinkedWorktreeOnly),
}));
}
if word == b"stash" {
return Ok(Some(TableControls {
flags: parse_stash_region(region, flags)?.wrapper,
own: OwnHatch::Policy(PolicyId::LinkedWorktreeOnly),
}));
}
if let Some(command) = branch_creation_command(word) {
return Ok(Some(TableControls {
flags: parse_branch_creation_region(command, region, flags)?.wrapper,
own: OwnHatch::Policy(PolicyId::BranchWorktreeOnly),
}));
}
if word == b"push" {
return Ok(Some(without_own_hatch(
parse_push_region(region, flags)?.wrapper,
)));
}
if word == b"status" {
return Ok(Some(without_own_hatch(
parse_status_region(region, flags)?.wrapper,
)));
}
if word == b"config" {
return Ok(Some(without_own_hatch(
parse_config_region(region, flags)?.wrapper,
)));
}
return Ok(None);
}
fn record_table_controls(found: &TableControls, controls: &mut Controls) -> Vec<usize> {
let mut positions: Vec<usize> = found.flags.escape.clone();
if !found.flags.escape.is_empty() {
match found.own {
OwnHatch::None => {}
OwnHatch::CommitOnly => controls.commit_only_escaped = true,
OwnHatch::Policy(policy) => record_control(controls, ControlMeaning::Escape(policy)),
}
}
for occurrence in &found.flags.other {
record_control(controls, CONTROL_SPELLINGS[occurrence.flag].meaning);
positions.push(occurrence.token);
}
return positions;
}
fn leading_controls(region: &[OsString], controls: &mut Controls) -> Vec<usize> {
let mut positions: Vec<usize> = Vec::<usize>::new();
for (index, token) in region.iter().enumerate() {
let Some(meaning) = control_meaning(token.as_encoded_bytes()) else {
break;
};
record_control(controls, meaning);
positions.push(index);
}
return positions;
}
fn untabled_controls(word: &[u8], region: &[OsString], controls: &mut Controls) -> Vec<usize> {
let mut positions: Vec<usize> = leading_controls(region, controls);
let second_word: usize = positions.len();
if word == b"worktree" && changes_worktree_registrations(®ion[second_word..]) {
let after: usize = second_word + 1;
for position in leading_controls(®ion[after..], controls) {
positions.push(position + after);
}
}
return positions;
}
pub fn strip_wrapper_controls(arguments: &[OsString]) -> StrippedInvocation {
let mut controls: Controls = no_controls();
let global_clean: Vec<OsString> = strip_global_controls(arguments, &mut controls);
let layout: GlobalLayout = global_layout(global_clean.as_slice());
if layout.outcome != GlobalOutcome::Command {
return StrippedInvocation {
arguments: global_clean,
layout,
controls,
reading: RegionReading::NoCommand,
};
}
let region_start: usize = layout.prefix_len + 1;
let region: &[OsString] = &global_clean[region_start..];
let flags: Vec<&'static [u8]> = control_flags();
let word: &[u8] = global_clean[layout.prefix_len].as_encoded_bytes();
let table: Result<Option<TableControls>, OptionError> =
read_table(word, region, flags.as_slice());
let (positions, reading): (Vec<usize>, RegionReading) = match table {
Ok(Some(found)) => (
record_table_controls(&found, &mut controls),
RegionReading::Table,
),
Ok(None) => (
untabled_controls(word, region, &mut controls),
RegionReading::LeadingOnly,
),
Err(error) => (
leading_controls(region, &mut controls),
RegionReading::Refused(error),
),
};
return StrippedInvocation {
arguments: without_tokens(global_clean.as_slice(), region_start, positions.as_slice()),
layout,
controls,
reading,
};
}
pub fn command_word(stripped: &StrippedInvocation) -> &[u8] {
return stripped.arguments[stripped.layout.prefix_len].as_encoded_bytes();
}
pub fn command_region(stripped: &StrippedInvocation) -> &[OsString] {
return &stripped.arguments[stripped.layout.prefix_len + 1..];
}
#[cfg(test)]
#[path = "wrapper_invocation_tests.rs"]
mod tests;