-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
No argument validation on globals #1546
Comments
I'll +1 this issue, I would like this to work as well. My tool has a bunch of universal required arguments (e.g. input directory) and then different tasks have some specific options. I was hoping to have usage look like
Where Maybe there's a better way to set this up? |
I don't know why this is the case, but a workaround now would be defining a This is one way, however one should keep in mind that this default value also limits what can be done with value. In my case, I will use an arg as Telegram token value, which is a long string, so setting default to Wish this will be implemented soon. |
I would welcome people to design how this should work and implement it. Unfortunately, as it is now, we have no design to implement this because the |
Until this is supported, maybe the derive macro could raise an error when |
I have come to say that I have run across this use-case several times now. It's been a long time (since Sep 2019) and this should be considered. |
I can understand the desire for this but it unfortunately isn't a priority. Before, the focus was on finishing the committed items for clap 3 by maintainers who had little time. We are now have some more liberal sponsorship (my employer) but I've completed the sponsored objective (clap3) and have to balance clap work against other sponsored objectives (cargo-edit atm). We have around 160 open issues which all exist because someone cares about them and we have to prioritize among them. Our current area of focus is in re-shaping clap to be more open ended with faster compile times and smaller binary sizes. If you'd like to work on this, I would be willing to help mentor you on it. |
I'd like to start working on this. I'm just not sure where in the codebase I should be looking first. |
The relevant calls are roughly
Naively swapping the calls around would probably affect the way globals and defaults interact. Whether we are willing to change that is up to the details of the situation. Unsure what other concerns might lurk in this. Note that master is now for v4. I would encourage doing development on a major feature there. If you want it earlier than v4's release (trying to keep it narrowly scoped to not extend more than a month or two) then once its merged, you can cherry pick it into the v3-master branch. |
OK, I've bitten off more than I can chew. I'm gonna need more help on this if anything. It's hard for me to track where things are going even stepping through with a debugger. Here's a minimal example: use clap::{Arg, Command};
fn main() {
let subcmd1 = Command::new("sub1");
let subcmd2 = Command::new("sub2")
.arg(clap::Arg::new("optional_arg").help("Optional argument for subcommand"));
let cli = Command::new("clap-fix")
.arg(Arg::new("string_arg").required(true).global(true))
.arg(Arg::new("num_arg").global(true).short('n').long("num-arg"))
.subcommand(subcmd1)
.subcommand(subcmd2);
// This works but it's incorrect.
let _ = cli.get_matches_from(["clap-fix", "string_arg_before", "sub1", "string_arg_after"]);
// If this works then we're good.
// let _ = cli.get_matches_from(["clap-fix", "sub1", "string_arg_after"]);
} I also saw you guys were on opencollective. Can I open a bounty on this issue? |
Probably? Previous maintainers were the ones mostly dealing with that; I have little experience. I've not seen people take up bounties too much for issues. |
@epage was this fixed recently, seems to work with 0.4.29? |
ah sorry, the issue persists, just the error message is wrong now. should I open a separate issue?
|
Is it possible to work around this by manually producing the right error when a global argument isn't specified? I tried handling the Foo::command()
.error(
MissingRequiredArgument,
"the following required arguments were not provided:\n \x1b[32m--application <APP>\x1b[0m",
)
.exit() And that seems to produce the right error:
EDIT: The original error message had an error |
If it isn't important to provide the custom usage, I'd just drop that and have In theory, we've opened up all of the APIs so you can generate the error like clap:
See https://docs.rs/clap/latest/src/clap/error/mod.rs.html#487 The RichFormatter will then generate the appropriate message for you. |
Sorry if I'm missing something, but is there any technical reason this can't be done? Like if the check for when an |
Currently, globals are propagated after all validation has occurred which would cause missing-required errors where they don't apply. For details, see my earlier comment |
This is very strange and does not conform to the normal usage.
|
Sometimes you want flags that are available for each subcommands. Replicating it in every subcommand's config is not great for maintainability.
…________________________________
From: KK Cheung ***@***.***>
Sent: Wednesday, November 29, 2023 11:55:26 AM
To: clap-rs/clap ***@***.***>
Cc: Kibouo ***@***.***>; Manual ***@***.***>
Subject: Re: [clap-rs/clap] Why can't global args be required? (#1546)
This is very strange and does not conform to the normal usage.
cmd cubcmd --global-arg # not work
cmd --global-arg cubcmd # work
—
Reply to this email directly, view it on GitHub<#1546 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AGESTOPXHBMVPTJIIHPCM5DYG4IB5AVCNFSM4IWKXEK2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBTGE3DMNJYGQ4A>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
While this isn't solved, I've managed to implement a rather (little) hacky way to still get a nice error message when using use clap::{CommandFactory as _, FromArgMatches as _, Parser};
pub const DEFAULT_NOT_SET: &str = "\u{0}\u{beef}";
#[derive(Debug, Parser)]
pub struct Cli {
#[arg(global = true, long, default_value = DEFAULT_NOT_SET)]
pub required: String,
#[command(subcommand)]
pub subcommand: Option<Subcommand>,
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
Command {
},
}
pub fn main() -> Result<(),Box<dyn std::error::Error>> {
let _cli: Cli = {
let mut cmd = Cli::command();
let matches = Cli::command().get_matches_from(["playground", "command"]);
let mut strings = vec![];
let succ = std::iter::successors(Some((cmd.clone(), &matches)), |(cmd, matches)| {
let (sub, matches) = matches.subcommand()?;
Some((cmd.find_subcommand(sub)?.clone(), matches))
});
for (cmd, matches) in succ {
let ids = cmd
.get_arguments()
.filter(|&arg| arg.is_global_set())
.map(|arg| {
(
arg.clone()
// this is a hacky way to display, without the override here the stylized call will panic
.num_args(arg.get_num_args().unwrap_or_default())
.to_string(),
arg.get_id(),
)
});
for (display, id) in ids {
if !matches!(
matches.value_source(id.as_str()),
Some(clap::parser::ValueSource::DefaultValue)
) {
continue;
}
let mut raw = matches.get_raw(id.as_str()).unwrap();
if raw.len() != 1 {
continue;
}
let raw = raw.next().unwrap();
if raw.to_string_lossy() != DEFAULT_NOT_SET {
continue;
}
strings.push(display);
}
}
if strings.is_empty() {
Cli::from_arg_matches(&matches)?
} else {
let mut err = clap::Error::new(clap::error::ErrorKind::MissingRequiredArgument);
err.insert(
clap::error::ContextKind::InvalidArg,
clap::error::ContextValue::Strings(strings),
);
err.insert(
clap::error::ContextKind::Usage,
clap::error::ContextValue::StyledStr(cmd.render_usage()),
);
err.with_cmd(&cmd).exit();
}
};
Ok(())
} one thing missing here is usage, since there is no good way to render it the same way clap does internally. |
Now common args like `--secret-name` can be specified after the sub commands. Common args like `--secret-name` are needed by every sub commands. To avoid declaring them in every sub command, they are specified outside sub commands. I think `ksher cmd --secret-name XX` means better than `ksher --secret-name XX cmd`, hence they are set as `global`. But per clap-rs/clap#1546, an arg cannot be global and required at the same time. So `--secret-name` is set as `Option` and checked manually to throw a clap style error message.
This came up in clap-rs#5812 and is especially problematic for derives. Not really a fan of this solution but its the least invasive. I also considered going wild with error recovery or moving towards a solution for clap-rs#1546.
This came up in clap-rs#5812 and is especially problematic for derives. Not really a fan of this solution but its the least invasive. I also considered going wild with error recovery or moving towards a solution for clap-rs#1546.
Rust Version
rustc 1.36.0 (a53f9df32 2019-07-03)
Affected Version of clap
2.33.0
Bug or Feature Request Summary
I was hoping that I could make an argument required, and also use the
global()
function to ensure that a user of my CLI could specify my required option wherever they please.Expected Behavior Summary
An Arg with both
.global()
and.required_unless()
would be usable.Actual Behavior Summary
I see
Steps to Reproduce the issue
Create a clap App with the arg below, and attempt to run it.
Sample Code or Link to Sample Code
The text was updated successfully, but these errors were encountered: