fix: Deprecate YAML API

Fixes #9
This commit is contained in:
Ed Page 2021-11-18 14:27:21 -06:00
parent 88fff13e71
commit 6ce9714e2b
10 changed files with 27 additions and 241 deletions

2
FAQ.md
View file

@ -73,14 +73,12 @@ To build an `App` there are three:
* Derive Macros * Derive Macros
* Builder Pattern * Builder Pattern
* Yaml
To build an `Arg` there are four: To build an `Arg` there are four:
* Derive Macros * Derive Macros
* Builder Pattern * Builder Pattern
* Usage Strings * Usage Strings
* Yaml
### Why is there a default subcommand of help? ### Why is there a default subcommand of help?

View file

@ -27,8 +27,7 @@ We are currently hard at work trying to release `3.0`. We have a `3.0.0-beta.5`
4. [Quick Example](#quick-example) 4. [Quick Example](#quick-example)
1. [Using Derive Macros](#using-derive-macros) 1. [Using Derive Macros](#using-derive-macros)
2. [Using Builder Pattern](#using-builder-pattern) 2. [Using Builder Pattern](#using-builder-pattern)
3. [Using YAML](#using-yaml) 3. [Running it](#running-it)
4. [Running it](#running-it)
5. [Try it!](#try-it) 5. [Try it!](#try-it)
1. [Pre-Built Test](#pre-built-test) 1. [Pre-Built Test](#pre-built-test)
2. [Build Your Own Binary](#build-your-own-binary) 2. [Build Your Own Binary](#build-your-own-binary)
@ -92,7 +91,6 @@ Below are a few of the features which `clap` supports, full descriptions and usa
* **Sub-Commands** (i.e. `git add <file>` where `add` is a sub-command of `git`) * **Sub-Commands** (i.e. `git add <file>` where `add` is a sub-command of `git`)
- Support their own sub-arguments, and sub-sub-commands independent of the parent - Support their own sub-arguments, and sub-sub-commands independent of the parent
- Get their own auto-generated Help, Version, and Usage independent of parent - Get their own auto-generated Help, Version, and Usage independent of parent
* **Support for building CLIs from YAML** - This keeps your Rust source nice and tidy and makes supporting localized translation very simple!
* **Requirement Rules**: Arguments can define the following types of requirement rules * **Requirement Rules**: Arguments can define the following types of requirement rules
- Can be required by default - Can be required by default
- Can be required only if certain arguments are present - Can be required only if certain arguments are present
@ -304,72 +302,6 @@ fn main() {
} }
``` ```
#### Using YAML
This third method shows how you can use a YAML file to build your CLI and keep your Rust source tidy
or support multiple localized translations by having different YAML files for each localization.
First, create the `cli.yaml` file to hold your CLI options, but it could be called anything we like:
```yaml
name: myapp
version: "1.0"
author: Kevin K. <kbknapp@gmail.com>
about: Does awesome things
args:
- config:
short: c
long: config
value_name: FILE
help: Sets a custom config file
takes_value: true
- INPUT:
help: Sets the input file to use
required: true
index: 1
- verbose:
short: v
multiple_occurrences: true
help: Sets the level of verbosity
subcommands:
- test:
about: controls testing features
version: "1.3"
author: Someone E. <someone_else@other.com>
args:
- debug:
short: d
long: debug
help: Print debug information
```
Since this feature requires additional dependencies that not everyone may want, it is *not* compiled in by default and we need to enable a feature flag in Cargo.toml:
Simply add the `yaml` feature flag to your `Cargo.toml`.
```toml
[dependencies]
clap = { version = "3.0.0-beta.5", features = ["yaml"] }
```
Finally we create our `main.rs` file just like we would have with the previous two examples:
```rust,ignore
// (Full example with detailed comments in examples/17_yaml.rs)
//
// This example demonstrates clap's building from YAML style of creating arguments which is far
// more clean, but takes a very small performance hit compared to the other two methods.
use clap::{App, load_yaml};
fn main() {
// The YAML file is found relative to the current file, similar to how modules are found
let yaml = load_yaml!("cli.yaml");
let matches = App::from(yaml).get_matches();
// Same as previous examples...
}
```
#### Running it #### Running it
If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output (except the first example where the help message sort of explains the Rust code). If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output (except the first example where the help message sort of explains the Rust code).

View file

@ -1,50 +0,0 @@
// In order to use YAML to define your CLI you must compile clap with the "yaml" feature because
// it's **not** included by default.
//
// In order to do this, ensure your Cargo.toml looks like one of the following:
//
// [dependencies.clap]
// features = ["yaml"]
//
// __OR__
//
// [dependencies]
// clap = { features = ["yaml"] }
// Using yaml requires calling a clap macro `load_yaml!()`.
// Note: If you're using clap as a dependency and don't have a feature for your users called
// "yaml", you'll need to remove the #[cfg(feature = "yaml")] conditional compilation attribute
#[cfg(feature = "yaml")]
fn main() {
use clap::{load_yaml, App};
// To load a yaml file containing our CLI definition such as the example '17_yaml.yaml' we can
// use the convenience macro which loads the file at compile relative to the current file
// similar to how modules are found.
//
// Then we pass that yaml object to App to build the CLI.
//
// Finally we call get_matches() to start the parsing process. We use the matches just as we
// normally would
let yaml = load_yaml!("17_yaml.yaml");
let m = App::from_yaml(yaml).get_matches();
// Because the example 17_yaml.yaml is rather large we'll just look a single arg so you can
// see that it works...
if let Some(mode) = m.value_of("mode") {
match mode {
"vi" => println!("You are using vi"),
"emacs" => println!("You are using emacs..."),
_ => unreachable!(),
}
} else {
println!("--mode <MODE> wasn't used...");
}
}
#[cfg(not(feature = "yaml"))]
fn main() {
// As stated above, if clap is not compiled with the YAML feature, it is disabled.
println!("YAML feature is disabled.");
println!("Pass --features yaml to cargo when trying this example.");
}

View file

@ -1,98 +0,0 @@
name: yaml_app
version: "1.0"
about: an example using a .yaml file to build a CLI
author: Kevin K. <kbknapp@gmail.com>
# AppSettings can be defined as a list and are **not** ascii case sensitive
settings:
- ArgRequiredElseHelp
# All Args must be defined in the 'args:' list where the name of the arg, is the
# key to a Hash object
args:
# The name of this argument, is 'opt' which will be used to access the value
# later in your Rust code
- opt:
help: example option argument from yaml
short: o
long: option
multiple_occurrences: true
takes_value: true
- pos:
help: example positional argument from yaml
index: 1
# A list of possible values can be defined as a list
possible_values:
- fast
- slow
- flag:
help: demo flag argument
short: F
multiple_occurrences: true
takes_value: true
global: true
# Conflicts, mutual overrides, and requirements can all be defined as a
# list, where the key is the name of the other argument
conflicts_with:
- opt
requires:
- pos
- mode:
long: mode
help: shows an option with specific values
# possible_values can also be defined in this list format
possible_values: [ vi, emacs ]
takes_value: true
- mvals:
long: mult-vals
help: demos an option which has two named values
# value names can be described in a list, where the help will be shown
# --mult-vals <one> <two>
value_names:
- one
- two
- minvals:
long: min-vals
multiple_values: true
help: you must supply at least two values to satisfy me
min_values: 2
- maxvals:
long: max-vals
multiple_values: true
help: you can only supply a max of 3 values for me!
max_values: 3
# All subcommands must be listed in the 'subcommand:' object, where the key to
# the list is the name of the subcommand, and all settings for that command are
# are part of a Hash object
subcommands:
# The name of this subcommand will be 'subcmd' which can be accessed in your
# Rust code later
- subcmd:
about: demos subcommands from yaml
version: "0.1"
author: Kevin K. <kbknapp@gmail.com>
# Subcommand args are exactly like App args
args:
- scopt:
short: B
multiple_occurrences: true
help: example subcommand option
takes_value: true
- scpos1:
help: example subcommand positional
index: 1
# ArgGroups are supported as well, and must be specified in the 'groups:'
# object of this file
groups:
# the name of the ArgGoup is specified here
- min-max-vals:
# All args and groups that are a part of this group are set here
args:
- minvals
- maxvals
# setting conflicts is done the same manner as setting 'args:'
#
# to make this group required, you could set 'required: true' but for
# this example we won't do that.

View file

@ -398,9 +398,14 @@ impl<'help> App<'help> {
) )
} }
/// TODO /// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
#[deprecated(
since = "3.0.0",
note = "Deprecated in Issue #9, maybe clap::Parser would fit your use case?"
)]
pub fn from_yaml(y: &'help Yaml) -> Self { pub fn from_yaml(y: &'help Yaml) -> Self {
#![allow(deprecated)]
let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); let yaml_file_hash = y.as_hash().expect("YAML file must be a hash");
// We WANT this to panic on error...so expect() is good. // We WANT this to panic on error...so expect() is good.
let (mut a, yaml, err) = if let Some(name) = y["name"].as_str() { let (mut a, yaml, err) = if let Some(name) = y["name"].as_str() {

View file

@ -349,16 +349,12 @@ impl<'help> Arg<'help> {
Self::new(n) Self::new(n)
} }
/// Creates a new instance of [`Arg`] from a .yaml (YAML) file. /// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
///
/// # Examples
///
/// ```ignore
/// use clap::{Arg, load_yaml};
/// let yaml = load_yaml!("arg.yaml");
/// let arg = Arg::from(yaml);
/// ```
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
#[deprecated(
since = "3.0.0",
note = "Deprecated in Issue #9, maybe clap::Parser would fit your use case?"
)]
pub fn from_yaml(y: &'help Yaml) -> Self { pub fn from_yaml(y: &'help Yaml) -> Self {
let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); let yaml_file_hash = y.as_hash().expect("YAML file must be a hash");
// We WANT this to panic on error...so expect() is good. // We WANT this to panic on error...so expect() is good.

View file

@ -114,9 +114,12 @@ impl<'help> ArgGroup<'help> {
Self::new(n) Self::new(n)
} }
/// Deprecated, see [`ArgGroup::from`] /// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
#[deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::from`")] #[deprecated(
since = "3.0.0",
note = "Deprecated in Issue #9, maybe clap::Parser would fit your use case?"
)]
pub fn from_yaml(yaml: &'help Yaml) -> Self { pub fn from_yaml(yaml: &'help Yaml) -> Self {
Self::from(yaml) Self::from(yaml)
} }
@ -436,9 +439,10 @@ impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> {
} }
} }
/// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
impl<'help> From<&'help Yaml> for ArgGroup<'help> { impl<'help> From<&'help Yaml> for ArgGroup<'help> {
/// TODO /// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
fn from(y: &'help Yaml) -> Self { fn from(y: &'help Yaml) -> Self {
let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table"); let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table");
// We WANT this to panic on error...so expect() is good. // We WANT this to panic on error...so expect() is good.

View file

@ -77,9 +77,14 @@ impl SubCommand {
App::new(name) App::new(name)
} }
/// TODO /// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
#[deprecated(
since = "3.0.0",
note = "Deprecated in Issue #9, maybe clap::Parser would fit your use case?"
)]
pub fn from_yaml(yaml: &yaml_rust::Yaml) -> App { pub fn from_yaml(yaml: &yaml_rust::Yaml) -> App {
#![allow(deprecated)]
App::from_yaml(yaml) App::from_yaml(yaml)
} }
} }

View file

@ -5,14 +5,7 @@ use std::fs;
use std::process::{Command, Output}; use std::process::{Command, Output};
fn run_example<S: AsRef<str>>(name: S, args: &[&str]) -> Output { fn run_example<S: AsRef<str>>(name: S, args: &[&str]) -> Output {
let mut all_args = vec![ let mut all_args = vec!["run", "--example", name.as_ref(), "--"];
"run",
"--example",
name.as_ref(),
"--features",
"yaml",
"--",
];
all_args.extend_from_slice(args); all_args.extend_from_slice(args);
Command::new(env!("CARGO")) Command::new(env!("CARGO"))

View file

@ -1,4 +1,5 @@
#![cfg(feature = "yaml")] #![cfg(feature = "yaml")]
#![allow(deprecated)]
use clap::{load_yaml, App, Arg, ErrorKind, ValueHint}; use clap::{load_yaml, App, Arg, ErrorKind, ValueHint};