Skip to content

Commit fcbc7e1

Browse files
committed
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
1 parent 355a5fd commit fcbc7e1

File tree

2 files changed

+151
-123
lines changed

2 files changed

+151
-123
lines changed

src/app/mod.rs

+75-68
Original file line numberDiff line numberDiff line change
@@ -98,74 +98,8 @@ impl<'a, 'b> App<'a, 'b> {
9898
/// // continued logic goes here, such as `app.get_matches()` etc.
9999
/// ```
100100
#[cfg(feature = "yaml")]
101-
pub fn from_yaml<'y>(mut yaml: &'y Yaml) -> App<'y, 'y> {
102-
use args::SubCommand;
103-
// We WANT this to panic on error...so expect() is good.
104-
let mut is_sc = None;
105-
let mut a = if let Some(name) = yaml["name"].as_str() {
106-
App::new(name)
107-
} else {
108-
let yaml_hash = yaml.as_hash().unwrap();
109-
let sc_key = yaml_hash.keys().nth(0).unwrap();
110-
is_sc = Some(yaml_hash.get(sc_key).unwrap());
111-
App::new(sc_key.as_str().unwrap())
112-
};
113-
yaml = if let Some(sc) = is_sc {
114-
sc
115-
} else {
116-
yaml
117-
};
118-
if let Some(v) = yaml["version"].as_str() {
119-
a = a.version(v);
120-
}
121-
if let Some(v) = yaml["author"].as_str() {
122-
a = a.author(v);
123-
}
124-
if let Some(v) = yaml["bin_name"].as_str() {
125-
a = a.bin_name(v);
126-
}
127-
if let Some(v) = yaml["about"].as_str() {
128-
a = a.about(v);
129-
}
130-
if let Some(v) = yaml["after_help"].as_str() {
131-
a = a.after_help(v);
132-
}
133-
if let Some(v) = yaml["usage"].as_str() {
134-
a = a.usage(v);
135-
}
136-
if let Some(v) = yaml["help"].as_str() {
137-
a = a.help(v);
138-
}
139-
if let Some(v) = yaml["help_short"].as_str() {
140-
a = a.help_short(v);
141-
}
142-
if let Some(v) = yaml["version_short"].as_str() {
143-
a = a.version_short(v);
144-
}
145-
if let Some(v) = yaml["settings"].as_vec() {
146-
for ys in v {
147-
if let Some(s) = ys.as_str() {
148-
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
149-
}
150-
}
151-
}
152-
if let Some(v) = yaml["args"].as_vec() {
153-
for arg_yaml in v {
154-
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));
155-
}
156-
}
157-
if let Some(v) = yaml["subcommands"].as_vec() {
158-
for sc_yaml in v {
159-
a = a.subcommand(SubCommand::from_yaml(&sc_yaml));
160-
}
161-
}
162-
if let Some(v) = yaml["groups"].as_vec() {
163-
for ag_yaml in v {
164-
a = a.group(ArgGroup::from_yaml(&ag_yaml.as_hash().unwrap()));
165-
}
166-
}
167-
168-
a
101+
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
102+
App::from(yaml)
169103
}
170104

171105
/// 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> {
810744
e.exit()
811745
}
812746
}
747+
748+
#[cfg(feature = "yaml")]
749+
impl<'a> From<&'a Yaml> for App<'a, 'a> {
750+
fn from(mut yaml: &'a Yaml) -> Self {
751+
use args::SubCommand;
752+
// We WANT this to panic on error...so expect() is good.
753+
let mut is_sc = None;
754+
let mut a = if let Some(name) = yaml["name"].as_str() {
755+
App::new(name)
756+
} else {
757+
let yaml_hash = yaml.as_hash().unwrap();
758+
let sc_key = yaml_hash.keys().nth(0).unwrap();
759+
is_sc = Some(yaml_hash.get(sc_key).unwrap());
760+
App::new(sc_key.as_str().unwrap())
761+
};
762+
yaml = if let Some(sc) = is_sc {
763+
sc
764+
} else {
765+
yaml
766+
};
767+
if let Some(v) = yaml["version"].as_str() {
768+
a = a.version(v);
769+
}
770+
if let Some(v) = yaml["author"].as_str() {
771+
a = a.author(v);
772+
}
773+
if let Some(v) = yaml["bin_name"].as_str() {
774+
a = a.bin_name(v);
775+
}
776+
if let Some(v) = yaml["about"].as_str() {
777+
a = a.about(v);
778+
}
779+
if let Some(v) = yaml["after_help"].as_str() {
780+
a = a.after_help(v);
781+
}
782+
if let Some(v) = yaml["usage"].as_str() {
783+
a = a.usage(v);
784+
}
785+
if let Some(v) = yaml["help"].as_str() {
786+
a = a.help(v);
787+
}
788+
if let Some(v) = yaml["help_short"].as_str() {
789+
a = a.help_short(v);
790+
}
791+
if let Some(v) = yaml["version_short"].as_str() {
792+
a = a.version_short(v);
793+
}
794+
if let Some(v) = yaml["settings"].as_vec() {
795+
for ys in v {
796+
if let Some(s) = ys.as_str() {
797+
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
798+
}
799+
}
800+
}
801+
if let Some(v) = yaml["args"].as_vec() {
802+
for arg_yaml in v {
803+
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));
804+
}
805+
}
806+
if let Some(v) = yaml["subcommands"].as_vec() {
807+
for sc_yaml in v {
808+
a = a.subcommand(SubCommand::from_yaml(&sc_yaml));
809+
}
810+
}
811+
if let Some(v) = yaml["groups"].as_vec() {
812+
for ag_yaml in v {
813+
a = a.group(ArgGroup::from(ag_yaml.as_hash().unwrap()));
814+
}
815+
}
816+
817+
a
818+
}
819+
}

