Skip to content

Commit

Permalink
Auto merge of #554 - kbknapp:issue-376, r=kbknapp
Browse files Browse the repository at this point in the history
Issue 376
  • Loading branch information
homu committed Jul 1, 2016
2 parents 3a000d6 + 7daee9d commit 70fa5f7
Show file tree
Hide file tree
Showing 7 changed files with 427 additions and 40 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
<a name="v2.9.0"></a>
## v2.9.0 (2016-07-01)


#### Documentation

* **Completions:** adds documentation for completion scripts ([c6c519e4](https://github.com/kbknapp/clap-rs/commit/c6c519e40efd6c4533a9ef5efe8e74fd150391b7))

#### Features

* **Completions:** one can now generate a bash completions script at compile time! ([e75b6c7b](https://github.com/kbknapp/clap-rs/commit/e75b6c7b75f729afb9eb1d2a2faf61dca7674634), closes [#376](https://github.com/kbknapp/clap-rs/issues/376))


<a name="v2.8.0"></a>
## v2.8.0 (2016-06-30)

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "clap"
version = "2.8.0"
version = "2.9.0"
authors = ["Kevin K. <[email protected]>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
Expand Down
40 changes: 8 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# clap
clap
====

[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kbknapp/clap-rs/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/kbknapp/clap-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/kbknapp/clap-rs?branch=master) [![Join the chat at https://gitter.im/kbknapp/clap-rs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kbknapp/clap-rs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Expand Down Expand Up @@ -38,6 +39,10 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)

## What's New

Here's the highlights for v2.9.0

* **Completions:** one can now generate a bash completions script at compile time!

Here's the highlights for v2.8.0

* **Arg:** adds new optional setting `Arg::require_delimiter` which requires val delimiter to parse multiple values
Expand Down Expand Up @@ -192,6 +197,8 @@ Below are a few of the features which `clap` supports, full descriptions and usa

* **Auto-generated Help, Version, and Usage information**
- Can optionally be fully, or partially overridden if you want a custom help, version, or usage
* **Auto-generated bash completion scripts at compile time**
- Even works through many multiple levels of subcommands
* **Flags / Switches** (i.e. bool fields)
- Both short and long versions supported (i.e. `-f` and `--flag` respectively)
- Supports combining short versions (i.e. `-fBgoZ` is the same as `-f -B -g -o -Z`)
Expand Down Expand Up @@ -340,37 +347,6 @@ fn main() {
}
```

The following combines the previous two examples by using the less verbose `from_usage` methods and the performance of the Builder Pattern.

```rust
// (Full example with detailed comments in examples/01c_quick_example.rs)
// Must be compiled with `--features unstable`
//
// This example demonstrates clap's "usage strings" method of creating arguments which is less
// less verbose
#[macro_use]
extern crate clap;

fn main() {
let matches = clap_app!(myapp =>
(version: "1.0")
(author: "Kevin K. <[email protected]>")
(about: "Does awesome things")
(@arg config: -c --config +takes_value "Sets a custom config file")
(@arg INPUT: +required "Sets the input file to use")
(@arg verbose: -v ... "Sets the level of verbosity")
(@subcommand test =>
(about: "controls testing features")
(version: "1.3")
(author: "Someone E. <[email protected]>")
(@arg verbose: -d --debug "Print debug information")
)
).get_matches();

// Same as previous examples...
}
```

This final 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.yml` file to hold your CLI options, but it could be called anything we like (we'll use the same both examples above to keep it functionally equivalent):

```yaml
Expand Down
84 changes: 83 additions & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
mod settings;
#[macro_use]
mod macros;
mod parser;
pub mod parser;
mod meta;
mod help;

Expand All @@ -27,6 +27,7 @@ use app::parser::Parser;
use app::help::Help;
use errors::Error;
use errors::Result as ClapResult;
use shell::Shell;

/// Used to create a representation of a command line program and all possible command line
/// arguments. Application settings are set using the "builder pattern" with the
Expand Down Expand Up @@ -939,6 +940,87 @@ impl<'a, 'b> App<'a, 'b> {
self.p.write_version(w).map_err(From::from)
}


/// Generate a completions file for a specified shell at compile time.
///
/// **NOTE:** to generate the this file at compile time you must use a `build.rs` "Build Script"
///
/// # Examples
///
/// The following example generates a bash completion script via a `build.rs` script. In this
/// simple example, we'll demo a very small application with only a single subcommand and two
/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
/// potentiall hundreds of arguments.
///
/// First, it helps if we separate out our `App` definition into a seperate file. Whether you
/// do this as a function, or bare App definition is a matter of personal preference.
///
/// ```ignore
/// // src/cli.rs
///
/// use clap::{App, Arg, SubCommand};
///
/// fn build_cli() -> App<'static, 'static> {
/// App::new("compl")
/// .about("Tests completions")
/// .arg(Arg::with_name("file")
/// .help("some input file"))
/// .subcommand(SubCommand::with_name("test")
/// .about("tests things")
/// .arg(Arg::with_name("case")
/// .long("case")
/// .takes_value(true)
/// .help("the case to test")))
/// }
/// ```
///
/// In our regular code, we can simply call this `build_cli()` function, then call
/// `get_mathces()`, or any of the other normal methods directly after. For example:
///
/// ```ignore
/// src/main.rs
///
/// use cli;
///
/// fn main() {
/// let m = cli::build_cli().get_matches();
///
/// // normal logic continues...
/// }
/// ```
/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
/// ```ignore
/// # Cargo.toml
/// build = "build.rs"
///
/// [build-dependencies]
/// clap = "2.9"
/// ```
///
/// Next, we place a `build.rs` in our project root.
///
/// ```ignore
/// extern crate clap;
///
/// use clap::Shell;
///
/// include!("src/cli.rs");
///
/// fn main() {
/// let mut app = build_cli();
/// app.gen_completions("myapp", // We need to specify the bin name manually
/// Shell::Bash, // Then say which shell to build completions for
/// env!("OUT_DIR")); // Then say where write the completions to
/// }
/// ```
/// Now, once we combile there will be a `bash.sh` file in the directory. Assuming we compiled
/// with debug mode, it would be somewhere similar to
/// `<project>/target/debug/build/myapp-<hash>/out/bash.sh`
pub fn gen_completions<T: Into<OsString>, S: Into<String>>(&mut self, bin_name: S, for_shell: Shell, out_dir: T) {
self.p.meta.bin_name = Some(bin_name.into());
self.p.gen_completions(for_shell, out_dir.into());
}

/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
/// the process will exit with the appropriate error code. By default this method gets all user
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,
Expand Down
56 changes: 50 additions & 6 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,24 @@ use fmt::{Format, ColorWhen};
use osstringext::OsStrExt2;
use app::meta::AppMeta;
use args::MatchedArg;
use shell::Shell;
use completions::ComplGen;

#[allow(missing_debug_implementations)]
#[doc(hidden)]
pub struct Parser<'a, 'b>
where 'a: 'b
{
required: Vec<&'b str>,
short_list: Vec<char>,
long_list: Vec<&'b str>,
pub short_list: Vec<char>,
pub long_list: Vec<&'b str>,
blacklist: Vec<&'b str>,
// A list of possible flags
flags: Vec<FlagBuilder<'a, 'b>>,
// A list of possible options
opts: Vec<OptBuilder<'a, 'b>>,
pub opts: Vec<OptBuilder<'a, 'b>>,
// A list of positional arguments
positionals: VecMap<PosBuilder<'a, 'b>>,
pub positionals: VecMap<PosBuilder<'a, 'b>>,
// A list of subcommands
#[doc(hidden)]
pub subcommands: Vec<App<'a, 'b>>,
Expand Down Expand Up @@ -97,6 +99,12 @@ impl<'a, 'b> Parser<'a, 'b>
.nth(0);
}

pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) {
self.propogate_help_version();
self.build_bin_names();
ComplGen::new(self, od).generate(for_shell)
}

// actually adds the arguments
pub fn add_arg(&mut self, a: &Arg<'a, 'b>) {
debug_assert!(!(self.flags.iter().any(|f| &f.name == &a.name) ||
Expand Down Expand Up @@ -236,6 +244,7 @@ impl<'a, 'b> Parser<'a, 'b>
self.required.iter()
}


#[cfg_attr(feature = "lints", allow(for_kv_map))]
pub fn get_required_from(&self,
reqs: &[&'a str],
Expand Down Expand Up @@ -652,7 +661,7 @@ impl<'a, 'b> Parser<'a, 'b>
self.meta
.bin_name
.as_ref()
.unwrap_or(&String::new()),
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
Expand Down Expand Up @@ -788,6 +797,41 @@ impl<'a, 'b> Parser<'a, 'b>
Ok(())
}

fn propogate_help_version(&mut self) {
debugln!("exec=propogate_help_version;");
self.create_help_and_version();
for sc in self.subcommands.iter_mut() {
sc.p.propogate_help_version();
}
}

fn build_bin_names(&mut self) {
debugln!("exec=build_bin_names;");
for sc in self.subcommands.iter_mut() {
debug!("bin_name set...");
if sc.p.meta.bin_name.is_none() {
sdebugln!("No");
let bin_name = format!("{}{}{}",
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*sc.p.meta.name);
debugln!("Setting bin_name of {} to {}", self.meta.name, bin_name);
sc.p.meta.bin_name = Some(bin_name);
} else {
sdebugln!("yes ({:?})", sc.p.meta.bin_name);
}
debugln!("Calling build_bin_names from...{}", sc.p.meta.name);
sc.p.build_bin_names();
}
}

fn parse_subcommand<I, T>(&mut self,
sc_name: String,
matcher: &mut ArgMatcher<'a>,
Expand Down Expand Up @@ -1258,7 +1302,7 @@ impl<'a, 'b> Parser<'a, 'b>
// If there was a delimiter used, we're not looking for more values
if val.contains_byte(delim as u32 as u8) || arg.is_set(ArgSettings::RequireDelimiter) {
ret = None;
}
}
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
Expand Down
Loading

0 comments on commit 70fa5f7

Please sign in to comment.