feat(generate): 'impl ArgEnum for Shell'

These keeps `FromStr` for ease of use with `value_of_t`.

This includes adding test to make sure everything works as expected.
This commit is contained in:
Ed Page 2021-08-18 13:00:56 -05:00
parent 562e64c723
commit 35d53d9dcf
4 changed files with 56 additions and 49 deletions

View file

@ -12,23 +12,13 @@
//! . ./value_hints_derive.fish
//! ./target/debug/examples/value_hints_derive --<TAB>
//! ```
use clap::{App, AppSettings, ArgEnum, IntoApp, Parser, ValueHint};
use clap::{App, AppSettings, IntoApp, Parser, ValueHint};
use clap_generate::generators::{Bash, Elvish, Fish, PowerShell, Zsh};
use clap_generate::{generate, Generator};
use clap_generate::{generate, Generator, Shell};
use std::ffi::OsString;
use std::io;
use std::path::PathBuf;
#[derive(ArgEnum, Debug, PartialEq, Clone)]
enum GeneratorChoice {
Bash,
Elvish,
Fish,
#[clap(name = "powershell")]
PowerShell,
Zsh,
}
#[derive(Parser, Debug, PartialEq)]
#[clap(
name = "value_hints_derive",
@ -38,7 +28,7 @@ enum GeneratorChoice {
struct Opt {
/// If provided, outputs the completion file for given shell
#[clap(long = "generate", arg_enum)]
generator: Option<GeneratorChoice>,
generator: Option<Shell>,
// Showcasing all possible ValueHints:
#[clap(long, value_hint = ValueHint::Unknown)]
unknown: Option<String>,
@ -79,11 +69,12 @@ fn main() {
let mut app = Opt::into_app();
eprintln!("Generating completion file for {:?}...", generator);
match generator {
GeneratorChoice::Bash => print_completions::<Bash>(&mut app),
GeneratorChoice::Elvish => print_completions::<Elvish>(&mut app),
GeneratorChoice::Fish => print_completions::<Fish>(&mut app),
GeneratorChoice::PowerShell => print_completions::<PowerShell>(&mut app),
GeneratorChoice::Zsh => print_completions::<Zsh>(&mut app),
Shell::Bash => print_completions::<Bash>(&mut app),
Shell::Elvish => print_completions::<Elvish>(&mut app),
Shell::Fish => print_completions::<Fish>(&mut app),
Shell::PowerShell => print_completions::<PowerShell>(&mut app),
Shell::Zsh => print_completions::<Zsh>(&mut app),
_ => unimplemented!("New shell type"),
}
} else {
println!("{:#?}", opt);

View file

@ -1,6 +1,8 @@
use std::fmt::Display;
use std::str::FromStr;
use clap::{ArgEnum, ArgValue};
/// Shell with auto-generated completion script available.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
@ -17,22 +19,12 @@ pub enum Shell {
Zsh,
}
impl Shell {
/// A list of supported shells in `[&'static str]` form.
pub fn variants() -> [&'static str; 5] {
["bash", "elvish", "fish", "powershell", "zsh"]
}
}
impl Display for Shell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Shell::Bash => write!(f, "bash"),
Shell::Elvish => write!(f, "elvish"),
Shell::Fish => write!(f, "fish"),
Shell::PowerShell => write!(f, "powershell"),
Shell::Zsh => write!(f, "zsh"),
}
self.to_arg_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
@ -40,15 +32,35 @@ impl FromStr for Shell {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"bash" => Ok(Shell::Bash),
"elvish" => Ok(Shell::Elvish),
"fish" => Ok(Shell::Fish),
"powershell" => Ok(Shell::PowerShell),
"zsh" => Ok(Shell::Zsh),
_ => Err(String::from(
"[valid values: bash, elvish, fish, powershell, zsh]",
)),
for variant in Self::value_variants() {
if variant.to_arg_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}
// Hand-rolled so it can work even when `derive` feature is disabled
impl ArgEnum for Shell {
fn value_variants<'a>() -> &'a [Self] {
&[
Shell::Bash,
Shell::Elvish,
Shell::Fish,
Shell::PowerShell,
Shell::Zsh,
]
}
fn to_arg_value<'a>(&self) -> Option<ArgValue<'a>> {
let value = match self {
Shell::Bash => ArgValue::new("bash"),
Shell::Elvish => ArgValue::new("elvish"),
Shell::Fish => ArgValue::new("fish"),
Shell::PowerShell => ArgValue::new("powershell"),
Shell::Zsh => ArgValue::new("zsh"),
};
Some(value)
}
}

View file

@ -25,7 +25,8 @@ use std::ffi::OsString;
/// throughout the application representing the normalized values coming from
/// the CLI.
///
/// ```rust
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// /// My super CLI
/// #[derive(clap::Parser)]
/// #[clap(name = "demo")]
@ -150,7 +151,8 @@ pub trait FromArgMatches: Sized {
/// Motivation: If our application had two CLI options, `--name
/// <STRING>` and the flag `--debug`, we may create a struct as follows:
///
/// ```no_run
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```no_run")]
/// struct Context {
/// name: String,
/// debug: bool
@ -160,7 +162,8 @@ pub trait FromArgMatches: Sized {
/// We then need to convert the `ArgMatches` that `clap` generated into our struct.
/// `from_arg_matches` serves as the equivalent of:
///
/// ```no_run
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```no_run")]
/// # use clap::ArgMatches;
/// # struct Context {
/// # name: String,
@ -192,7 +195,8 @@ pub trait FromArgMatches: Sized {
///
/// # Example
///
/// ```rust
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
/// #[clap(flatten)]
@ -229,7 +233,8 @@ pub trait Args: FromArgMatches + Sized {
///
/// # Example
///
/// ```rust
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
/// #[clap(subcommand)]
@ -265,7 +270,8 @@ pub trait Subcommand: FromArgMatches + Sized {
///
/// # Example
///
/// ```rust
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
/// #[clap(arg_enum)]

View file

@ -31,7 +31,6 @@ pub use crate::{
parse::{ArgMatches, Indices, OsValues, Values},
};
#[cfg(feature = "derive")]
pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcommand};
#[cfg(feature = "yaml")]
@ -50,7 +49,6 @@ pub use lazy_static;
#[allow(missing_docs)]
mod macros;
#[cfg(feature = "derive")]
mod derive;
#[cfg(feature = "regex")]