src/args/group.rs

+76-55
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use yaml_rust::Yaml;
4949
/// .required(true))
5050
/// # ;
5151
/// ```
52+
#[derive(Default)]
5253
pub struct ArgGroup<'a> {
5354
#[doc(hidden)]
5455
pub name: &'a str,
@@ -93,54 +94,8 @@ impl<'a> ArgGroup<'a> {
9394
/// let ag = ArgGroup::from_yaml(yml);
9495
/// ```
9596
#[cfg(feature = "yaml")]
96-
pub fn from_yaml<'y>(y: &'y BTreeMap<Yaml, Yaml>) -> ArgGroup<'y> {
97-
// We WANT this to panic on error...so expect() is good.
98-
let name_yml = y.keys().nth(0).unwrap();
99-
let name_str = name_yml.as_str().unwrap();
100-
let mut a = ArgGroup::with_name(name_str);
101-
let group_settings = y.get(name_yml).unwrap().as_hash().unwrap();
102-
103-
for (k, v) in group_settings.iter() {
104-
a = match k.as_str().unwrap() {
105-
"required" => a.required(v.as_bool().unwrap()),
106-
"args" => {
107-
for ys in v.as_vec().unwrap() {
108-
if let Some(s) = ys.as_str() {
109-
a = a.arg(s);
110-
}
111-
}
112-
a
113-
}
114-
"arg" => {
115-
if let Some(ys) = v.as_str() {
116-
a = a.arg(ys);
117-
}
118-
a
119-
}
120-
"requires" => {
121-
for ys in v.as_vec().unwrap() {
122-
if let Some(s) = ys.as_str() {
123-
a = a.requires(s);
124-
}
125-
}
126-
a
127-
}
128-
"conflicts_with" => {
129-
for ys in v.as_vec().unwrap() {
130-
if let Some(s) = ys.as_str() {
131-
a = a.conflicts_with(s);
132-
}
133-
}
134-
a
135-
}
136-
s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
137-
ArgGroup '{}'",
138-
s,
139-
name_str),
140-
}
141-
}
142-
143-
a
97+
pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> {
98+
ArgGroup::from(y.as_hash().unwrap())
14499
}
145100

146101
/// Adds an argument to this group by name
@@ -325,13 +280,13 @@ impl<'a> ArgGroup<'a> {
325280
impl<'a> Debug for ArgGroup<'a> {
326281
fn fmt(&self, f: &mut Formatter) -> Result {
327282
write!(f,
328-
"{{
329-
name:{:?},
330-
args: {:?},
331-
required: {:?},
332-
requires: {:?},
333-
conflicts: {:?},
334-
}}",
283+
"{{\n\
284+
\tname: {:?},\n\
285+
\targs: {:?},\n\
286+
\trequired: {:?},\n\
287+
\trequires: {:?},\n\
288+
\tconflicts: {:?},\n\
289+
}}",
335290
self.name,
336291
self.args,
337292
self.required,
@@ -352,9 +307,75 @@ impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
352307
}
353308
}
354309

310+
#[cfg(feature = "yaml")]
311+
impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
312+
fn from(b: &'a BTreeMap<Yaml, Yaml>) -> Self {
313+
// We WANT this to panic on error...so expect() is good.
314+
let mut a = ArgGroup::default();
315+
let group_settings = if b.len() == 1 {
316+
let name_yml = b.keys().nth(0).expect("failed to get name");
317+
let name_str = name_yml.as_str().expect("failed to convert name to str");
318+
a.name = name_str;
319+
b.get(name_yml).expect("failed to get name_str").as_hash().expect("failed to convert to a hash")
320+
} else {
321+
b
322+
};
323+
324+
for (k, v) in group_settings.iter() {
325+
a = match k.as_str().unwrap() {
326+
"required" => a.required(v.as_bool().unwrap()),
327+
"args" => {
328+
for ys in v.as_vec().unwrap() {
329+
if let Some(s) = ys.as_str() {
330+
a = a.arg(s);
331+
}
332+
}
333+
a
334+
}
335+
"arg" => {
336+
if let Some(ys) = v.as_str() {
337+
a = a.arg(ys);
338+
}
339+
a
340+
}
341+
"requires" => {
342+
for ys in v.as_vec().unwrap() {
343+
if let Some(s) = ys.as_str() {
344+
a = a.requires(s);
345+
}
346+
}
347+
a
348+
}
349+
"conflicts_with" => {
350+
for ys in v.as_vec().unwrap() {
351+
if let Some(s) = ys.as_str() {
352+
a = a.conflicts_with(s);
353+
}
354+
}
355+
a
356+
}
357+
"name" => {
358+
if let Some(ys) = v.as_str() {
359+
a.name = ys;
360+
}
361+
a
362+
}
363+
s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
364+
ArgGroup '{}'",
365+
s,
366+
a.name),
367+
}
368+
}
369+
370+
a
371+
}
372+
}
373+
355374
#[cfg(test)]
356375
mod test {
357376
use super::ArgGroup;
377+
#[cfg(feature = "yaml")]
378+
use yaml_rust::YamlLoader;
358379

359380
#[test]
360381
fn groups() {

0 commit comments

Comments
 (0)