-
Notifications
You must be signed in to change notification settings - Fork 52
Open
Labels
enhancementNew feature or requestNew feature or requestquestionFurther information is requestedFurther information is requested
Description
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:
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); | |
| } |
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestquestionFurther information is requestedFurther information is requested