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

TypedValueParser cannot return single Vec<String> #4808

Closed
2 tasks done
LegionMammal978 opened this issue Mar 29, 2023 · 4 comments
Closed
2 tasks done

TypedValueParser cannot return single Vec<String> #4808

LegionMammal978 opened this issue Mar 29, 2023 · 4 comments
Labels
C-bug Category: Updating dependencies

Comments

@LegionMammal978
Copy link

Please complete the following tasks

Rust Version

rustc 1.68.2 (9eb3afe9e 2023-03-27)

Clap Version

4.2.0

Minimal reproducible code

use clap::{builder::ValueParser, ArgAction, Parser};
use std::convert::Infallible;

fn my_parser(arg: &str) -> Result<Vec<String>, Infallible> {
    Ok(arg.split(',').map(str::to_string).collect())
}

#[derive(Parser)]
struct Args {
    #[arg(
        long = "values",
        value_parser = ValueParser::new(my_parser),
        action = ArgAction::Set,
        required = true,
    )]
    values: Vec<String>,
}

fn main() {
    let args = Args::parse_from(["example", "--values=a,b,c"]);
    println!("{:?}", args.values);
}

Steps to reproduce the bug with the above code

cargo run

Actual Behaviour

thread 'main' panicked at 'Mismatch between definition and access of `values`. Could not downcast to alloc::string::String, need to downcast to alloc::vec::Vec<alloc::string::String>
', src/main.rs:16:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Expected Behaviour

["a", "b", "c"]

Additional Context

According to the Derive Reference:

clap assumes some intent based on the type used:

Type Effect Implies
Vec<T> 0.. occurrences of argument .action(ArgAction::Append).required(false)

In addition, .value_parser(value_parser!(T)) is called for each field.

Notes:

  • For custom type behavior, you can override the implied attributes/settings and/or set additional ones

This suggests that if I override action and required, then my TypedValueParser can return a Vec<T> just like any other T. However, this always produces an error, since clap_derive unconditionally uses get_many for Vec<T> fields, regardless of what attributes I set. So far, I've been able to work around it by having my TypedValueParser return a Box<[T]>, but this is less than ideal.

Debug Output

[clap_builder::builder::command] 	Command::_do_parse
[clap_builder::builder::command] 	Command::_build: name="example"
[clap_builder::builder::command] 	Command::_propagate:example
[clap_builder::builder::command] 	Command::_check_help_and_version:example 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:example
[clap_builder::builder::debug_asserts] 	Command::_debug_asserts
[clap_builder::builder::debug_asserts] 	Arg::_debug_asserts:values
[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::get_matches_with: Begin parsing '"--values=a,b,c"'
[clap_builder::parser::parser] 	Parser::possible_subcommand: arg=Ok("--values=a,b,c")
[clap_builder::parser::parser] 	Parser::get_matches_with: sc=None
[clap_builder::parser::parser] 	Parser::parse_long_arg
[clap_builder::parser::parser] 	Parser::parse_long_arg: Does it contain '='...
[clap_builder::parser::parser] 	Parser::parse_long_arg: Found valid arg or flag '--values <VALUES>'
[clap_builder::parser::parser] 	Parser::parse_long_arg("values"): Found an arg with value 'Some("a,b,c")'
[clap_builder::parser::parser] 	Parser::parse_opt_value; arg=values, val=Some("a,b,c"), has_eq=true
[clap_builder::parser::parser] 	Parser::parse_opt_value; arg.settings=ArgFlags(REQUIRED)
[clap_builder::parser::parser] 	Parser::parse_opt_value; Checking for val...
[clap_builder::parser::parser] 	Parser::react action=Set, identifier=Some(Long), source=CommandLine
[clap_builder::parser::parser] 	Parser::react: cur_idx:=1
[clap_builder::parser::parser] 	Parser::remove_overrides: id="values"
[clap_builder::parser::arg_matcher] 	ArgMatcher::start_custom_arg: id="values", source=CommandLine
[clap_builder::builder::command] 	Command::groups_for_arg: id="values"
[clap_builder::parser::arg_matcher] 	ArgMatcher::start_custom_arg: id="Args", source=CommandLine
[clap_builder::parser::parser] 	Parser::push_arg_values: ["a,b,c"]
[clap_builder::parser::parser] 	Parser::add_single_val_to_arg: cur_idx:=2
[clap_builder::parser::arg_matcher] 	ArgMatcher::needs_more_vals: o=values, 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::get_matches_with: After parse_long_arg ValuesDone
[clap_builder::parser::parser] 	Parser::add_defaults
[clap_builder::parser::parser] 	Parser::add_defaults:iter:values:
[clap_builder::parser::parser] 	Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser] 	Parser::add_default_value:iter:values: 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="values"
[clap_builder::parser::validator] 	Conflicts::gather_direct_conflicts id="values", conflicts=[]
[clap_builder::parser::validator] 	Conflicts::gather_direct_conflicts id="Args", conflicts=[]
[clap_builder::parser::validator] 	Validator::validate_conflicts
[clap_builder::parser::validator] 	Validator::validate_exclusive
[clap_builder::parser::validator] 	Validator::validate_conflicts::iter: id="values"
[clap_builder::parser::validator] 	Conflicts::gather_conflicts: arg="values"
[clap_builder::parser::validator] 	Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator] 	Validator::validate_required: required=ChildGraph([Child { id: "values", children: [] }])
[clap_builder::parser::validator] 	Validator::gather_requires
[clap_builder::parser::validator] 	Validator::gather_requires:iter:"values"
[clap_builder::parser::validator] 	Validator::gather_requires:iter:"Args"
[clap_builder::parser::validator] 	Validator::gather_requires:iter:"Args":group
[clap_builder::parser::validator] 	Validator::validate_required: is_exclusive_present=false
[clap_builder::parser::arg_matcher] 	ArgMatcher::get_global_values: global_arg_vec=[]
thread 'main' panicked at 'Mismatch between definition and access of `values`. Could not downcast to alloc::string::String, need to downcast to alloc::vec::Vec<alloc::string::String>
', src/main.rs:16:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
@LegionMammal978 LegionMammal978 added the C-bug Category: Updating dependencies label Mar 29, 2023
@epage
Copy link
Member

epage commented Mar 29, 2023

This suggests that if I override action and required, then my TypedValueParser can return a Vec just like any other T. However, this always produces an error, since clap_derive unconditionally uses get_many for Vec fields, regardless of what attributes I set. So far, I've been able to work around it by having my TypedValueParser return a Box<[T]>, but this is less than ideal.

The problem is we do not know what accessor to use, so we do a hard coded lookup based on the type's name. You can work around this by referring to the type by something other than Vec, like std::vec::Vec. We have #4626 for exploring how to improve this.

Closing in favor of #4626

@epage epage closed this as not planned Won't fix, can't repro, duplicate, stale Mar 29, 2023
@LegionMammal978
Copy link
Author

I wonder if it might be helpful to document this behavior in the meantime, since it took me a fair while to track down the underlying cause of the issue.

epage added a commit that referenced this issue Mar 29, 2023
@epage
Copy link
Member

epage commented Mar 29, 2023

Huh, I thought we had that somewhere but not seeing it, so added it now.

@LegionMammal978
Copy link
Author

There seems to be a typo ("completel").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Updating dependencies
Projects
None yet
Development

No branches or pull requests

2 participants