Reputation: 2033
I want to have an argument that can be a number or a string. Not sure how to achieve this besides implementing FromStr
myself. It seems like a simple use-case to handle, not sure what macros to use.
Here, I want the testapp_id to be a number or a string.
I'm aiming for the usage to be like this:
cargo run -- testapps status 3
or cargo run -- testapps status app4
#[derive(Parser, Debug)]
#[clap(author,version,about)]
enum Submodules {
#[clap(subcommand)]
Testapps(TestappsCommands)
}
#[derive(Subcommand, Debug)]
enum TestappsCommands {
List,
Status {
#[clap(flatten)]
testapp_id: NumberOrString
},
}
#[derive(Debug)]
enum NumberOrString {
Number(u32),
Str(String),
}
I was thinking flatten
is what I could use for this but it's still complaining about missing implementation of FromStr
.
It works fine if I use only u32 or String type for testapp_id
, why not both?
Upvotes: 1
Views: 1089
Reputation: 2248
One common way in Rust of approach the problem it's with the use of generics. You can make your field's type to accept a generic for that field in particular.
#[derive(Subcommand, Debug)]
enum TestappsCommands<T> {
List,
Status {
#[clap(flatten)]
testapp_id: T
},
}
but this it's not enought. Because you now have a type T
instead of the NumberOrString
, which doesn't solve your problem.
I don't know if you design the NumberOrString
just for the purpose of making the testapp_id
accept both numbers and strings, but, if you don't need it, you may desire to write some bounds
.
Bounds are a way of constraint generics over a type T
, where some restrictions are applied.
Instead of using your enum, you can write this:
use std::str::FromStr;
// ... your code...
#[derive(Subcommand, Debug)]
enum TestappsCommands<T: FromStr> {
List,
Status {
#[clap(flatten)]
testapp_id: T
},
}
and now your field would only contain values of any type T
that already implements FromStr
.
Due to your requeriment of accept both numbers or strings, FromStr
it's already implemented in Rust for all the signed and unsigned variants for a "number".
You can read it better in the docs:
https://doc.rust-lang.org/std/str/trait.FromStr.html
Upvotes: 2
Reputation: 23404
Don't use flatten
for this. Instead you need to implement FromStr
for your enum, e.g.:
impl FromStr for NumberOrString {
type Err = &'static str; // The actual type doesn't matter since we never error, but it must implement `Display`
fn from_str(s: &str) -> Result<Self, Self::Err>
{
Ok (s.parse::<u32>()
.map (NumberOrString::Number)
.unwrap_or_else (|_| NumberOrString::Str (s.to_string())))
}
}
Upvotes: 2