WIP changing macros into MKeyMap calls

This commit is contained in:
Alena Yuryeva 2018-07-19 15:50:47 +03:00
parent c22bc3e1fc
commit 98d37c133e
5 changed files with 430 additions and 61 deletions

View file

@ -22,6 +22,7 @@ use output::fmt::ColorWhen;
use output::{Help, Usage};
use parse::errors::Result as ClapResult;
use parse::{ArgMatcher, ArgMatches, Parser};
use mkeymap::{MKeyMap, KeyType};
#[doc(hidden)]
#[allow(dead_code)]
@ -105,7 +106,7 @@ where
#[doc(hidden)]
pub g_settings: AppFlags,
#[doc(hidden)]
pub args: Vec<Arg<'a, 'b>>,
pub args: MKeyMap,
#[doc(hidden)]
pub subcommands: Vec<App<'a, 'b>>,
#[doc(hidden)]
@ -637,6 +638,7 @@ impl<'a, 'b> App<'a, 'b> {
None
};
let arg = a.into().help_heading(help_heading);
//TODO add push functionality to MKeyMap
self.args.push(arg);
self
}
@ -1439,12 +1441,13 @@ impl<'a, 'b> App<'a, 'b> {
}
// Perform expensive debug assertions
debug_assert!({
for a in &self.args {
for a in self.args.values() {
self._arg_debug_asserts(a);
}
true
});
for a in &mut self.args {
//TODO add .values_mut() for MKeyMap
for a in self.args.values_mut() {
// Fill in the groups
if let Some(ref grps) = a.groups {
for g in grps {

View file

@ -581,7 +581,11 @@ use std::result::Result as StdResult;
#[macro_use]
mod macros;
<<<<<<< HEAD
mod build;
=======
mod mkeymap;
>>>>>>> WIP changing macros into MKeyMap calls
mod completions;
mod output;
mod parse;

View file

@ -1024,29 +1024,29 @@ macro_rules! find {
}
}
macro_rules! find_by_long {
($app:expr, $long:expr, $what:ident) => {{
$what!($app)
.filter(|a| a.long.is_some())
.find(|a| match_alias!(a, $long, a.long.unwrap()))
}};
($app:expr, $long:expr) => {{
$app.args.iter()
.filter(|a| a.long.is_some())
.find(|a| match_alias!(a, $long, a.long.unwrap()))
}};
}
// macro_rules! find_by_long {
// ($app:expr, $long:expr, $what:ident) => {{
// $what!($app)
// .filter(|a| a.long.is_some())
// .find(|a| match_alias!(a, $long, a.long.unwrap()))
// }};
// ($app:expr, $long:expr) => {{
// $app.args.iter()
// .filter(|a| a.long.is_some())
// .find(|a| match_alias!(a, $long, a.long.unwrap()))
// }};
// }
macro_rules! find_by_short {
($app:expr, $short:expr, $what:ident) => {{
$what!($app)
.find(|a| a.short == Some($short))
}};
($app:expr, $short:expr) => {{
$app.args.iter()
.find(|a| a.short == Some($short))
}}
}
// macro_rules! find_by_short {
// ($app:expr, $short:expr, $what:ident) => {{
// $what!($app)
// .find(|a| a.short == Some($short))
// }};
// ($app:expr, $short:expr) => {{
// $app.args.iter()
// .find(|a| a.short == Some($short))
// }}
// }
macro_rules! find_subcmd_cloned {
($_self:expr, $sc:expr) => {{
@ -1062,27 +1062,42 @@ macro_rules! find_subcmd {
}};
}
macro_rules! shorts {
($app:expr) => {{
_shorts_longs!($app, short)
}};
}
// macro_rules! shorts {
// ($app:expr) => {{
// _shorts_longs!($app, short)
// }};
// }
// macro_rules! longs {
// ($app:expr) => {{
// $app.args.iter()
// .filter(|a| a.long.is_some())
// .map(|a| a.long.unwrap())
// .chain($app.args.iter()
// .filter(|a| a.aliases.is_some())
// .flat_map(|a| a.aliases.as_ref().unwrap().iter().map(|als| als.0)))
// }};
// }
// macro_rules! _shorts_longs {
// ($app:expr, $what:ident) => {{
// $app.args.iter().filter_map(|a| a.$what)
// }};
// }
//TODO change into one macro (repeated structure)
macro_rules! longs {
($app:expr) => {{
$app.args.iter()
.filter(|a| a.long.is_some())
.map(|a| a.long.unwrap())
.chain($app.args.iter()
.filter(|a| a.aliases.is_some())
.flat_map(|a| a.aliases.as_ref().unwrap().iter().map(|als| als.0)))
}};
($app:expr) => ({
use mkeymap::KeyType;
$app.args.keys().filter_map(|a| if let KeyType::Long(v) = a {Some(v)} else {None})
});
}
macro_rules! _shorts_longs {
($app:expr, $what:ident) => {{
$app.args.iter().filter_map(|a| a.$what)
}};
macro_rules! shorts {
($app:expr) => ({
use mkeymap::KeyType;
$app.args.keys().filter_map(|a| if let KeyType::Short(v) = a {Some(v)} else {None})
})
}
macro_rules! _names {
@ -1113,15 +1128,15 @@ macro_rules! sc_names {
}};
}
macro_rules! match_alias {
($a:expr, $to:expr, $what:expr) => {{
$what == $to ||
($a.aliases.is_some() &&
$a.aliases
.as_ref()
.unwrap()
.iter()
.any(|alias| alias.0 == $to))
// macro_rules! match_alias {
// ($a:expr, $to:expr, $what:expr) => {{
// $what == $to ||
// ($a.aliases.is_some() &&
// $a.aliases
// .as_ref()
// .unwrap()
// .iter()
// .any(|alias| alias.0 == $to))
}}
}
// }}
// }

327
src/mkeymap.rs Normal file
View file

@ -0,0 +1,327 @@
#![feature(nll)]
use std::collections::hash_map;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
use std::slice;
use build::Arg;
// ! rustdoc
#[derive(Default, PartialEq, Debug)]
pub struct MKeyMap<'a, 'b>
where
'a: 'b,
{
keys: HashMap<KeyType<'a>, usize>,
value_index: Vec<Arg<'a, 'b>>,
values: HashMap<u64, HashSet<usize>>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum KeyType<'a> {
Short(char),
Long(&'a OsStr),
Position(usize),
}
impl<'a, 'b> MKeyMap<'a, 'b> {
pub fn new() -> Self {
MKeyMap::default()
}
//TODO ::from(x), ::with_capacity(n) etc
//? set theory ops?
pub fn insert(&mut self, key: KeyType<'a>, value: Arg<'a, 'b>) -> usize {
let index = self.push(value);
self.keys.insert(key, index);
index
}
pub fn push(&mut self, value: Arg<'a, 'b>) -> usize {
let index;
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
let hash = hasher.finish();
if let Some((idx, _)) = self.values.get(&hash).and_then(|ids| {
ids.iter()
.map(|&x| (x, &self.value_index[x]))
.find(|(_i, x)| x == &&value)
}) {
index = idx;
} else {
self.value_index.push(value);
index = self.value_index.len() - 1;
self.values
.entry(hash)
.and_modify(|x| {
x.insert(index);
})
.or_insert({
let mut set = HashSet::new();
set.insert(index);
set
});
}
}
//TODO ::push_many([x, y])
pub fn insert_key(&mut self, key: KeyType<'a>, index: usize) {
if index >= self.values.len() {
panic!("Index out of bounds");
}
self.keys.insert(key, index);
}
//TODO ::insert_keyset([Long, Key2])
pub fn insert_key_by_name(&mut self, key: KeyType<'a>, name: &str) {
let index = self
.value_index
.iter()
.position(|x| x.name == name)
.expect("No such name found");
self.keys.insert(key, index);
}
pub fn get(&self, key: KeyType<'a>) -> &Arg<'a, 'b> {
self.keys
.get(&key)
.and_then(|&idx| self.value_index.get(idx))
.expect(&format!("No entry for the key: {:?}", key))
}
//TODO ::get_first([KeyA, KeyB])
pub fn get_mut(&mut self, key: KeyType<'a>) -> &mut Arg<'a, 'b> {
let idx = *self
.keys
.get(&key)
.expect(&format!("No entry for the key: {:?}", key));
self.value_index.get_mut(idx).unwrap()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty() && self.values.is_empty()
}
pub fn remove(&mut self, key: KeyType) -> Option<Arg> {
unimplemented!()
}
//TODO ::remove_many([KeyA, KeyB])
//? probably shouldn't add a possibility for removal?
//? or remove by replacement by some dummy object, so the order is preserved
pub fn remove_key(&mut self, key: KeyType) {
unimplemented!()
}
//TODO ::remove_keys([KeyA, KeyB])
pub fn keys(&'a self) -> Keys<'a, usize> {
Keys {
iter: self.keys.keys(),
}
}
pub fn values(&'a self) -> Values<'a, Arg> {
Values {
iter: self.value_index.iter(),
}
}
pub fn values_mut(&'a mut self) -> ValuesMut<'a, Arg> {
ValuesMut {
iter: self.value_index.iter_mut(),
}
}
}
pub struct Keys<'a, V: 'a> {
iter: hash_map::Keys<'a, KeyType<'a>, V>,
}
impl<'a, V> Iterator for Keys<'a, V> {
type Item = &'a KeyType<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
pub struct Values<'a, V: 'a> {
iter: slice::Iter<'a, V>,
}
impl<'a, V> Iterator for Values<'a, V> {
type Item = &'a V;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
pub struct ValuesMut<'a, V: 'a> {
iter: slice::IterMut<'a, V>,
}
impl<'a, V> Iterator for ValuesMut<'a, V> {
type Item = &'a V;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::OsStr;
use self::KeyType::*;
#[test]
fn get_some_value() {
let mut map: MKeyMap = MKeyMap::new();
{
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
}
assert_eq!(
map.get(Long(&OsStr::new("One"))),
&Arg::with_name("Value1")
);
}
#[test]
#[should_panic]
fn get_none_value() {
let mut map: MKeyMap = MKeyMap::new();
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.get(Long(&OsStr::new("Two")));
}
// #[test]
// fn insert_delete_value() {
// let mut map = MKeyMap::new();
// map.insert("One", clap::Arg::with_name("Value1"));
// assert_eq!(map.remove("One"), Some(clap::Arg::with_name("Value1")));
// assert!(map.is_empty());
// }
#[test]
fn insert_duplicate_key() {
let mut map: MKeyMap = MKeyMap::new();
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
assert_eq!(
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value2")),
1
);
}
#[test]
fn insert_duplicate_value() {
let mut map: MKeyMap = MKeyMap::new();
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
let orig_len = map.values.len();
map.insert(Long(&OsStr::new("Two")), Arg::with_name("Value1"));
assert_eq!(map.values.len(), orig_len);
assert_eq!(
map.get(Long(&OsStr::new("One"))),
map.get(Long(&OsStr::new("Two")))
);
}
// #[test]
// fn insert_delete_none() {
// let mut map = MKeyMap::new();
// map.insert("One", clap::Arg::with_name("Value1"));
// assert_eq!(map.remove("Two"), None);
// assert!(!map.is_empty());
// assert_eq!(map.get("One"), Some(clap::Arg::with_name("Value1")));
// }
#[test]
fn insert_multiple_keys() {
let mut map: MKeyMap = MKeyMap::new();
let index = map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.insert_key(Long(&OsStr::new("Two")), index);
assert_eq!(
map.get(Long(&OsStr::new("One"))),
map.get(Long(&OsStr::new("Two")))
);
assert_eq!(map.values.len(), 1);
}
#[test]
fn insert_by_name() {
let mut map: MKeyMap = MKeyMap::new();
let index = map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.insert_key_by_name(Long(&OsStr::new("Two")), "Value1");
assert_eq!(
map.get(Long(&OsStr::new("One"))),
map.get(Long(&OsStr::new("Two")))
);
assert_eq!(map.values.len(), 1);
}
#[test]
fn get_mutable(){
let mut map: MKeyMap = MKeyMap::new();
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
assert_eq!(
map.get_mut(Long(&OsStr::new("One"))),
&mut Arg::with_name("Value1")
);
}
#[test]
fn remove_key() {
let mut map: MKeyMap = MKeyMap::new();
let index = map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.insert_key(Long(&OsStr::new("Two")), index);
map.remove_key(Long(&OsStr::new("One")));
assert_eq!(map.keys.len(), 1);
assert_eq!(map.values.len(), 1);
}
#[test]
fn iter_keys() {
let mut map: MKeyMap = MKeyMap::new();
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.insert(Long(&OsStr::new("Two")), Arg::with_name("Value2"));
map.insert(Position(1), Arg::with_name("Value1"));
let iter = map.keys().cloned();
let mut ground_truth = HashSet::new();
ground_truth.insert(Long(&OsStr::new("One")));
ground_truth.insert(Long(&OsStr::new("Two")));
ground_truth.insert(Position(1));
assert_eq!(
ground_truth.symmetric_difference(&iter.collect()).count(),
0
);
}
}

View file

@ -38,6 +38,9 @@ use parse::{ArgMatcher, SubCommand};
use util::OsStrExt2;
use INVALID_UTF8;
use INTERNAL_ERROR_MSG;
use parse::features::suggestions;
use output::Usage;
use mkeymap::KeyType;
#[derive(Debug, PartialEq, Copy, Clone)]
#[doc(hidden)]
@ -303,7 +306,22 @@ where
pub(crate) fn _build(&mut self) {
debugln!("Parser::_build;");
for a in &mut self.app.args {
for (i, a) in self.app.args.values_mut().enumerate() {
if let Some(index) = a.index {
self.app.args.insert_key(KeyType::Positional(index), i);
} else {
if let Some(c) = a.short {
self.app.args.insert_key(KeyType::Short(c), i);
}
if let Some(l) = a.long {
self.app.args.insert_key(KeyType::Long(&OsStr::new(l)), i);
}
if let Some(v) = a.aliases {
for (item, _) in &v {
self.app.args.insert_key(KeyType::Long(&OsStr::new(item)), i);
}
}
}
// Add conditional requirements
if let Some(ref r_ifs) = a.r_ifs {
for &(arg, val) in r_ifs {
@ -1012,8 +1030,9 @@ where
sdebugln!("No");
full_arg.trim_left_matches(b'-')
};
if let Some(opt) = find_by_long!(self.app, arg, opts) {
// opts?? Should probably now check once, then check whether it's opt or flag, or sth else
if let Some(opt) = self.app.args.get(KeyType::Long(arg))
{
debugln!(
"Parser::parse_long_arg: Found valid opt '{}'",
opt.to_string()
@ -1025,7 +1044,8 @@ where
}
return Ok(ret);
} else if let Some(flag) = find_by_long!(self.app, arg, flags) {
//flags??
} else if let Some(flag) = self.app.args.get(KeyType::Long(arg)) {
debugln!(
"Parser::parse_long_arg: Found valid flag '{}'",
flag.to_string()
@ -1092,7 +1112,7 @@ where
// concatenated value: -oval
// Option: -o
// Value: val
if let Some(opt) = find_by_short!(self.app, c, opts) {
if let Some(opt) = self.app.args.get(KeyType::Short(c)) {
debugln!("Parser::parse_short_arg:iter:{}: Found valid opt", c);
self.app.settings.set(AS::ValidArgFound);
// Check for trailing concatenated value
@ -1124,7 +1144,7 @@ where
}
return Ok(ret);
} else if let Some(flag) = find_by_short!(self.app, c, flags) {
} else if let Some(flag) = self.app.args.get(KeyType::Short(c)) {
debugln!("Parser::parse_short_arg:iter:{}: Found valid flag", c);
self.app.settings.set(AS::ValidArgFound);
// Only flags can be help or version
@ -1510,11 +1530,11 @@ where
// Add the arg to the matches to build a proper usage string
if let Some(name) = suffix.1 {
if let Some(opt) = find_by_long!(self.app, name) {
if let Some(opt) = self.app.args.get(KeyType::Long(name)) {
self.groups_for_arg(&*opt.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
matcher.insert(&*opt.name);
} else if let Some(flg) = find_by_long!(self.app, name) {
} else if let Some(flg) = self.app.args.get(KeyType::Long(name)) {
self.groups_for_arg(&*flg.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
matcher.insert(&*flg.name);