#[cfg(feature = "yaml")]
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter, Result};
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
#[derive(Default)]
pub struct ArgGroup<'a> {
#[doc(hidden)]
pub name: &'a str,
#[doc(hidden)]
pub args: Vec<&'a str>,
#[doc(hidden)]
pub required: bool,
#[doc(hidden)]
pub requires: Option<Vec<&'a str>>,
#[doc(hidden)]
pub conflicts: Option<Vec<&'a str>>,
#[doc(hidden)]
pub multiple: bool,
}
impl<'a> ArgGroup<'a> {
pub fn with_name(n: &'a str) -> Self {
ArgGroup {
name: n,
required: false,
args: vec![],
requires: None,
conflicts: None,
multiple: false,
}
}
#[cfg(feature = "yaml")]
pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> {
ArgGroup::from(y.as_hash().unwrap())
}
pub fn arg(mut self, n: &'a str) -> Self {
assert!(
self.name != n,
"ArgGroup '{}' can not have same name as arg inside it",
&*self.name
);
self.args.push(n);
self
}
pub fn args(mut self, ns: &[&'a str]) -> Self {
for n in ns {
self = self.arg(n);
}
self
}
pub fn multiple(mut self, m: bool) -> Self {
self.multiple = m;
self
}
pub fn required(mut self, r: bool) -> Self {
self.required = r;
self
}
pub fn requires(mut self, n: &'a str) -> Self {
if let Some(ref mut reqs) = self.requires {
reqs.push(n);
} else {
self.requires = Some(vec![n]);
}
self
}
pub fn requires_all(mut self, ns: &[&'a str]) -> Self {
for n in ns {
self = self.requires(n);
}
self
}
pub fn conflicts_with(mut self, n: &'a str) -> Self {
if let Some(ref mut confs) = self.conflicts {
confs.push(n);
} else {
self.conflicts = Some(vec![n]);
}
self
}
pub fn conflicts_with_all(mut self, ns: &[&'a str]) -> Self {
for n in ns {
self = self.conflicts_with(n);
}
self
}
}
impl<'a> Debug for ArgGroup<'a> {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(
f,
"{{\n\
\tname: {:?},\n\
\targs: {:?},\n\
\trequired: {:?},\n\
\trequires: {:?},\n\
\tconflicts: {:?},\n\
}}",
self.name, self.args, self.required, self.requires, self.conflicts
)
}
}
impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
fn from(g: &'z ArgGroup<'a>) -> Self {
ArgGroup {
name: g.name,
required: g.required,
args: g.args.clone(),
requires: g.requires.clone(),
conflicts: g.conflicts.clone(),
multiple: g.multiple,
}
}
}
#[cfg(feature = "yaml")]
impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
fn from(b: &'a BTreeMap<Yaml, Yaml>) -> Self {
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 arg YAML 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 {
a = match k.as_str().unwrap() {
"required" => a.required(v.as_bool().unwrap()),
"multiple" => a.multiple(v.as_bool().unwrap()),
"args" => yaml_vec_or_str!(v, a, arg),
"arg" => {
if let Some(ys) = v.as_str() {
a = a.arg(ys);
}
a
}
"requires" => yaml_vec_or_str!(v, a, requires),
"conflicts_with" => yaml_vec_or_str!(v, a, conflicts_with),
"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() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
let confs = vec!["c1", "c2", "c3", "c4"];
assert_eq!(g.args, args);
assert_eq!(g.requires, Some(reqs));
assert_eq!(g.conflicts, Some(confs));
}
#[test]
fn test_debug() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
let confs = vec!["c1", "c2", "c3", "c4"];
let debug_str = format!(
"{{\n\
\tname: \"test\",\n\
\targs: {:?},\n\
\trequired: {:?},\n\
\trequires: {:?},\n\
\tconflicts: {:?},\n\
}}",
args,
true,
Some(reqs),
Some(confs)
);
assert_eq!(&*format!("{:?}", g), &*debug_str);
}
#[test]
fn test_from() {
let g = ArgGroup::with_name("test")
.arg("a1")
.arg("a4")
.args(&["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(&["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(&["r2", "r3"])
.requires("r4");
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
let confs = vec!["c1", "c2", "c3", "c4"];
let g2 = ArgGroup::from(&g);
assert_eq!(g2.args, args);
assert_eq!(g2.requires, Some(reqs));
assert_eq!(g2.conflicts, Some(confs));
}
#[cfg(feature = "yaml")]
#[cfg_attr(feature = "yaml", test)]
fn test_yaml() {
let g_yaml = "name: test
args:
- a1
- a4
- a2
- a3
conflicts_with:
- c1
- c2
- c3
- c4
requires:
- r1
- r2
- r3
- r4";
let yml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0];
let g = ArgGroup::from_yaml(yml);
let args = vec!["a1", "a4", "a2", "a3"];
let reqs = vec!["r1", "r2", "r3", "r4"];
let confs = vec!["c1", "c2", "c3", "c4"];
assert_eq!(g.args, args);
assert_eq!(g.requires, Some(reqs));
assert_eq!(g.conflicts, Some(confs));
}
}
impl<'a> Clone for ArgGroup<'a> {
fn clone(&self) -> Self {
ArgGroup {
name: self.name,
required: self.required,
args: self.args.clone(),
requires: self.requires.clone(),
conflicts: self.conflicts.clone(),
multiple: self.multiple,
}
}
}