mirror of
https://github.com/clap-rs/clap
synced 2024-12-12 13:52:34 +00:00
docs(cookbook): Add position-sensitive example
This commit is contained in:
parent
7486a0b4b9
commit
9c9cc9fcff
5 changed files with 162 additions and 0 deletions
|
@ -126,6 +126,10 @@ required-features = ["cargo"]
|
|||
name = "escaped-positional-derive"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "find"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "git-derive"
|
||||
required-features = ["derive"]
|
||||
|
|
47
examples/find.md
Normal file
47
examples/find.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
`find` is an example of position-sensitive flags
|
||||
|
||||
```console
|
||||
$ find --help
|
||||
clap 4.0.0-alpha.0
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
USAGE:
|
||||
find[EXE] [OPTIONS] --name <NAME>
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
TESTS:
|
||||
--empty File is empty and is either a regular file or a directory
|
||||
--name <NAME> Base of file name (the path with the leading directories removed) matches
|
||||
shell pattern pattern
|
||||
|
||||
OPERATORS:
|
||||
-o, --or expr2 is not evaluate if exp1 is true
|
||||
-a, --and Same as `expr1 expr1`
|
||||
|
||||
$ find --empty -o --name .keep
|
||||
[
|
||||
(
|
||||
"empty",
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
(
|
||||
"or",
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
String(
|
||||
".keep",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
```
|
||||
|
99
examples/find.rs
Normal file
99
examples/find.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use clap::{arg, command, ArgGroup, ArgMatches, Command};
|
||||
|
||||
fn main() {
|
||||
let matches = cli().get_matches();
|
||||
let values = Value::from_matches(&matches);
|
||||
println!("{:#?}", values);
|
||||
}
|
||||
|
||||
fn cli() -> Command<'static> {
|
||||
command!()
|
||||
.group(ArgGroup::new("tests").multiple(true))
|
||||
.next_help_heading("TESTS")
|
||||
.args([
|
||||
arg!(--empty "File is empty and is either a regular file or a directory").group("tests"),
|
||||
arg!(--name <NAME> "Base of file name (the path with the leading directories removed) matches shell pattern pattern").group("tests"),
|
||||
])
|
||||
.group(ArgGroup::new("operators").multiple(true))
|
||||
.next_help_heading("OPERATORS")
|
||||
.args([
|
||||
arg!(-o - -or "expr2 is not evaluate if exp1 is true").group("operators"),
|
||||
arg!(-a - -and "Same as `expr1 expr1`").group("operators"),
|
||||
])
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Value {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_matches(matches: &ArgMatches) -> Vec<(clap::Id, Self)> {
|
||||
let mut values = BTreeMap::new();
|
||||
for id in matches.ids() {
|
||||
if matches.try_get_many::<clap::Id>(id.as_str()).is_ok() {
|
||||
// ignore groups
|
||||
continue;
|
||||
}
|
||||
let value_source = matches
|
||||
.value_source(id.as_str())
|
||||
.expect("id came from matches");
|
||||
if value_source != clap::parser::ValueSource::CommandLine {
|
||||
// Any other source just gets tacked on at the end (like default values)
|
||||
continue;
|
||||
}
|
||||
if Self::extract::<String>(matches, id, &mut values) {
|
||||
continue;
|
||||
}
|
||||
if Self::extract::<bool>(matches, id, &mut values) {
|
||||
continue;
|
||||
}
|
||||
unimplemented!("unknown type for {}: {:?}", id, matches);
|
||||
}
|
||||
values.into_values().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn extract<T: Clone + Into<Value> + Send + Sync + 'static>(
|
||||
matches: &ArgMatches,
|
||||
id: &clap::Id,
|
||||
output: &mut BTreeMap<usize, (clap::Id, Self)>,
|
||||
) -> bool {
|
||||
match matches.try_get_many::<T>(id.as_str()) {
|
||||
Ok(Some(values)) => {
|
||||
for (value, index) in values.zip(
|
||||
matches
|
||||
.indices_of(id.as_str())
|
||||
.expect("id came from matches"),
|
||||
) {
|
||||
output.insert(index, (id.clone(), value.clone().into()));
|
||||
}
|
||||
true
|
||||
}
|
||||
Ok(None) => {
|
||||
unreachable!("`ids` only reports what is present")
|
||||
}
|
||||
Err(clap::parser::MatchesError::UnknownArgument { .. }) => {
|
||||
unreachable!("id came from matches")
|
||||
}
|
||||
Err(clap::parser::MatchesError::Downcast { .. }) => false,
|
||||
Err(_) => {
|
||||
unreachable!("id came from matches")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(other: String) -> Self {
|
||||
Self::String(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(other: bool) -> Self {
|
||||
Self::Bool(other)
|
||||
}
|
||||
}
|
7
src/_cookbook/find.rs
Normal file
7
src/_cookbook/find.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
//! # Example: find-like CLI (Builder API)
|
||||
//!
|
||||
//! ```rust
|
||||
#![doc = include_str!("../../examples/find.rs")]
|
||||
//! ```
|
||||
//!
|
||||
#![doc = include_str!("../../examples/find.md")]
|
|
@ -16,6 +16,10 @@
|
|||
//! - Subcommands
|
||||
//! - Cargo plugins
|
||||
//!
|
||||
//! find-like interface: [builder][find]
|
||||
//! - Topics:
|
||||
//! - Position-sensitive flags
|
||||
//!
|
||||
//! git-like interface: [builder][git], [derive][git_derive]
|
||||
//! - Topics:
|
||||
//! - Subcommands
|
||||
|
@ -46,6 +50,7 @@ pub mod cargo_example;
|
|||
pub mod cargo_example_derive;
|
||||
pub mod escaped_positional;
|
||||
pub mod escaped_positional_derive;
|
||||
pub mod find;
|
||||
pub mod git;
|
||||
pub mod git_derive;
|
||||
pub mod multicall_busybox;
|
||||
|
|
Loading…
Reference in a new issue