clap icon indicating copy to clipboard operation
clap copied to clipboard

Setting flag argument's env var to `flase` still flagged as conflicting

Open elichai opened this issue 1 year ago • 2 comments

Please complete the following tasks

Rust Version

1.79.0

Clap Version

4.5.9

Minimal reproducible code

use clap::{arg, value_parser, ArgGroup, Command};

fn main() {
    std::env::set_var("MEMORY_STORAGE", "false");
    std::env::set_var(
        "POSTGRES_CONNECTION_STRING",
        "postgres://user:password@localhost:5432/db",
    );

    let matches = Command::new("sample")
        .group(
            ArgGroup::new("storage")
                .args(["memory-storage", "postgres"])
                .multiple(false)
                .required(true),
        )
        .arg(
            arg!(--"memory-storage" "Use an in-memory storage backend")
                .value_parser(value_parser!(bool))
                .env("MEMORY_STORAGE"),
        )
        .arg(
            arg!(-p --postgres <CONNECTION_STRING> "A postgreSQL connection string")
                .env("POSTGRES_CONNECTION_STRING"),
        )
        .get_matches();

    println!("{matches:?}");
}

Steps to reproduce the bug with the above code

cargo add clap --features env

Actual Behaviour

error: the argument '--memory-storage' cannot be used with '--postgres <CONNECTION_STRING>'

Usage: sample <--memory-storage|--postgres <CONNECTION_STRING>>

For more information, try '--help'.

Expected Behaviour

When I set a flag's env variable to false I expect it to not be flagged as conflicting and that it will properly use the postgres connection string

Additional Context

No response

Debug Output

[clap_builder::builder::command]Command::_do_parse
[clap_builder::builder::command]Command::_build: name="sample"
[clap_builder::builder::command]Command::_propagate:sample
[clap_builder::builder::command]Command::_check_help_and_version:sample expand_help_tree=false
[clap_builder::builder::command]Command::long_help_exists
[clap_builder::builder::command]Command::_check_help_and_version: Building default --help
[clap_builder::builder::command]Command::_propagate_global_args:sample
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:memory-storage
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:postgres
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:help
[clap_builder::builder::debug_asserts]Command::_verify_positionals
[clap_builder::parser::parser]Parser::get_matches_with
[clap_builder::parser::parser]Parser::add_env
[clap_builder::parser::parser]Parser::add_env: Checking arg `--memory-storage`
[clap_builder::parser::parser]Parser::add_env: Found an opt with value="false"
[clap_builder::parser::parser]Parser::react action=SetTrue, identifier=None, source=EnvVariable
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="memory-storage", source=EnvVariable
[clap_builder::builder::command]Command::groups_for_arg: id="memory-storage"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="storage", source=EnvVariable
[clap_builder::parser::parser]Parser::push_arg_values: ["false"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=1
[clap_builder::parser::parser]Parser::add_env: Checking arg `--postgres <CONNECTION_STRING>`
[clap_builder::parser::parser]Parser::add_env: Found an opt with value="postgres://user:password@localhost:5432/db"
[clap_builder::parser::parser]Parser::react action=Set, identifier=None, source=EnvVariable
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="postgres", source=EnvVariable
[clap_builder::builder::command]Command::groups_for_arg: id="postgres"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="storage", source=EnvVariable
[clap_builder::parser::parser]Parser::push_arg_values: ["postgres://user:password@localhost:5432/db"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=2
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=postgres, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::add_env: Checking arg `--help`
[clap_builder::parser::parser]Parser::add_defaults
[clap_builder::parser::parser]Parser::add_defaults:iter:memory-storage:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:memory-storage: has default vals
[clap_builder::parser::parser]Parser::add_default_value:iter:memory-storage: was used
[clap_builder::parser::parser]Parser::add_defaults:iter:postgres:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:postgres: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:help:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:help: doesn't have default vals
[clap_builder::parser::validator]Validator::validate
[clap_builder::builder::command]Command::groups_for_arg: id="memory-storage"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="memory-storage", conflicts=["postgres"]
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="storage", conflicts=[]
[clap_builder::builder::command]Command::groups_for_arg: id="postgres"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="postgres", conflicts=["memory-storage"]
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_exclusive:iter:"memory-storage"
[clap_builder::parser::validator]Validator::validate_exclusive:iter:"storage"
[clap_builder::parser::validator]Validator::validate_exclusive:iter:"postgres"
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="memory-storage"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="memory-storage"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=["postgres", "postgres"]
[clap_builder::parser::validator]Validator::build_conflict_err: name="memory-storage"
[ clap_builder::output::usage]Usage::create_usage_with_title
[ clap_builder::output::usage]Usage::create_usage_no_title
[ clap_builder::output::usage]Usage::create_smart_usage
[ clap_builder::output::usage]Usage::write_arg_usage; incl_reqs=true
[ clap_builder::output::usage]Usage::write_args: incls=["memory-storage"]
[ clap_builder::output::usage]Usage::get_args: unrolled_reqs=["storage"]
[clap_builder::builder::command]Command::unroll_args_in_group: group="storage"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: entity="memory-storage"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: this is an arg
[clap_builder::builder::command]Command::unroll_args_in_group:iter: entity="postgres"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: this is an arg
[clap_builder::builder::command]Command::unroll_args_in_group: group="storage"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: entity="memory-storage"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: this is an arg
[clap_builder::builder::command]Command::unroll_args_in_group:iter: entity="postgres"
[clap_builder::builder::command]Command::unroll_args_in_group:iter: this is an arg
[ clap_builder::output::usage]Usage::create_usage_with_title: usage=Usage: wat <--memory-storage|--postgres <CONNECTION_STRING>>
[clap_builder::builder::command]Command::color: Color setting...
[clap_builder::builder::command]Auto
[clap_builder::builder::command]Command::color: Color setting...
[clap_builder::builder::command]Auto

elichai avatar Jul 21 '24 15:07 elichai

We have a similar problem in uv, where boolean env vars cannot be reset, causing conflicts (https://github.com/astral-sh/uv/issues/13385). This affects a number of arguments in uv that have both a default value and conflict with another argument.

use clap::Parser;

#[derive(Debug, Parser)]
pub struct Args {
    /// Don't resolve.
    #[arg(long, env = "FROZEN", value_parser = clap::builder::BoolishValueParser::new(), conflicts_with_all = ["check"])]
    frozen: bool,

    /// Check that the resolution is valid.
    #[arg(long, env = "CHECK", value_parser = clap::builder::BoolishValueParser::new())]
    check: bool,
}

fn main() {
    let args = Args::parse();
    println!("Args: {args:?}");
}
$ CHECK=0 mini-uv --frozen
error: the argument '--frozen' cannot be used with '--check'

Usage: clap-conflicts --frozen

For more information, try '--help'.

konstin avatar May 13 '25 07:05 konstin

Is there perhaps a high level suggested change? I could try to take a look over the weekend?

NellyWhads avatar May 14 '25 22:05 NellyWhads