Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

standalone "mommy" binary? #65

Open
Gankra opened this issue Nov 20, 2023 · 3 comments
Open

standalone "mommy" binary? #65

Gankra opened this issue Nov 20, 2023 · 3 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@Gankra
Copy link
Owner

Gankra commented Nov 20, 2023

Many people have asked, it might be reasonable to have the cargo-mommy package include two binaries: cargo-mommy (the current one) and mommy (a new one). The mommy binary would behave more like time or whatever, in that it would execute all the arguments that follow, without any notion of "re-invoking cargo". cargo mommy check would be basically equivalent to mommy cargo check, i think.

If we want to do this, the bulk of main.rs should be hoisted into a new lib.rs, as both binaries would share most logic.

They would basically differ in the cli logic in main.rs:

cargo-mommy/src/main.rs

Lines 45 to 168 in 0ec1736

// 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);
}

@Gankra Gankra added enhancement New feature or request question Further information is requested labels Nov 20, 2023
@Beyley
Copy link

Beyley commented Dec 1, 2023

Ideally this separate binary would have the optional ability to just take in a process exit code number and use that directly, its possible already using CARGO_MOMMYS_ACTUAL=bash cargo-mommy -c "exit $?" in a zsh precmd hook to integrate it with my shell, but it feels like quite a hack, it'd be nice if i could just pass cargo mommy the $? exit code directly and have it react on that, since then there isnt the overhead of spinning up bash and running exit and all

@Zopolis4
Copy link

Zopolis4 commented Dec 1, 2023

That's how FWDekker/mommy does it, and that works quite well especially for shell integration.

@lucyamonster
Copy link

CARGO_MOMMYS_ACTUAL=bash cargo-mommy -c "exit $?"

Doesn't work for me but CARGO_MOMMYS_ACTUAL=echo >> /dev/null cargo-mommy does and it feel a bit more clean. Although a standalone binary would still be nice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants