|
// Interpret the argument as a path so we can manipulate it~ |
|
let first_arg = arg_iter.next(); |
|
let bin_path = env::current_exe() |
|
.unwrap_or_else(|_| std::path::PathBuf::from(first_arg.unwrap_or_default())); |
|
// Get the extensionless-file name, and parse it case-insensitively~ |
|
let bin_name = bin_path |
|
.file_stem() |
|
.unwrap_or_default() |
|
.to_string_lossy() |
|
.to_lowercase(); |
|
let true_role = if let Some((_path, role)) = bin_name.rsplit_once("cargo-") { |
|
role.to_owned() |
|
} else { |
|
// If something messed up is going on "mommy" will always take care of it~ |
|
"mommy".to_owned() |
|
}; |
|
|
|
let cargo = env::var(format!("CARGO_{}S_ACTUAL", true_role.to_uppercase())) |
|
.or_else(|_| env::var("CARGO")) |
|
.unwrap_or_else(|_| "cargo".to_owned()); |
|
|
|
// Check if someone has told mommy to keep calling herself~ |
|
// Mommy loves you, darlings, but she can't keep running forever~ |
|
let mut new_limit = 1; |
|
if let Ok(limit) = env::var(RECURSION_LIMIT_VAR) { |
|
if let Ok(n) = limit.parse::<u8>() { |
|
if n > RECURSION_LIMIT { |
|
let mut response = select_response(&true_role, &rng, ResponseType::Overflow); |
|
match &mut response { |
|
Ok(s) | Err(s) => { |
|
*s += "\nyou didn't set CARGO to something naughty, did you?\n" |
|
} |
|
} |
|
pretty_print(response); |
|
return Ok(2); |
|
} else { |
|
new_limit = n + 1; |
|
} |
|
} |
|
} |
|
|
|
// *GASPS FOR BREATH* |
|
// |
|
// *INHALES DESPERATELY* |
|
// |
|
// cargo subcommands when run as "cargo blah" get the "blah" argument passed to themselves |
|
// as the *second* argument. However if we are invoked directly as "cargo-blah" we won't |
|
// have that extra argument! So if there's a second argument that is "mommy" (or whatever) |
|
// we pop off that redundant copy before forwarding the rest of the args back to "cargo ...". |
|
// if we don't do this, we'll infinitely recurse into ourselves by re-calling "cargo mommy"! |
|
// (note that it *is* supported to do `cargo mommy mommy` and get two messages, although I |
|
// believe we do this, `cargo-mommy mommy` will still only get you one message). |
|
|
|
if arg_iter.peek().map_or(false, |arg| arg == &true_role) { |
|
let _ = arg_iter.next(); |
|
} |
|
|
|
// *WHEEZES* |
|
// |
|
// *PANTS FOR A MINUTE* |
|
// |
|
// Ok so now we want to detect if the invocation looked like "cargo mommy i mean daddy" |
|
// if it *does* we want to copy ourselves to rename cargo-mommy to cargo-daddy. To make this |
|
// simpler, collect the args into a vec so we can peek more than one element. |
|
// |
|
// ... |
|
// |
|
// ~ |
|
let mut args: Vec<_> = arg_iter.collect(); |
|
{ |
|
// We speculate the "i mean" part so that can easily discard it |
|
// in the case of "cargo mommy i mean mommy", making the execution |
|
// equivalent to "cargo mommy mommy". Not popping off the extra |
|
// "mommy" let "cargo mommy i mean mommy i mean mommy" work right~ |
|
let new_role = args.get(2); |
|
let mean = args.get(1) == Some(&"mean".to_owned()); |
|
let i = args.get(0) == Some(&"i".to_owned()); |
|
if i && mean { |
|
if let Some(new_role) = new_role.cloned() { |
|
// Ok at this point we're confident we got "i mean <new_role>" |
|
// so definitely consume those arguments~ |
|
args.drain(..2); |
|
|
|
// If the new role is the same as before, they typed something like |
|
// "cargo mommy i mean mommy test" so we don't need to do anything~ |
|
if new_role != true_role { |
|
if let Some(parent) = bin_path.parent() { |
|
let new_bin_name = format!("cargo-{new_role}"); |
|
let mut new_bin_path = parent.join(new_bin_name); |
|
if let Some(ext) = bin_path.extension() { |
|
new_bin_path.set_extension(ext); |
|
} |
|
if let Err(e) = std::fs::copy(bin_path, new_bin_path) { |
|
Err(format!( |
|
"{role} couldn't copy {pronoun}self...\n{e:?}", |
|
role = ROLE.load(&true_role, &rng)?, |
|
pronoun = PRONOUN.load(&true_role, &rng)?, |
|
))? |
|
} else { |
|
// Just exit immediately on success, don't try to get too clever here~ |
|
eprintln!("{true_role} is now {new_role}~"); |
|
return Ok(0); |
|
} |
|
} else { |
|
Err(format!( |
|
"{role} couldn't copy {pronoun}self...\n(couldn't find own parent dir)", |
|
role = ROLE.load(&true_role, &rng)?, |
|
pronoun = PRONOUN.load(&true_role, &rng)?, |
|
))?; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Time for mommy to call cargo~ |
|
let mut cmd = std::process::Command::new(cargo); |
|
cmd.args(args) |
|
.env(RECURSION_LIMIT_VAR, new_limit.to_string()); |
|
let status = cmd.status()?; |
|
let code = status.code().unwrap_or(1); |
|
if is_quiet_mode_enabled(cmd.get_args()) { |
|
return Ok(code); |
|
} |