mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
fix: adds support for building ArgGroups from standalone YAML
ArgGroups can now be built from standalone YAML documents. This is labeled as a fix because it should have been the case prior. A standalone YAML document for a group would look something like ``` name: test args: - arg1 - arg2 required: true conflicts: - arg3 requires: - arg4 ``` This commit also keeps support building groups as part of a larger entire App YAML document where the name is specified as a key to the yaml tree
This commit is contained in:
parent
355a5fdabd
commit
fcbc7e12f5
2 changed files with 151 additions and 123 deletions
143
src/app/mod.rs
143
src/app/mod.rs
|
@ -98,74 +98,8 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// // continued logic goes here, such as `app.get_matches()` etc.
|
||||
/// ```
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml<'y>(mut yaml: &'y Yaml) -> App<'y, 'y> {
|
||||
use args::SubCommand;
|
||||
// We WANT this to panic on error...so expect() is good.
|
||||
let mut is_sc = None;
|
||||
let mut a = if let Some(name) = yaml["name"].as_str() {
|
||||
App::new(name)
|
||||
} else {
|
||||
let yaml_hash = yaml.as_hash().unwrap();
|
||||
let sc_key = yaml_hash.keys().nth(0).unwrap();
|
||||
is_sc = Some(yaml_hash.get(sc_key).unwrap());
|
||||
App::new(sc_key.as_str().unwrap())
|
||||
};
|
||||
yaml = if let Some(sc) = is_sc {
|
||||
sc
|
||||
} else {
|
||||
yaml
|
||||
};
|
||||
if let Some(v) = yaml["version"].as_str() {
|
||||
a = a.version(v);
|
||||
}
|
||||
if let Some(v) = yaml["author"].as_str() {
|
||||
a = a.author(v);
|
||||
}
|
||||
if let Some(v) = yaml["bin_name"].as_str() {
|
||||
a = a.bin_name(v);
|
||||
}
|
||||
if let Some(v) = yaml["about"].as_str() {
|
||||
a = a.about(v);
|
||||
}
|
||||
if let Some(v) = yaml["after_help"].as_str() {
|
||||
a = a.after_help(v);
|
||||
}
|
||||
if let Some(v) = yaml["usage"].as_str() {
|
||||
a = a.usage(v);
|
||||
}
|
||||
if let Some(v) = yaml["help"].as_str() {
|
||||
a = a.help(v);
|
||||
}
|
||||
if let Some(v) = yaml["help_short"].as_str() {
|
||||
a = a.help_short(v);
|
||||
}
|
||||
if let Some(v) = yaml["version_short"].as_str() {
|
||||
a = a.version_short(v);
|
||||
}
|
||||
if let Some(v) = yaml["settings"].as_vec() {
|
||||
for ys in v {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["args"].as_vec() {
|
||||
for arg_yaml in v {
|
||||
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["subcommands"].as_vec() {
|
||||
for sc_yaml in v {
|
||||
a = a.subcommand(SubCommand::from_yaml(&sc_yaml));
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["groups"].as_vec() {
|
||||
for ag_yaml in v {
|
||||
a = a.group(ArgGroup::from_yaml(&ag_yaml.as_hash().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
a
|
||||
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
|
||||
App::from(yaml)
|
||||
}
|
||||
|
||||
/// Sets a string of author(s) that will be displayed to the user when they request the help
|
||||
|
@ -810,3 +744,76 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
e.exit()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
impl<'a> From<&'a Yaml> for App<'a, 'a> {
|
||||
fn from(mut yaml: &'a Yaml) -> Self {
|
||||
use args::SubCommand;
|
||||
// We WANT this to panic on error...so expect() is good.
|
||||
let mut is_sc = None;
|
||||
let mut a = if let Some(name) = yaml["name"].as_str() {
|
||||
App::new(name)
|
||||
} else {
|
||||
let yaml_hash = yaml.as_hash().unwrap();
|
||||
let sc_key = yaml_hash.keys().nth(0).unwrap();
|
||||
is_sc = Some(yaml_hash.get(sc_key).unwrap());
|
||||
App::new(sc_key.as_str().unwrap())
|
||||
};
|
||||
yaml = if let Some(sc) = is_sc {
|
||||
sc
|
||||
} else {
|
||||
yaml
|
||||
};
|
||||
if let Some(v) = yaml["version"].as_str() {
|
||||
a = a.version(v);
|
||||
}
|
||||
if let Some(v) = yaml["author"].as_str() {
|
||||
a = a.author(v);
|
||||
}
|
||||
if let Some(v) = yaml["bin_name"].as_str() {
|
||||
a = a.bin_name(v);
|
||||
}
|
||||
if let Some(v) = yaml["about"].as_str() {
|
||||
a = a.about(v);
|
||||
}
|
||||
if let Some(v) = yaml["after_help"].as_str() {
|
||||
a = a.after_help(v);
|
||||
}
|
||||
if let Some(v) = yaml["usage"].as_str() {
|
||||
a = a.usage(v);
|
||||
}
|
||||
if let Some(v) = yaml["help"].as_str() {
|
||||
a = a.help(v);
|
||||
}
|
||||
if let Some(v) = yaml["help_short"].as_str() {
|
||||
a = a.help_short(v);
|
||||
}
|
||||
if let Some(v) = yaml["version_short"].as_str() {
|
||||
a = a.version_short(v);
|
||||
}
|
||||
if let Some(v) = yaml["settings"].as_vec() {
|
||||
for ys in v {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["args"].as_vec() {
|
||||
for arg_yaml in v {
|
||||
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["subcommands"].as_vec() {
|
||||
for sc_yaml in v {
|
||||
a = a.subcommand(SubCommand::from_yaml(&sc_yaml));
|
||||
}
|
||||
}
|
||||
if let Some(v) = yaml["groups"].as_vec() {
|
||||
for ag_yaml in v {
|
||||
a = a.group(ArgGroup::from(ag_yaml.as_hash().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
a
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ use yaml_rust::Yaml;
|
|||
/// .required(true))
|
||||
/// # ;
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct ArgGroup<'a> {
|
||||
#[doc(hidden)]
|
||||
pub name: &'a str,
|
||||
|
@ -93,54 +94,8 @@ impl<'a> ArgGroup<'a> {
|
|||
/// let ag = ArgGroup::from_yaml(yml);
|
||||
/// ```
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml<'y>(y: &'y BTreeMap<Yaml, Yaml>) -> ArgGroup<'y> {
|
||||
// We WANT this to panic on error...so expect() is good.
|
||||
let name_yml = y.keys().nth(0).unwrap();
|
||||
let name_str = name_yml.as_str().unwrap();
|
||||
let mut a = ArgGroup::with_name(name_str);
|
||||
let group_settings = y.get(name_yml).unwrap().as_hash().unwrap();
|
||||
|
||||
for (k, v) in group_settings.iter() {
|
||||
a = match k.as_str().unwrap() {
|
||||
"required" => a.required(v.as_bool().unwrap()),
|
||||
"args" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.arg(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
"arg" => {
|
||||
if let Some(ys) = v.as_str() {
|
||||
a = a.arg(ys);
|
||||
}
|
||||
a
|
||||
}
|
||||
"requires" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.requires(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
"conflicts_with" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.conflicts_with(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
|
||||
ArgGroup '{}'",
|
||||
s,
|
||||
name_str),
|
||||
}
|
||||
}
|
||||
|
||||
a
|
||||
pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> {
|
||||
ArgGroup::from(y.as_hash().unwrap())
|
||||
}
|
||||
|
||||
/// Adds an argument to this group by name
|
||||
|
@ -325,12 +280,12 @@ impl<'a> ArgGroup<'a> {
|
|||
impl<'a> Debug for ArgGroup<'a> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f,
|
||||
"{{
|
||||
name:{:?},
|
||||
args: {:?},
|
||||
required: {:?},
|
||||
requires: {:?},
|
||||
conflicts: {:?},
|
||||
"{{\n\
|
||||
\tname: {:?},\n\
|
||||
\targs: {:?},\n\
|
||||
\trequired: {:?},\n\
|
||||
\trequires: {:?},\n\
|
||||
\tconflicts: {:?},\n\
|
||||
}}",
|
||||
self.name,
|
||||
self.args,
|
||||
|
@ -352,9 +307,75 @@ impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
|
||||
fn from(b: &'a BTreeMap<Yaml, Yaml>) -> Self {
|
||||
// We WANT this to panic on error...so expect() is good.
|
||||
let mut a = ArgGroup::default();
|
||||
let group_settings = if b.len() == 1 {
|
||||
let name_yml = b.keys().nth(0).expect("failed to get name");
|
||||
let name_str = name_yml.as_str().expect("failed to convert name to str");
|
||||
a.name = name_str;
|
||||
b.get(name_yml).expect("failed to get name_str").as_hash().expect("failed to convert to a hash")
|
||||
} else {
|
||||
b
|
||||
};
|
||||
|
||||
for (k, v) in group_settings.iter() {
|
||||
a = match k.as_str().unwrap() {
|
||||
"required" => a.required(v.as_bool().unwrap()),
|
||||
"args" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.arg(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
"arg" => {
|
||||
if let Some(ys) = v.as_str() {
|
||||
a = a.arg(ys);
|
||||
}
|
||||
a
|
||||
}
|
||||
"requires" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.requires(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
"conflicts_with" => {
|
||||
for ys in v.as_vec().unwrap() {
|
||||
if let Some(s) = ys.as_str() {
|
||||
a = a.conflicts_with(s);
|
||||
}
|
||||
}
|
||||
a
|
||||
}
|
||||
"name" => {
|
||||
if let Some(ys) = v.as_str() {
|
||||
a.name = ys;
|
||||
}
|
||||
a
|
||||
}
|
||||
s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
|
||||
ArgGroup '{}'",
|
||||
s,
|
||||
a.name),
|
||||
}
|
||||
}
|
||||
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ArgGroup;
|
||||
#[cfg(feature = "yaml")]
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
#[test]
|
||||
fn groups() {
|
||||
|
|
Loading…
Reference in a new issue