use crate::forwarded_command::ResolvedCommand;
pub const ALWAYS_WRITING_COMMANDS: [&[u8]; 15] = [
b"add",
b"am",
b"checkout",
b"cherry-pick",
b"merge",
b"mv",
b"pull",
b"read-tree",
b"rebase",
b"revert",
b"rm",
b"sparse-checkout",
b"stash",
b"switch",
b"update-index",
];
#[derive(Clone, Copy, Debug)]
pub struct LongOption {
pub name: &'static [u8],
pub minimum_length: usize,
}
pub const RESTORE_STAGED: LongOption = LongOption { name: b"--staged", minimum_length: 4 };
pub const RESET_SOFT: LongOption = LongOption { name: b"--soft", minimum_length: 3 };
pub const APPLY_CACHED: LongOption = LongOption { name: b"--cached", minimum_length: 4 };
pub const APPLY_INDEX: LongOption = LongOption { name: b"--index", minimum_length: 5 };
pub fn option_tokens(command: &ResolvedCommand) -> Vec<&[u8]> {
let mut options: Vec<&[u8]> = Vec::new();
for token in command.region() {
let bytes: &[u8] = token.as_encoded_bytes();
if bytes == b"--" {
break;
}
if bytes.starts_with(b"-") && bytes != b"-" {
options.push(bytes);
}
}
return options;
}
pub fn matches_long(token: &[u8], option: LongOption) -> bool {
let name: &[u8] = match token.iter().position(is_equals) {
Some(end) => &token[..end],
None => token,
};
return name.len() >= option.minimum_length && option.name.starts_with(name);
}
fn is_equals(byte: &u8) -> bool {
return *byte == b'=';
}
pub fn cluster_has(token: &[u8], letter: u8, value_letters: &[u8]) -> bool {
if !token.starts_with(b"-") || token.starts_with(b"--") {
return false;
}
for byte in &token[1..] {
if *byte == letter {
return true;
}
if value_letters.contains(byte) {
return false;
}
}
return false;
}
pub fn is_index_writer(command: &ResolvedCommand) -> bool {
if !command.is_builtin() {
return false;
}
let word: &[u8] = command.word();
if ALWAYS_WRITING_COMMANDS.contains(&word) {
return true;
}
let options: Vec<&[u8]> = option_tokens(command);
if word == b"restore" {
for token in &options {
if matches_long(token, RESTORE_STAGED) || cluster_has(token, b'S', b"s") {
return true;
}
}
return false;
}
if word == b"reset" {
for token in &options {
if matches_long(token, RESET_SOFT) {
return false;
}
}
return true;
}
if word == b"apply" {
for token in &options {
if matches_long(token, APPLY_CACHED) || matches_long(token, APPLY_INDEX) {
return true;
}
}
}
return false;
}
#[cfg(test)]
#[path = "index_writer_commands_tests.rs"]
mod tests;