use std::fmt::{Debug, Formatter, Result};
use crate::util::Key;
type Id = u64;
#[derive(Default)]
pub struct ArgGroup<'a> {
pub(crate) id: Id,
pub(crate) name: &'a str,
pub(crate) args: Vec<Id>,
pub(crate) required: bool,
pub(crate) requires: Option<Vec<Id>>,
pub(crate) conflicts: Option<Vec<Id>>,
pub(crate) multiple: bool,
}
impl<'a> ArgGroup<'a> {
pub(crate) fn _with_id(id: Id) -> Self {
ArgGroup {
id,
..ArgGroup::default()
}
}
pub fn new<T: Key>(id: T) -> Self {
ArgGroup::_with_id(id.key())
}
pub fn with_name(n: &'a str) -> Self {
ArgGroup {
id: n.key(),
name: n,
..ArgGroup::default()
}
}
#[cfg(feature = "yaml")]
pub fn from_yaml(y: &'a yaml_rust::Yaml) -> ArgGroup<'a> {
ArgGroup::from(y.as_hash().unwrap())
}
pub fn arg<T: Key>(mut self, arg_id: T) -> Self {
self.args.push(arg_id.key());
self
}
pub fn args<T: Key>(mut self, ns: &[T]) -> 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<T: Key>(mut self, id: T) -> Self {
let arg_id = id.key();
if let Some(ref mut reqs) = self.requires {
reqs.push(arg_id);
} else {
self.requires = Some(vec![arg_id]);
}
self
}
pub fn requires_all(mut self, ns: &[&'a str]) -> Self {
for n in ns {
self = self.requires(n);
}
self
}
pub fn conflicts_with<T: Key>(mut self, id: T) -> Self {
let arg_id = id.key();
if let Some(ref mut confs) = self.conflicts {
confs.push(arg_id);
} else {
self.conflicts = Some(vec![arg_id]);
}
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 {
id: g.id,
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 yaml_rust::yaml::Hash> for ArgGroup<'a> {
fn from(b: &'a yaml_rust::yaml::Hash) -> Self {
let mut a = ArgGroup::default();
let group_settings = if b.len() == 1 {
let name_yml = b.keys().next().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;
use super::Key;
#[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".key(), "a4".key(), "a2".key(), "a3".key()];
let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()];
let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()];
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".key(), "a4".key(), "a2".key(), "a3".key()];
let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()];
let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()];
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".key(), "a4".key(), "a2".key(), "a3".key()];
let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()];
let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()];
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".key(), "a4".key(), "a2".key(), "a3".key()];
let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()];
let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()];
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 {
id: self.id,
name: self.name,
required: self.required,
args: self.args.clone(),
requires: self.requires.clone(),
conflicts: self.conflicts.clone(),
multiple: self.multiple,
}
}
}