mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
Moving error messages definitions to Error.h
This commit is contained in:
parent
04e01d2b70
commit
c1fb53f00d
@ -369,7 +369,7 @@ class App {
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
if(opt->get_positional())
|
||||
throw IncorrectConstruction("Flags cannot be positional");
|
||||
throw IncorrectConstruction::PositionalFlag(name);
|
||||
opt->set_custom_option("", 0);
|
||||
return opt;
|
||||
}
|
||||
@ -389,7 +389,7 @@ class App {
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
if(opt->get_positional())
|
||||
throw IncorrectConstruction("Flags cannot be positional");
|
||||
throw IncorrectConstruction::PositionalFlag(name);
|
||||
opt->set_custom_option("", 0);
|
||||
return opt;
|
||||
}
|
||||
@ -409,7 +409,7 @@ class App {
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
if(opt->get_positional())
|
||||
throw IncorrectConstruction("Flags cannot be positional");
|
||||
throw IncorrectConstruction::PositionalFlag(name);
|
||||
opt->set_custom_option("", 0);
|
||||
opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
|
||||
return opt;
|
||||
@ -428,7 +428,7 @@ class App {
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
if(opt->get_positional())
|
||||
throw IncorrectConstruction("Flags cannot be positional");
|
||||
throw IncorrectConstruction::PositionalFlag(name);
|
||||
opt->set_custom_option("", 0);
|
||||
return opt;
|
||||
}
|
||||
@ -1100,7 +1100,7 @@ class App {
|
||||
std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_);
|
||||
while(!values.empty()) {
|
||||
if(!_parse_ini(values)) {
|
||||
throw ExtrasINIError(values.back().fullname);
|
||||
throw INIError::Extras(values.back().fullname);
|
||||
}
|
||||
}
|
||||
} catch(const FileError &) {
|
||||
@ -1149,8 +1149,7 @@ class App {
|
||||
if(opt->get_required() || opt->count() != 0) {
|
||||
// Make sure enough -N arguments parsed (+N is already handled in parsing function)
|
||||
if(opt->get_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_expected()))
|
||||
throw ArgumentMismatch(opt->single_name() + ": At least " + std::to_string(-opt->get_expected()) +
|
||||
" required");
|
||||
throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_expected());
|
||||
|
||||
// Required but empty
|
||||
if(opt->get_required() && opt->count() == 0)
|
||||
@ -1167,10 +1166,8 @@ class App {
|
||||
}
|
||||
|
||||
auto selected_subcommands = get_subcommands();
|
||||
if(require_subcommand_min_ > 0 && selected_subcommands.empty())
|
||||
throw RequiredError("Subcommand required");
|
||||
else if(require_subcommand_min_ > selected_subcommands.size())
|
||||
throw RequiredError("Requires at least " + std::to_string(require_subcommand_min_) + " subcommands");
|
||||
if(require_subcommand_min_ > selected_subcommands.size())
|
||||
throw RequiredError::Subcommand(require_subcommand_min_);
|
||||
|
||||
// Convert missing (pairs) to extras (string only)
|
||||
if(!(allow_extras_ || prefix_command_)) {
|
||||
@ -1210,6 +1207,9 @@ class App {
|
||||
// Let's not go crazy with pointer syntax
|
||||
Option_p &op = *op_ptr;
|
||||
|
||||
if(!op->get_configurable())
|
||||
throw INIError::NotConfigurable(current.fullname);
|
||||
|
||||
if(op->results_.empty()) {
|
||||
// Flag parsing
|
||||
if(op->get_expected() == 0) {
|
||||
@ -1226,10 +1226,10 @@ class App {
|
||||
for(size_t i = 0; i < ui; i++)
|
||||
op->results_.emplace_back("");
|
||||
} catch(const std::invalid_argument &) {
|
||||
throw ConversionError(current.fullname + ": Should be true/false or a number");
|
||||
throw ConversionError::TrueFalse(current.fullname);
|
||||
}
|
||||
} else
|
||||
throw ConversionError(current.fullname + ": too many inputs for a flag");
|
||||
throw ConversionError::TooManyInputsFlag(current.fullname);
|
||||
} else {
|
||||
op->results_ = current.inputs;
|
||||
op->run_callback();
|
||||
@ -1424,8 +1424,7 @@ class App {
|
||||
}
|
||||
|
||||
if(num > 0) {
|
||||
throw ArgumentMismatch(op->single_name() + ": " + std::to_string(num) + " required " +
|
||||
op->get_type_name() + " missing");
|
||||
throw ArgumentMismatch::TypedAtLeast(op->single_name(), num, op->get_type_name());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,14 +35,14 @@ enum class ExitCodes {
|
||||
IncorrectConstruction = 100,
|
||||
BadNameString,
|
||||
OptionAlreadyAdded,
|
||||
File,
|
||||
FileError,
|
||||
ConversionError,
|
||||
ValidationError,
|
||||
RequiredError,
|
||||
RequiresError,
|
||||
ExcludesError,
|
||||
ExtrasError,
|
||||
ExtrasINIError,
|
||||
INIError,
|
||||
InvalidError,
|
||||
HorribleError,
|
||||
OptionNotFound,
|
||||
@ -85,18 +85,50 @@ class ConstructionError : public Error {
|
||||
class IncorrectConstruction : public ConstructionError {
|
||||
CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)
|
||||
CLI11_ERROR_SIMPLE(IncorrectConstruction)
|
||||
static IncorrectConstruction PositionalFlag(std::string name) {
|
||||
return IncorrectConstruction(name + ": Flags cannot be positional");}
|
||||
static IncorrectConstruction Set0Opt(std::string name) {
|
||||
return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");}
|
||||
static IncorrectConstruction ChangeNotVector(std::string name) {
|
||||
return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");}
|
||||
static IncorrectConstruction AfterMultiOpt(std::string name) {
|
||||
return IncorrectConstruction(name + ": You can't change expected arguments after you've changed the multi option policy!");}
|
||||
static IncorrectConstruction MissingOption(std::string name) {
|
||||
return IncorrectConstruction("Option " + name + " is not defined");}
|
||||
static IncorrectConstruction MultiOptionPolicy(std::string name) {
|
||||
return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options");}
|
||||
|
||||
};
|
||||
|
||||
/// Thrown on construction of a bad name
|
||||
class BadNameString : public ConstructionError {
|
||||
CLI11_ERROR_DEF(ConstructionError, BadNameString)
|
||||
CLI11_ERROR_SIMPLE(BadNameString)
|
||||
static BadNameString OneCharName(std::string name) {
|
||||
return BadNameString("Invalid one char name: " + name);
|
||||
}
|
||||
static BadNameString BadLongName(std::string name) {
|
||||
return BadNameString("Bad long name: " + name);
|
||||
}
|
||||
static BadNameString DashesOnly(std::string name) {
|
||||
return BadNameString("Must have a name, not just dashes: " + name);
|
||||
}
|
||||
static BadNameString MultiPositionalNames(std::string name) {
|
||||
return BadNameString("Only one positional name allowed, remove: " + name);
|
||||
}
|
||||
};
|
||||
|
||||
/// Thrown when an option already exists
|
||||
class OptionAlreadyAdded : public ConstructionError {
|
||||
CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
|
||||
CLI11_ERROR_SIMPLE(OptionAlreadyAdded)
|
||||
OptionAlreadyAdded(std::string name)
|
||||
: OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
|
||||
static OptionAlreadyAdded Requires(std::string name, std::string other) {
|
||||
return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
|
||||
}
|
||||
static OptionAlreadyAdded Excludes(std::string name, std::string other) {
|
||||
return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded);
|
||||
}
|
||||
};
|
||||
|
||||
// Parsing errors
|
||||
@ -129,7 +161,8 @@ class RuntimeError : public ParseError {
|
||||
/// Thrown when parsing an INI file and it is missing
|
||||
class FileError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, FileError)
|
||||
FileError(std::string name) : FileError(name + " was not readable (missing?)", ExitCodes::File) {}
|
||||
CLI11_ERROR_SIMPLE(FileError)
|
||||
static FileError Missing(std::string name) {return FileError(name + " was not readable (missing?)");}
|
||||
};
|
||||
|
||||
/// Thrown when conversion call back fails, such as when an int fails to coerce to a string
|
||||
@ -138,6 +171,12 @@ class ConversionError : public ParseError {
|
||||
CLI11_ERROR_SIMPLE(ConversionError)
|
||||
ConversionError(std::string member, std::string name)
|
||||
: ConversionError("The value " + member + "is not an allowed value for " + name) {}
|
||||
ConversionError(std::string name, std::vector<std::string> results)
|
||||
: ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
|
||||
static ConversionError TooManyInputsFlag(std::string name) {
|
||||
return ConversionError(name + ": too many inputs for a flag");}
|
||||
static ConversionError TrueFalse(std::string name) {
|
||||
return ConversionError(name + ": Should be true/false or a number");}
|
||||
};
|
||||
|
||||
/// Thrown when validation of results fails
|
||||
@ -150,7 +189,15 @@ class ValidationError : public ParseError {
|
||||
/// Thrown when a required option is missing
|
||||
class RequiredError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, RequiredError)
|
||||
CLI11_ERROR_SIMPLE(RequiredError)
|
||||
RequiredError(std::string name) : RequiredError(name + " is required") {}
|
||||
static RequiredError Subcommand(size_t min_subcom) {
|
||||
if(min_subcom == 1)
|
||||
return RequiredError("A subcommand");
|
||||
else
|
||||
return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/// Thrown when the wrong number of arguments has been received
|
||||
@ -163,6 +210,13 @@ class ArgumentMismatch : public ParseError {
|
||||
: ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
|
||||
", got " + std::to_string(recieved)),
|
||||
ExitCodes::ArgumentMismatch) {}
|
||||
|
||||
static ArgumentMismatch AtLeast(std::string name, int num) {
|
||||
return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required");}
|
||||
static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
|
||||
return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/// Thrown when a requires option is missing
|
||||
@ -190,9 +244,12 @@ class ExtrasError : public ParseError {
|
||||
};
|
||||
|
||||
/// Thrown when extra values are found in an INI file
|
||||
class ExtrasINIError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, ExtrasINIError)
|
||||
ExtrasINIError(std::string item) : ExtrasINIError("INI was not able to parse " + item, ExitCodes::ExtrasINIError) {}
|
||||
class INIError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, INIError)
|
||||
CLI11_ERROR_SIMPLE(INIError)
|
||||
static INIError Extras(std::string item) {return INIError("INI was not able to parse " + item);}
|
||||
static INIError NotConfigurable(std::string item) {return INIError(item + ": This option is not allowed in a configuration file");}
|
||||
|
||||
};
|
||||
|
||||
/// Thrown when validation fails before parsing
|
||||
@ -204,6 +261,7 @@ class InvalidError : public ParseError {
|
||||
};
|
||||
|
||||
/// This is just a safety check to verify selection and parsing match - you should not ever see it
|
||||
/// Strings are directly added to this error, but again, it should never be seen.
|
||||
class HorribleError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, HorribleError)
|
||||
CLI11_ERROR_SIMPLE(HorribleError)
|
||||
|
@ -106,7 +106,7 @@ inline std::vector<ini_ret_t> parse_ini(const std::string &name) {
|
||||
|
||||
std::ifstream input{name};
|
||||
if(!input.good())
|
||||
throw FileError(name);
|
||||
throw FileError::Missing(name);
|
||||
|
||||
return parse_ini(input);
|
||||
}
|
||||
|
@ -41,8 +41,12 @@ template <typename CRTP> class OptionBase {
|
||||
/// Ignore the case when matching (option, not value)
|
||||
bool ignore_case_{false};
|
||||
|
||||
/// Allow this option to be given in a configuration file
|
||||
bool configurable_{true};
|
||||
|
||||
/// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too)
|
||||
MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
|
||||
|
||||
|
||||
template <typename T> void copy_to(T *other) const {
|
||||
other->group(group_);
|
||||
@ -80,6 +84,9 @@ template <typename CRTP> class OptionBase {
|
||||
|
||||
/// The status of ignore case
|
||||
bool get_ignore_case() const { return ignore_case_; }
|
||||
|
||||
/// The status of configurable
|
||||
bool get_configurable() const { return configurable_; }
|
||||
|
||||
/// The status of the multi option policy
|
||||
MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
|
||||
@ -106,6 +113,12 @@ template <typename CRTP> class OptionBase {
|
||||
self->multi_option_policy(MultiOptionPolicy::Join);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Allow in a configuration file
|
||||
CRTP *configurable(bool value = true) {
|
||||
configurable_ = value;
|
||||
return static_cast<CRTP *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
class OptionDefaults : public OptionBase<OptionDefaults> {
|
||||
@ -235,12 +248,11 @@ class Option : public OptionBase<Option> {
|
||||
if(expected_ == value)
|
||||
return this;
|
||||
else if(value == 0)
|
||||
throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
|
||||
throw IncorrectConstruction::Set0Opt(single_name());
|
||||
else if(!changeable_)
|
||||
throw IncorrectConstruction("You can only change the expected arguments for vectors");
|
||||
throw IncorrectConstruction::ChangeNotVector(single_name());
|
||||
else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
|
||||
throw IncorrectConstruction(
|
||||
"You can't change expected arguments after you've changed the multi option policy!");
|
||||
throw IncorrectConstruction::AfterMultiOpt(single_name());
|
||||
|
||||
expected_ = value;
|
||||
return this;
|
||||
@ -269,7 +281,7 @@ class Option : public OptionBase<Option> {
|
||||
Option *requires(Option *opt) {
|
||||
auto tup = requires_.insert(opt);
|
||||
if(!tup.second)
|
||||
throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name());
|
||||
throw OptionAlreadyAdded::Requires(get_name(), opt->get_name());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -278,7 +290,7 @@ class Option : public OptionBase<Option> {
|
||||
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
|
||||
if(opt.get() != this && opt->check_name(opt_name))
|
||||
return requires(opt.get());
|
||||
throw IncorrectConstruction("Option " + opt_name + " is not defined");
|
||||
throw IncorrectConstruction::MissingOption(opt_name);
|
||||
}
|
||||
|
||||
/// Any number supported, any mix of string and Opt
|
||||
@ -291,7 +303,7 @@ class Option : public OptionBase<Option> {
|
||||
Option *excludes(Option *opt) {
|
||||
auto tup = excludes_.insert(opt);
|
||||
if(!tup.second)
|
||||
throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name());
|
||||
throw OptionAlreadyAdded::Excludes(get_name(), opt->get_name());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -300,7 +312,7 @@ class Option : public OptionBase<Option> {
|
||||
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
|
||||
if(opt.get() != this && opt->check_name(opt_name))
|
||||
return excludes(opt.get());
|
||||
throw IncorrectConstruction("Option " + opt_name + " is not defined");
|
||||
throw IncorrectConstruction::MissingOption(opt_name);
|
||||
}
|
||||
/// Any number supported, any mix of string and Opt
|
||||
template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
|
||||
@ -324,7 +336,7 @@ class Option : public OptionBase<Option> {
|
||||
|
||||
for(const Option_p &opt : parent->options_)
|
||||
if(opt.get() != this && *opt == *this)
|
||||
throw OptionAlreadyAdded(opt->get_name() + " is already added");
|
||||
throw OptionAlreadyAdded(opt->get_name());
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -332,7 +344,7 @@ class Option : public OptionBase<Option> {
|
||||
/// Take the last argument if given multiple times
|
||||
Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
|
||||
if(get_expected() != 0 && get_expected() != 1)
|
||||
throw IncorrectConstruction("multi_option_policy only works for flags and single value options!");
|
||||
throw IncorrectConstruction::MultiOptionPolicy(single_name());
|
||||
multi_option_policy_ = value;
|
||||
return this;
|
||||
}
|
||||
@ -480,7 +492,7 @@ class Option : public OptionBase<Option> {
|
||||
}
|
||||
|
||||
if(local_result)
|
||||
throw ConversionError("Could not convert: " + get_name() + "=" + detail::join(results_));
|
||||
throw ConversionError(get_name(), results_);
|
||||
}
|
||||
|
||||
/// If options share any of the same names, they are equal (not counting positional)
|
||||
|
@ -66,18 +66,18 @@ get_names(const std::vector<std::string> &input) {
|
||||
if(name.length() == 2 && valid_first_char(name[1]))
|
||||
short_names.emplace_back(1, name[1]);
|
||||
else
|
||||
throw BadNameString("Invalid one char name: " + name);
|
||||
throw BadNameString::OneCharName(name);
|
||||
} else if(name.length() > 2 && name.substr(0, 2) == "--") {
|
||||
name = name.substr(2);
|
||||
if(valid_name_string(name))
|
||||
long_names.push_back(name);
|
||||
else
|
||||
throw BadNameString("Bad long name: " + name);
|
||||
throw BadNameString::BadLongName(name);
|
||||
} else if(name == "-" || name == "--") {
|
||||
throw BadNameString("Must have a name, not just dashes");
|
||||
throw BadNameString::DashesOnly(name);
|
||||
} else {
|
||||
if(pos_name.length() > 0)
|
||||
throw BadNameString("Only one positional name allowed, remove: " + name);
|
||||
throw BadNameString::MultiPositionalNames(name);
|
||||
pos_name = name;
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ TEST_F(TApp, IniFailure) {
|
||||
out << "val=1" << std::endl;
|
||||
}
|
||||
|
||||
EXPECT_THROW(run(), CLI::ExtrasINIError);
|
||||
EXPECT_THROW(run(), CLI::INIError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniSubFailure) {
|
||||
@ -392,7 +392,7 @@ TEST_F(TApp, IniSubFailure) {
|
||||
out << "val=1" << std::endl;
|
||||
}
|
||||
|
||||
EXPECT_THROW(run(), CLI::ExtrasINIError);
|
||||
EXPECT_THROW(run(), CLI::INIError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniNoSubFailure) {
|
||||
@ -407,7 +407,7 @@ TEST_F(TApp, IniNoSubFailure) {
|
||||
out << "val=1" << std::endl;
|
||||
}
|
||||
|
||||
EXPECT_THROW(run(), CLI::ExtrasINIError);
|
||||
EXPECT_THROW(run(), CLI::INIError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniFlagConvertFailure) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user