Arg that is either an ValueEnum variant or another type (like PathBuf) #6452
|
I want to have a command line argument that can either take a variant of a ValueEnum or a PathBuf. (However this question is also applicable to other types like Numbers.) My initial idea was just to implement Then, to get a list of possible values in the help output, I implemented a Minimal example: use std::path::PathBuf;
use clap::{
Arg, Command, CommandFactory, Parser, ValueEnum,
builder::{PossibleValue, TypedValueParser},
};
#[derive(Clone, ValueEnum, Debug)]
enum Values {
Foo,
Bar,
FooBar,
}
#[derive(Clone, Debug)]
enum ArgType {
#[allow(unused)]
Enum(Values),
#[allow(unused)]
Path(PathBuf),
}
#[derive(Clone)]
struct ArgTypeValueParser;
impl TypedValueParser for ArgTypeValueParser {
type Value = ArgType;
fn parse_ref(
&self,
_: &Command,
_: Option<&Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
if let Some(s) = value.to_str()
&& let Ok(v) = Values::from_str(s, true)
{
Ok(ArgType::Enum(v))
} else {
Ok(ArgType::Path(PathBuf::from(value)))
}
}
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
Some(Box::new(
Values::value_variants()
.iter()
.filter_map(Values::to_possible_value),
))
}
}
#[derive(Parser)]
#[command()]
struct Cli {
#[arg(value_parser = ArgTypeValueParser)]
arg: ArgType,
}
fn main() {
clap_complete::CompleteEnv::with_factory(Cli::command).complete();
println!("{:#?}", Cli::parse().arg);
} |
Replies: 2 comments 2 replies
|
Yes, we made the assumption that value hints was mutually exclusive to possible values. Overall, the current completion system is in maintenance mode and is expected to be deprecated when the new completion system is stabilized. We may even move away from |
|
on this new day with restored vigor, i found something that does the trick fn completer(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
let mut completions: Vec<CompletionCandidate> = Values::value_variants()
.iter()
.filter_map(Values::to_possible_value)
.map(|x| CompletionCandidate::new(x.get_name()))
.collect();
completions.extend(PathCompleter::file().complete(current));
completions
}with full exampleuse std::path::PathBuf;
use clap::{
Arg, Command, CommandFactory, Parser, ValueEnum,
builder::{PossibleValue, TypedValueParser},
};
use clap_complete::{
ArgValueCompleter, CompletionCandidate, PathCompleter, engine::ValueCompleter,
};
#[derive(Clone, ValueEnum, Debug)]
enum Values {
/// foo
Foo,
/// bar
Bar,
/// foo and bar combined
FooBar,
}
#[derive(Clone, Debug)]
enum ArgType {
#[allow(unused)]
Enum(Values),
#[allow(unused)]
Path(PathBuf),
}
#[derive(Clone)]
struct ArgTypeValueParser;
impl TypedValueParser for ArgTypeValueParser {
type Value = ArgType;
fn parse_ref(
&self,
_: &Command,
_: Option<&Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
if let Some(s) = value.to_str()
&& let Ok(v) = Values::from_str(s, true)
{
Ok(ArgType::Enum(v))
} else {
Ok(ArgType::Path(PathBuf::from(value)))
}
}
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
let mut vec: Vec<PossibleValue> = Values::value_variants()
.iter()
.filter_map(Values::to_possible_value)
.collect();
vec.push(PossibleValue::new("<path>").help("any path"));
Some(Box::new(vec.into_iter()))
}
}
fn completer(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
let mut completions: Vec<CompletionCandidate> = Values::value_variants()
.iter()
.filter_map(Values::to_possible_value)
.map(|x| CompletionCandidate::new(x.get_name()))
.collect();
completions.extend(PathCompleter::file().complete(current));
completions
}
#[derive(Parser)]
#[command()]
struct Cli {
#[arg(value_parser = ArgTypeValueParser, add = ArgValueCompleter::new(completer))]
arg: ArgType,
}
fn main() {
clap_complete::CompleteEnv::with_factory(Cli::command).complete();
println!("{:#?}", Cli::parse().arg);
}with [package]
name = "hello"
version = "0.1.0"
edition = "2024"
[dependencies]
clap = { version = "4.6.4", features = ["derive"] }
clap_complete = { version = "4.6.7", features = ["unstable-dynamic"] }maybe there is a way to make this kind of combination of types easier |
on this new day with restored vigor, i found something that does the trick
with
add = ArgValueCompleter::new(completer)full example