use crate::util::{Id, Key};
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ArgGroup<'help> {
pub(crate) id: Id,
pub(crate) name: &'help str,
pub(crate) args: Vec<Id>,
pub(crate) required: bool,
pub(crate) requires: Vec<Id>,
pub(crate) conflicts: Vec<Id>,
pub(crate) multiple: bool,
}
impl<'help> ArgGroup<'help> {
pub(crate) fn with_id(id: Id) -> Self {
ArgGroup {
id,
..ArgGroup::default()
}
}
pub fn new<S: Into<&'help str>>(n: S) -> Self {
let name = n.into();
ArgGroup {
id: Id::from(&*name),
name,
..ArgGroup::default()
}
}
pub fn arg<T: Key>(mut self, arg_id: T) -> Self {
self.args.push(arg_id.into());
self
}
pub fn args<T: Key>(mut self, ns: &[T]) -> Self {
for n in ns {
self = self.arg(n);
}
self
}
#[inline]
pub fn multiple(mut self, m: bool) -> Self {
self.multiple = m;
self
}
#[inline]
pub fn required(mut self, r: bool) -> Self {
self.required = r;
self
}
pub fn requires<T: Key>(mut self, id: T) -> Self {
self.requires.push(id.into());
self
}
pub fn requires_all(mut self, ns: &[&'help str]) -> Self {
for n in ns {
self = self.requires(n);
}
self
}
pub fn conflicts_with<T: Key>(mut self, id: T) -> Self {
self.conflicts.push(id.into());
self
}
pub fn conflicts_with_all(mut self, ns: &[&'help str]) -> Self {
for n in ns {
self = self.conflicts_with(n);
}
self
}
}
impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> {
fn from(g: &ArgGroup<'help>) -> Self {
ArgGroup {
id: g.id.clone(),
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<'help> From<&'help Yaml> for ArgGroup<'help> {
fn from(y: &'help Yaml) -> Self {
let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table");
let mut a = ArgGroup::default();
let group_settings = if b.len() == 1 {
let name_yaml = b.keys().next().expect("failed to get name");
let name_str = name_yaml
.as_str()
.expect("failed to convert arg YAML name to str");
a.name = name_str;
b.get(name_yaml)
.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!(a, v, arg),
"arg" => {
if let Some(ys) = v.as_str() {
a = a.arg(ys);
}
a
}
"requires" => yaml_vec_or_str!(a, v, requires),
"conflicts_with" => yaml_vec_or_str!(a, v, 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::new("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".into(), "a4".into(), "a2".into(), "a3".into()];
let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
assert_eq!(g.args, args);
assert_eq!(g.requires, reqs);
assert_eq!(g.conflicts, confs);
}
#[test]
fn test_from() {
let g = ArgGroup::new("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".into(), "a4".into(), "a2".into(), "a3".into()];
let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
let g2 = ArgGroup::from(&g);
assert_eq!(g2.args, args);
assert_eq!(g2.requires, reqs);
assert_eq!(g2.conflicts, confs);
}
#[cfg(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 yaml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0];
let g = ArgGroup::from(yaml);
let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
assert_eq!(g.args, args);
assert_eq!(g.requires, reqs);
assert_eq!(g.conflicts, confs);
}
#[test]
fn arg_group_send_sync() {
fn foo<T: Send + Sync>(_: T) {}
foo(ArgGroup::new("test"))
}
}
impl Clone for ArgGroup<'_> {
fn clone(&self) -> Self {
ArgGroup {
id: self.id.clone(),
name: self.name,
required: self.required,
args: self.args.clone(),
requires: self.requires.clone(),
conflicts: self.conflicts.clone(),
multiple: self.multiple,
}
}
}