use crate::command_checkout_table::{
CHECKOUT_TABLE, DETACH, FORCE, NEW_BRANCH, NEW_BRANCH_FORCE, ORPHAN, PATCH, PATHSPEC_FROM_FILE,
};
use crate::command_options::{Boundary, DEFAULT_MODE, ParsedOptions, parse_options};
use crate::command_options_query::is_stated;
use crate::diagnostic_log::debug;
use std::ffi::OsString;
const TAG: &str = "conflictSideRequest";
fn declined(word: &str, reason: &str, region: &[OsString]) -> Option<SideRequest> {
debug(TAG, &format!("git {word} {region:?} is not read as taking a side of a conflicted file: {reason}"));
return None;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SideRequest {
pub paths: Vec<OsString>,
}
fn checkout_request(region: &[OsString]) -> Option<SideRequest> {
let Ok(parsed): Result<ParsedOptions, _> = parse_options(region, CHECKOUT_TABLE, DEFAULT_MODE, &[]) else {
return declined("checkout", "an option is not one Git's table lists", region);
};
let Boundary::DashDash(at) = parsed.boundary else {
return declined("checkout", "there is no `--` before the paths", region);
};
for forbidden in [NEW_BRANCH, NEW_BRANCH_FORCE, ORPHAN, DETACH, PATCH, FORCE, PATHSPEC_FROM_FILE] {
if is_stated(&parsed, forbidden) {
return declined("checkout", "it forces, patches, makes a branch, detaches or reads paths from a file", region);
}
}
if parsed.leading.len() > 1 {
return declined("checkout", "it names more than one thing before `--`", region);
}
let paths: Vec<OsString> = region[at + 1..].to_vec();
if paths.is_empty() {
return declined("checkout", "no path follows `--`", region);
}
return Some(SideRequest { paths });
}
fn is_equals(byte: &u8) -> bool {
return *byte == b'=';
}
const RESTORE_PLAIN: [&[u8]; 14] = [
b"--ours",
b"--theirs",
b"-m",
b"--merge",
b"-W",
b"--worktree",
b"-q",
b"--quiet",
b"--progress",
b"--no-progress",
b"--overlay",
b"--no-overlay",
b"--ignore-unmerged",
b"--ignore-skip-worktree-bits",
];
const RESTORE_VALUED: [&[u8]; 3] = [b"--source", b"-s", b"--conflict"];
fn restore_request(region: &[OsString]) -> Option<SideRequest> {
let mut staged: bool = false;
let mut worktree: bool = false;
let mut at: usize = 0;
while at < region.len() {
let token: &[u8] = region[at].as_encoded_bytes();
if token == b"--" {
if staged && !worktree {
return declined("restore", "it restores the index only, which writes no worktree file", region);
}
let paths: Vec<OsString> = region[at + 1..].to_vec();
if paths.is_empty() {
return declined("restore", "no path follows `--`", region);
}
return Some(SideRequest { paths });
}
if token == b"-W" || token == b"--worktree" {
worktree = true;
at += 1;
continue;
}
if RESTORE_PLAIN.contains(&token) {
at += 1;
continue;
}
if token == b"-S" || token == b"--staged" {
staged = true;
at += 1;
continue;
}
let name: &[u8] = token.split(is_equals).next().unwrap_or_default();
if RESTORE_VALUED.contains(&name) {
at += if name.len() < token.len() { 1 } else { 2 };
continue;
}
return declined("restore", format!("`{}` is not a spelling this reading lists", region[at].to_string_lossy()).as_str(), region);
}
return declined("restore", "there is no `--` before the paths", region);
}
pub fn conflict_side_request(word: &[u8], region: &[OsString]) -> Option<SideRequest> {
if word == b"checkout" {
return checkout_request(region);
}
if word == b"restore" {
return restore_request(region);
}
return None;
}
#[cfg(test)]
#[path = "command_conflict_side_tests.rs"]
mod tests;