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
* Builder Pattern
* Yaml
To build an `Arg` there are four:
* Derive Macros
* Builder Pattern
* Usage Strings
* Yaml
### 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)
1. [Using Derive Macros](#using-derive-macros)
2. [Using Builder Pattern](#using-builder-pattern)
3. [Using YAML](#using-yaml)
4. [Running it](#running-it)
3. [Running it](#running-it)
5. [Try it!](#try-it)
1. [Pre-Built Test](#pre-built-test)
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`)
- 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
* **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
- Can be required by default
- 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
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")]
#[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 {
#![allow(deprecated)]
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.
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)
}
/// Creates a new instance of [`Arg`] from a .yaml (YAML) file.
///
/// # Examples
///
/// ```ignore
/// use clap::{Arg, load_yaml};
/// let yaml = load_yaml!("arg.yaml");
/// let arg = Arg::from(yaml);
/// ```
/// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[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 {
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.

View file

@ -114,9 +114,12 @@ impl<'help> ArgGroup<'help> {
Self::new(n)
}
/// Deprecated, see [`ArgGroup::from`]
/// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[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 {
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")]
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 {
let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table");
// We WANT this to panic on error...so expect() is good.

View file

@ -77,9 +77,14 @@ impl SubCommand {
App::new(name)
}
/// TODO
/// Deprecated in Issue #9, maybe clap::Parser would fit your use case?
#[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 {
#![allow(deprecated)]
App::from_yaml(yaml)
}
}

View file

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

View file

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