9ef3800986
- complete with ctest support
707 Zeilen
23 KiB
C++
707 Zeilen
23 KiB
C++
// MIT License
|
|
//
|
|
// Copyright (c) 2020, The Regents of the University of California,
|
|
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
|
// required approvals from the U.S. Dept. of Energy). All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#ifndef TIMEMORY_UTILITY_ARGPARSE_CPP_
|
|
#define TIMEMORY_UTILITY_ARGPARSE_CPP_
|
|
|
|
#include "timemory/utility/macros.hpp"
|
|
|
|
#if !defined(TIMEMORY_UTILITY_HEADER_MODE)
|
|
# include "timemory/utility/argparse.hpp"
|
|
#endif
|
|
|
|
namespace tim
|
|
{
|
|
namespace argparse
|
|
{
|
|
TIMEMORY_UTILITY_INLINE
|
|
argument_vector::argument_vector(int& argc, char**& argv)
|
|
: base_type()
|
|
{
|
|
reserve(argc);
|
|
for(int i = 0; i < argc; ++i)
|
|
push_back(argv[i]);
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE
|
|
argument_vector::argument_vector(int& argc, const char**& argv)
|
|
: base_type()
|
|
{
|
|
reserve(argc);
|
|
for(int i = 0; i < argc; ++i)
|
|
push_back(argv[i]);
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE
|
|
argument_vector::argument_vector(int& argc, const char* const*& argv)
|
|
{
|
|
reserve(argc);
|
|
for(int i = 0; i < argc; ++i)
|
|
push_back(argv[i]);
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_vector::cargs_t
|
|
argument_vector::get_execv(const base_type& _prepend, size_t _beg, size_t _end) const
|
|
{
|
|
std::stringstream cmdss;
|
|
// find the end if not specified
|
|
_end = std::min<size_t>(size(), _end);
|
|
// determine the number of arguments
|
|
auto _argc = (_end - _beg) + _prepend.size();
|
|
// create the new C argument array, add an extra entry at the end which will
|
|
// always be a null pointer because that is how execv determines the end
|
|
char** _argv = new char*[_argc + 1];
|
|
|
|
// ensure all arguments are null pointers initially
|
|
for(size_t i = 0; i < _argc + 1; ++i)
|
|
_argv[i] = nullptr;
|
|
|
|
// add the prepend list
|
|
size_t _idx = 0;
|
|
for(const auto& itr : _prepend)
|
|
_argv[_idx++] = helpers::strdup(itr.c_str());
|
|
|
|
// copy over the arguments stored internally from the range specified
|
|
for(auto i = _beg; i < _end; ++i)
|
|
_argv[_idx++] = helpers::strdup(this->at(i).c_str());
|
|
|
|
// add check that last argument really is a nullptr
|
|
assert(_argv[_argc] == nullptr);
|
|
|
|
// create the command string
|
|
for(size_t i = 0; i < _argc; ++i)
|
|
cmdss << " " << _argv[i];
|
|
auto cmd = cmdss.str().substr(1);
|
|
|
|
// return a new (int argc, char** argv) and subtract 1 bc nullptr in last entry
|
|
// does not count as argc
|
|
return cargs_t(_argc - 1, _argv, cmd);
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_vector::cargs_t
|
|
argument_vector::get_execv(size_t _beg, size_t _end) const
|
|
{
|
|
return get_execv(base_type{}, _beg, _end);
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE void
|
|
argument_parser::print_help(const std::string& _extra)
|
|
{
|
|
std::stringstream _usage;
|
|
if(!m_desc.empty())
|
|
_usage << "[" << m_desc << "] ";
|
|
_usage << "Usage: " << m_bin;
|
|
|
|
std::cerr << _usage.str();
|
|
|
|
std::stringstream _sshort_desc;
|
|
auto _indent = _usage.str().length() + 2;
|
|
size_t _ncnt = 0;
|
|
for(auto& a : m_arguments)
|
|
{
|
|
std::string name = a.m_names.at(0);
|
|
if(name.empty() || name.find_first_of('-') > name.find_first_not_of(" -"))
|
|
continue;
|
|
// select the first long option
|
|
for(size_t n = 1; n < a.m_names.size(); ++n)
|
|
{
|
|
if(name.find("--") == 0)
|
|
break;
|
|
else if(a.m_names.at(n).find("--") == 0)
|
|
{
|
|
name = a.m_names.at(n);
|
|
break;
|
|
}
|
|
}
|
|
if(name.length() > 0)
|
|
{
|
|
if(_ncnt++ > 0)
|
|
_sshort_desc << "\n " << std::setw(_indent) << " " << name;
|
|
else
|
|
_sshort_desc << " " << name;
|
|
|
|
_sshort_desc << " (";
|
|
if(a.m_count != argument::Count::ANY)
|
|
_sshort_desc << "count: " << a.m_count;
|
|
else if(a.m_min_count != argument::Count::ANY)
|
|
_sshort_desc << "min: " << a.m_min_count;
|
|
else if(a.m_max_count != argument::Count::ANY)
|
|
_sshort_desc << "max: " << a.m_max_count;
|
|
else
|
|
_sshort_desc << "count: unlimited";
|
|
if(!a.m_dtype.empty())
|
|
_sshort_desc << ", dtype: " << a.m_dtype;
|
|
else if(a.m_count == 0 ||
|
|
(a.m_count == argument::Count::ANY && a.m_max_count == 1))
|
|
_sshort_desc << ", dtype: bool" << a.m_dtype;
|
|
_sshort_desc << ")";
|
|
}
|
|
}
|
|
|
|
std::string _short_desc;
|
|
if(!_sshort_desc.str().empty())
|
|
{
|
|
_short_desc.append("[" + _sshort_desc.str());
|
|
std::stringstream _tmp;
|
|
_tmp << "\n" << std::setw(_indent) << "]";
|
|
_short_desc.append(_tmp.str());
|
|
}
|
|
|
|
if(m_positional_arguments.empty())
|
|
{
|
|
std::cerr << " " << _short_desc << " " << _extra << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << " " << _short_desc;
|
|
if(!_short_desc.empty())
|
|
std::cerr << "\n" << std::setw(_indent - 2) << " ";
|
|
for(auto& itr : m_positional_arguments)
|
|
{
|
|
std::cerr << " " << helpers::ltrim(itr.m_names.at(0), [](int c) -> bool {
|
|
return c != static_cast<int>('-');
|
|
});
|
|
}
|
|
|
|
int current = 0;
|
|
for(auto& v : m_positional_map)
|
|
{
|
|
if(v.first != argument::Position::LastArgument)
|
|
{
|
|
for(; current < v.first; ++current)
|
|
std::cerr << " [" << current << "]";
|
|
std::cerr << " ["
|
|
<< helpers::ltrim(
|
|
m_arguments[static_cast<size_t>(v.second)].m_names.at(0),
|
|
[](int c) -> bool { return c != static_cast<int>('-'); })
|
|
<< "]";
|
|
}
|
|
else
|
|
{
|
|
std::cerr << " ... ["
|
|
<< helpers::ltrim(
|
|
m_arguments[static_cast<size_t>(v.second)].m_names.at(0),
|
|
[](int c) -> bool { return c != static_cast<int>('-'); })
|
|
<< "]";
|
|
}
|
|
}
|
|
std::cerr << " " << _extra << std::endl;
|
|
}
|
|
|
|
std::cerr << "\nOptions:" << std::endl;
|
|
for(auto& a : m_arguments)
|
|
{
|
|
std::string name = a.m_names.at(0);
|
|
for(size_t n = 1; n < a.m_names.size(); ++n)
|
|
name.append(", " + a.m_names[n]);
|
|
std::stringstream ss;
|
|
ss << name;
|
|
if(a.m_choices.size() > 0)
|
|
{
|
|
ss << " [";
|
|
auto itr = a.m_choices.begin();
|
|
ss << " " << *itr++;
|
|
for(; itr != a.m_choices.end(); ++itr)
|
|
ss << " | " << *itr;
|
|
ss << " ] ";
|
|
}
|
|
std::stringstream prefix;
|
|
prefix << " " << std::setw(m_width) << std::left << ss.str();
|
|
std::cerr << std::left << prefix.str();
|
|
|
|
auto desc = a.m_desc;
|
|
if(ss.str().length() >= static_cast<size_t>(m_width))
|
|
desc = std::string("\n%{NEWLINE}%") + desc;
|
|
|
|
{
|
|
// replace %{INDENT}% with indentation
|
|
const std::string indent_key = "%{INDENT}%";
|
|
const auto npos = std::string::npos;
|
|
auto pos = npos;
|
|
std::stringstream indent;
|
|
indent << std::setw(prefix.str().length()) << "";
|
|
while((pos = desc.find(indent_key)) != npos)
|
|
desc = desc.replace(pos, indent_key.length(), indent.str());
|
|
}
|
|
|
|
{
|
|
// replace %{NEWLINE}% with indentation
|
|
const std::string indent_key = "%{NEWLINE}%";
|
|
const auto npos = std::string::npos;
|
|
auto pos = npos;
|
|
std::stringstream indent;
|
|
indent << std::setw(m_width + 5) << "";
|
|
while((pos = desc.find(indent_key)) != npos)
|
|
desc = desc.replace(pos, indent_key.length(), indent.str());
|
|
}
|
|
|
|
std::cerr << " " << std::setw(m_width) << desc;
|
|
|
|
if(a.m_required)
|
|
std::cerr << " (Required)";
|
|
std::cerr << std::endl;
|
|
}
|
|
std::cerr << '\n';
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::arg_result
|
|
argument_parser::parse_known_args(int* argc, char*** argv, strvec_t& _args,
|
|
const std::string& _delim, int verbose_level)
|
|
{
|
|
// check for help flag
|
|
auto help_check = [&](int _argc, char** _argv) {
|
|
strset_t help_args = { "-h", "--help", "-?" };
|
|
auto _help_req = (exists("help") ||
|
|
(_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
|
|
if(_help_req && !exists("help"))
|
|
{
|
|
for(auto hitr : help_args)
|
|
{
|
|
auto hstr = hitr.substr(hitr.find_first_not_of('-'));
|
|
auto itr = m_name_map.find(hstr);
|
|
if(itr != m_name_map.end())
|
|
m_arguments[static_cast<size_t>(itr->second)].m_found = true;
|
|
}
|
|
}
|
|
return _help_req;
|
|
};
|
|
|
|
// check for a dash in th command line
|
|
bool _pdash = false;
|
|
for(int i = 1; i < *argc; ++i)
|
|
{
|
|
if((*argv)[i] == std::string("--"))
|
|
_pdash = true;
|
|
}
|
|
|
|
// parse the known args and get the remaining argc/argv
|
|
auto _pargs = parse_known_args(*argc, *argv, _args, _delim, verbose_level);
|
|
auto _perrc = std::get<0>(_pargs);
|
|
auto _pargc = std::get<1>(_pargs);
|
|
auto _pargv = std::get<2>(_pargs);
|
|
|
|
// check if help was requested before the dash (if dash exists)
|
|
if(help_check((_pdash) ? 0 : _pargc, _pargv))
|
|
return arg_result{ "help requested" };
|
|
|
|
// assign the argc and argv
|
|
*argc = _pargc;
|
|
*argv = _pargv;
|
|
|
|
return _perrc;
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::known_args_t
|
|
argument_parser::parse_known_args(int argc, char** argv, strvec_t& _args,
|
|
const std::string& _delim, int verbose_level)
|
|
{
|
|
int _cmdc = argc; // the argc after known args removed
|
|
char** _cmdv = argv; // the argv after known args removed
|
|
// _cmdv and argv are same pointer unless delimiter is found
|
|
|
|
if(argc > 0)
|
|
{
|
|
m_bin = std::string((const char*) argv[0]);
|
|
_args.push_back(std::string((const char*) argv[0]));
|
|
}
|
|
|
|
for(int i = 1; i < argc; ++i)
|
|
{
|
|
std::string _arg = argv[i];
|
|
if(_arg == _delim)
|
|
{
|
|
_cmdc = argc - i;
|
|
_cmdv = new char*[_cmdc + 1];
|
|
_cmdv[_cmdc] = nullptr;
|
|
_cmdv[0] = helpers::strdup(argv[0]);
|
|
int k = 1;
|
|
for(int j = i + 1; j < argc; ++j, ++k)
|
|
_cmdv[k] = helpers::strdup(argv[j]);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
_args.push_back(std::string((const char*) argv[i]));
|
|
}
|
|
}
|
|
|
|
auto cmd_string = [](int _ac, char** _av) {
|
|
std::stringstream ss;
|
|
for(int i = 0; i < _ac; ++i)
|
|
ss << _av[i] << " ";
|
|
return ss.str();
|
|
};
|
|
|
|
if((_cmdc > 0 && verbose_level > 0) || verbose_level > 1)
|
|
std::cerr << "\n";
|
|
|
|
if(verbose_level > 1)
|
|
{
|
|
std::cerr << "[original]> " << cmd_string(argc, argv) << std::endl;
|
|
std::cerr << "[cfg-args]> ";
|
|
for(auto& itr : _args)
|
|
std::cerr << itr << " ";
|
|
std::cerr << std::endl;
|
|
}
|
|
|
|
if(_cmdc > 0 && verbose_level > 0)
|
|
std::cerr << "[command]> " << cmd_string(_cmdc, _cmdv) << "\n\n";
|
|
|
|
return known_args_t{ parse(_args, verbose_level), _cmdc, _cmdv };
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::arg_result
|
|
argument_parser::parse(const std::vector<std::string>& _args, int verbose_level)
|
|
{
|
|
if(verbose_level > 0)
|
|
{
|
|
std::cerr << "[argparse::parse]> parsing '";
|
|
for(const auto& itr : _args)
|
|
std::cerr << itr << " ";
|
|
std::cerr << "'" << '\n';
|
|
}
|
|
|
|
for(auto& a : m_arguments)
|
|
a.m_callback(a.m_default);
|
|
for(auto& a : m_positional_arguments)
|
|
a.m_callback(a.m_default);
|
|
|
|
using argmap_t = std::map<std::string, argument*>;
|
|
|
|
argmap_t m_arg_map = {};
|
|
arg_result err;
|
|
int argc = _args.size();
|
|
// the set of options which use a single leading dash but are longer than
|
|
// one character, e.g. -LS ...
|
|
std::set<std::string> long_short_opts;
|
|
if(_args.size() > 1)
|
|
{
|
|
auto is_leading_dash = [](int c) -> bool { return c != static_cast<int>('-'); };
|
|
// build name map
|
|
for(auto& a : m_arguments)
|
|
{
|
|
for(auto& n : a.m_names)
|
|
{
|
|
auto nleading_dash = helpers::lcount(n, is_leading_dash);
|
|
std::string name = helpers::ltrim(n, is_leading_dash);
|
|
if(name.empty())
|
|
continue;
|
|
if(m_name_map.find(name) != m_name_map.end())
|
|
return arg_result("Duplicate of argument name: " + n);
|
|
m_name_map[name] = a.m_index;
|
|
m_arg_map[name] = &a;
|
|
if(nleading_dash == 1 && name.length() > 1)
|
|
long_short_opts.insert(name);
|
|
}
|
|
if(a.m_position >= 0 || a.m_position == argument::Position::LastArgument)
|
|
m_positional_map.at(a.m_position) = a.m_index;
|
|
}
|
|
|
|
m_bin = _args.at(0);
|
|
|
|
// parse
|
|
std::string current_arg;
|
|
size_t arg_len;
|
|
for(int argv_index = 1; argv_index < argc; ++argv_index)
|
|
{
|
|
current_arg = _args.at(argv_index);
|
|
arg_len = current_arg.length();
|
|
if(arg_len == 0)
|
|
continue;
|
|
if(argv_index == argc - 1 &&
|
|
m_positional_map.find(argument::Position::LastArgument) !=
|
|
m_positional_map.end())
|
|
{
|
|
err = end_argument();
|
|
arg_result b = err;
|
|
err = add_value(current_arg, argument::Position::LastArgument);
|
|
if(b)
|
|
return b;
|
|
// return (m_error_func(*this, b), b);
|
|
if(err)
|
|
return (m_error_func(*this, err), err);
|
|
continue;
|
|
}
|
|
|
|
// count number of leading dashes
|
|
auto nleading_dash = helpers::lcount(current_arg, is_leading_dash);
|
|
// ignores the case if the arg is just a '-'
|
|
// look for -a (short) or --arg (long) args
|
|
bool is_arg = (nleading_dash > 0 && arg_len > 1 && arg_len != nleading_dash)
|
|
? true
|
|
: false;
|
|
|
|
if(is_arg && !helpers::is_numeric(current_arg))
|
|
{
|
|
err = end_argument();
|
|
if(err)
|
|
return (m_error_func(*this, err), err);
|
|
|
|
auto name = current_arg.substr(nleading_dash);
|
|
auto islong = (nleading_dash > 1 || long_short_opts.count(name) > 0);
|
|
err = begin_argument(name, islong, argv_index);
|
|
if(err)
|
|
return (m_error_func(*this, err), err);
|
|
}
|
|
else if(current_arg.length() > 0)
|
|
{
|
|
// argument value
|
|
err = add_value(current_arg, argv_index);
|
|
if(err)
|
|
return (m_error_func(*this, err), err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// return the help
|
|
if(m_help_enabled && exists("help"))
|
|
return arg_result("help requested");
|
|
|
|
err = end_argument();
|
|
if(err)
|
|
return (m_error_func(*this, err), err);
|
|
|
|
// check requirements
|
|
for(auto& a : m_arguments)
|
|
{
|
|
if(a.m_required && !a.m_found)
|
|
{
|
|
return arg_result("Required argument not found: " + a.m_names.at(0));
|
|
}
|
|
if(a.m_position >= 0 && argc >= a.m_position && !a.m_found)
|
|
{
|
|
return arg_result("argument " + a.m_names.at(0) + " expected in position " +
|
|
std::to_string(a.m_position));
|
|
}
|
|
}
|
|
|
|
// check requirements
|
|
for(auto& a : m_positional_arguments)
|
|
{
|
|
if(a.m_required && !a.m_found)
|
|
return arg_result("Required argument not found: " + a.m_names.at(0));
|
|
}
|
|
|
|
// check all the counts have been satisfied
|
|
for(auto& a : m_arguments)
|
|
{
|
|
if(a.m_found && a.m_default == nullptr)
|
|
{
|
|
auto cnt_err = check_count(a);
|
|
if(cnt_err)
|
|
return cnt_err;
|
|
}
|
|
}
|
|
|
|
// execute the global actions
|
|
for(auto& itr : m_actions)
|
|
{
|
|
if(itr.first(*this))
|
|
itr.second(*this);
|
|
}
|
|
|
|
// execute the argument-specific actions
|
|
for(auto& itr : m_arg_map)
|
|
{
|
|
if(exists(itr.first))
|
|
itr.second->execute_actions(*this);
|
|
}
|
|
|
|
return arg_result{};
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::arg_result
|
|
argument_parser::begin_argument(const std::string& arg, bool longarg, int position)
|
|
{
|
|
auto it = m_positional_map.find(position);
|
|
if(it != m_positional_map.end())
|
|
{
|
|
arg_result err = end_argument();
|
|
argument& a = m_arguments[static_cast<size_t>(it->second)];
|
|
a.m_values.push_back(arg);
|
|
a.m_found = true;
|
|
return err;
|
|
}
|
|
if(m_current != -1)
|
|
{
|
|
return arg_result("Current argument left open");
|
|
}
|
|
size_t name_end = helpers::find_punct(arg);
|
|
std::string arg_name = arg.substr(0, name_end);
|
|
if(longarg)
|
|
{
|
|
int equal_pos = helpers::find_equiv(arg);
|
|
auto nmf = m_name_map.find(arg_name);
|
|
if(nmf == m_name_map.end())
|
|
{
|
|
arg_name = arg.substr(0, equal_pos);
|
|
nmf = m_name_map.find(arg_name);
|
|
}
|
|
if(nmf == m_name_map.end())
|
|
{
|
|
return arg_result("Unrecognized command line option '" + arg_name + "'");
|
|
}
|
|
m_current = nmf->second;
|
|
m_arguments[static_cast<size_t>(nmf->second)].m_found = true;
|
|
if(equal_pos == 0 || (equal_pos < 0 && arg_name.length() < arg.length()))
|
|
{
|
|
// malformed argument
|
|
return arg_result("Malformed argument: " + arg);
|
|
}
|
|
else if(equal_pos > 0)
|
|
{
|
|
std::string arg_value = arg.substr(name_end + 1);
|
|
add_value(arg_value, position);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arg_result r;
|
|
if(arg_name.length() == 1)
|
|
{
|
|
return begin_argument(arg, true, position);
|
|
}
|
|
else
|
|
{
|
|
for(char& c : arg_name)
|
|
{
|
|
r = begin_argument(std::string(1, c), true, position);
|
|
if(r)
|
|
{
|
|
return r;
|
|
}
|
|
r = end_argument();
|
|
if(r)
|
|
{
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return arg_result{};
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::arg_result
|
|
argument_parser::add_value(const std::string& value, int location)
|
|
{
|
|
auto unnamed = [&]() {
|
|
auto itr = m_positional_map.find(location);
|
|
if(itr != m_positional_map.end())
|
|
{
|
|
argument& a = m_arguments[static_cast<size_t>(itr->second)];
|
|
a.m_values.push_back(value);
|
|
a.m_found = true;
|
|
}
|
|
else
|
|
{
|
|
auto idx = m_positional_values.size();
|
|
m_positional_values.emplace(idx, value);
|
|
if(idx < m_positional_arguments.size())
|
|
{
|
|
auto& a = m_positional_arguments.at(idx);
|
|
a.m_found = true;
|
|
auto err = a.check_choice(value);
|
|
if(err)
|
|
return err;
|
|
a.m_values.push_back(value);
|
|
a.execute_actions(*this);
|
|
}
|
|
}
|
|
return arg_result{};
|
|
};
|
|
|
|
if(m_current >= 0)
|
|
{
|
|
arg_result err;
|
|
size_t c = static_cast<size_t>(m_current);
|
|
consume_parameters(c);
|
|
argument& a = m_arguments[static_cast<size_t>(m_current)];
|
|
|
|
err = a.check_choice(value);
|
|
if(err)
|
|
return err;
|
|
|
|
auto num_values = [&]() { return static_cast<int>(a.m_values.size()); };
|
|
|
|
// check {m_count, m_max_count} > COUNT::ANY && m_values.size() >= {value}
|
|
if((a.m_count >= 0 && num_values() >= a.m_count) ||
|
|
(a.m_max_count >= 0 && num_values() >= a.m_max_count))
|
|
{
|
|
err = end_argument();
|
|
if(err)
|
|
return err;
|
|
return unnamed();
|
|
}
|
|
|
|
a.m_values.push_back(value);
|
|
|
|
// check {m_count, m_max_count} > COUNT::ANY && m_values.size() >= {value}
|
|
if((a.m_count >= 0 && num_values() >= a.m_count) ||
|
|
(a.m_max_count >= 0 && num_values() >= a.m_max_count))
|
|
{
|
|
err = end_argument();
|
|
if(err)
|
|
return err;
|
|
}
|
|
return arg_result{};
|
|
}
|
|
|
|
return unnamed();
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE argument_parser::arg_result
|
|
argument_parser::end_argument()
|
|
{
|
|
if(m_current >= 0)
|
|
{
|
|
argument& a = m_arguments[static_cast<size_t>(m_current)];
|
|
m_current = -1;
|
|
if(static_cast<int>(a.m_values.size()) < a.m_count)
|
|
return arg_result("Too few arguments given for " + a.m_names.at(0));
|
|
if(a.m_max_count >= 0)
|
|
{
|
|
if(static_cast<int>(a.m_values.size()) > a.m_max_count)
|
|
return arg_result("Too many arguments given for " + a.m_names.at(0));
|
|
}
|
|
else if(a.m_count >= 0)
|
|
{
|
|
if(static_cast<int>(a.m_values.size()) > a.m_count)
|
|
return arg_result("Too many arguments given for " + a.m_names.at(0));
|
|
}
|
|
}
|
|
return arg_result{};
|
|
}
|
|
|
|
TIMEMORY_UTILITY_INLINE std::ostream&
|
|
operator<<(std::ostream& os, const argument_parser::arg_result& r)
|
|
{
|
|
os << r.what();
|
|
return os;
|
|
}
|
|
|
|
} // namespace argparse
|
|
} // namespace tim
|
|
|
|
#endif
|