I have this enum:
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum TheAge {
Adult,
Age(u8)
}
And the cli struct
#[derive(Parser)]
#[command(author, version, about, long_about)]
pub struct Cli {
#[arg(short, long, value_enum)]
pub age: TheAge
}
This fails with the error:
error: `#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped
When I remove the Age(u8)
from the enum, this compiles.
Any tips on how to use an enum that is not unit variants?
#[derive(ValueEnum)]
does not support non-unit variants, so you can't derive it.
And if you look at the required items it's sort of clear why:
impl ValueEnum for TheAge {
fn value_variants() -> &'a [Self] { todo!() }
fn to_possible_value(&self) -> Option<PossibleValue> { todo!() }
}
value_variants
is supposed to return
All possible argument values, in display order.
that's not really feasible when "all possible values" includes every single u8
(even if that's only 257 values total, it still makes a messy UI). There is no way to generically generate all values of a type so you can't #[derive(ValueEnum)]
, it might make sense to implement it by hand in some cases though (for example when the values are enum
s with only a few variants).
Instead you can implement From<&str>
for that struct and it will work:
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum TheAge {
Adult,
Age(u8)
}
const LEGAL_ADULT_AGE: u8 = 18;
impl From<&str> for TheAge {
fn from(v: &str) -> TheAge {
v.parse::<u8>().map_or(TheAge::Adult, |a| {
if a >= LEGAL_ADULT_AGE {
TheAge::Adult
} else {
TheAge::Age(a)
}
})
}
}
fn main() {
dbg!(Cli::parse_from(["", "--age", "18"]).age); // and above -> Adult
dbg!(Cli::parse_from(["", "--age", "17"]).age); // and below -> Age(17)
dbg!(Cli::parse_from(["", "--age", "anything_else"]).age); // -> Adult
}
@cafce25's great answer returns Adult
for an invalid age. If you want an error instead, impl FromStr
rather than From<&str>
:
use clap::Parser;
use std::str::FromStr;
use anyhow::Context;
#[derive(Parser)]
#[command(author, version, about, long_about)]
pub struct Cli {
#[arg(short, long)]
pub age: TheAge
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum TheAge {
Adult,
Age(u8)
}
const LEGAL_ADULT_AGE: u8 = 18;
impl FromStr for TheAge {
type Err = anyhow::Error;
fn from_str(v: &str) -> Result<TheAge, Self::Err> {
v.parse::<u8>().map(|a| {
if a >= LEGAL_ADULT_AGE {
TheAge::Adult
} else {
TheAge::Age(a)
}
}).context("invalid age")
}
}
fn main() {
dbg!(Cli::parse_from(["", "--age", "18"]).age); // and above -> Adult
dbg!(Cli::parse_from(["", "--age", "17"]).age); // and below -> Age(17)
dbg!(Cli::parse_from(["", "--age", "anything_else"]).age); // -> Error
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With