pub fn restore_double_dash(parsed_args: &[String]) -> Vec<String> {
let raw_args: Vec<String> = std::env::args().collect();
restore_double_dash_with_raw(parsed_args, &raw_args)
}
pub fn restore_double_dash_with_raw(parsed_args: &[String], raw_args: &[String]) -> Vec<String> {
let raw_dash_count = raw_args.iter().filter(|a| a.as_str() == "--").count();
let parsed_dash_count = parsed_args.iter().filter(|a| a.as_str() == "--").count();
if raw_dash_count <= parsed_dash_count {
return parsed_args.to_vec();
}
let missing_dashes = raw_dash_count - parsed_dash_count;
let user_region_len = parsed_args.len() + missing_dashes;
if raw_args.len() <= user_region_len {
return parsed_args.to_vec();
}
let user_region_start = raw_args.len() - user_region_len;
raw_args[user_region_start..].to_vec()
}
#[cfg(test)]
mod tests {
use super::*;
fn restore_with_raw(parsed: &[&str], raw: &[&str]) -> Vec<String> {
let parsed: Vec<String> = parsed.iter().map(|s| s.to_string()).collect();
let raw: Vec<String> = raw.iter().map(|s| s.to_string()).collect();
restore_double_dash_with_raw(parsed.as_slice(), raw.as_slice())
}
#[test]
fn test_single_dash_swallowed() {
let raw = vec!["rtk", "git", "diff", "--", "file"];
let parsed = vec!["file"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["--", "file"]);
}
#[test]
fn test_args_before_dash() {
let raw = vec!["rtk", "cargo", "test", "name", "--", "--nocapture"];
let parsed = vec!["name", "--nocapture"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["name", "--", "--nocapture"]
);
}
#[test]
fn test_multiple_dashes_all_swallowed() {
let raw = vec!["rtk", "git", "diff", "--", "--", "--"];
let parsed: Vec<&str> = vec![];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["--", "--", "--"]);
}
#[test]
fn test_dashes_with_args_between() {
let raw = vec!["rtk", "git", "diff", "--", "arg1", "--", "arg2"];
let parsed = vec!["arg1", "arg2"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["--", "arg1", "--", "arg2"]
);
}
#[test]
fn test_multiple_dashes_some_preserved() {
let raw = vec!["rtk", "git", "diff", "--", "--"];
let parsed = vec!["--"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["--", "--"]);
}
#[test]
fn test_dash_already_preserved() {
let raw = vec![
"rtk", "cargo", "clippy", "-p", "pkg", "--", "-D", "warnings",
];
let parsed = vec!["-p", "pkg", "--", "-D", "warnings"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["-p", "pkg", "--", "-D", "warnings"]
);
}
#[test]
fn test_trailing_dash_preserved() {
let raw = vec!["rtk", "git", "diff", "file", "--"];
let parsed = vec!["file", "--"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["file", "--"]);
}
#[test]
fn test_no_dash_in_original() {
let cases = vec![
(
vec!["rtk", "git", "diff", "feature/auth"],
vec!["feature/auth"],
),
(
vec!["rtk", "git", "diff", "main...feature"],
vec!["main...feature"],
),
(vec!["rtk", "git", "diff", "main"], vec!["main"]),
(
vec!["rtk", "git", "diff", "--stat", "--cached"],
vec!["--stat", "--cached"],
),
];
for (raw, parsed) in cases {
assert_eq!(restore_with_raw(&parsed, &raw), parsed);
}
}
#[test]
fn test_duplicate_args_both_sides() {
let raw = vec![
"rtk", "cargo", "clippy", "-p", "p1", "-p", "p2", "--", "-p", "p3",
];
let parsed = vec!["-p", "p1", "-p", "p2", "-p", "p3"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["-p", "p1", "-p", "p2", "--", "-p", "p3"]
);
}
#[test]
fn test_empty_args() {
let raw = vec!["rtk", "cargo", "test"];
let parsed: Vec<&str> = vec![];
assert_eq!(restore_with_raw(&parsed, &raw), Vec::<String>::new());
}
#[test]
fn test_cargo_clippy_missing_dash() {
let raw = vec!["rtk", "cargo", "clippy", "-D", "warnings"];
let parsed = vec!["-D", "warnings"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["-D", "warnings"]);
}
#[test]
fn test_positional_equals_subcommand() {
let raw = vec!["rtk", "git", "diff", "--", "diff"];
let parsed = vec!["diff"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["--", "diff"]);
}
#[test]
fn test_cargo_test_named_cargo() {
let raw = vec!["rtk", "cargo", "test", "cargo", "--", "--nocapture"];
let parsed = vec!["cargo", "--nocapture"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["cargo", "--", "--nocapture"]
);
}
#[test]
fn test_consecutive_dashes_before_file() {
let raw = vec!["rtk", "git", "diff", "--", "--", "file"];
let parsed_a = vec!["--", "file"];
assert_eq!(restore_with_raw(&parsed_a, &raw), vec!["--", "--", "file"]);
let parsed_b = vec!["file"];
assert_eq!(restore_with_raw(&parsed_b, &raw), vec!["--", "--", "file"]);
}
#[test]
fn test_git_diff_ref_before_path() {
let raw = vec!["rtk", "git", "diff", "HEAD", "--", "file"];
let parsed = vec!["HEAD", "file"];
assert_eq!(restore_with_raw(&parsed, &raw), vec!["HEAD", "--", "file"]);
}
#[test]
fn test_git_diff_flags_before_path() {
let raw = vec!["rtk", "git", "diff", "--cached", "--", "file"];
let parsed = vec!["--cached", "file"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["--cached", "--", "file"]
);
}
#[test]
fn test_git_diff_multiple_files() {
let raw = vec!["rtk", "git", "diff", "--", "file1", "file2", "file3"];
let parsed = vec!["file1", "file2", "file3"];
assert_eq!(
restore_with_raw(&parsed, &raw),
vec!["--", "file1", "file2", "file3"]
);
}
}