diff --git a/CHANGELOG.md b/CHANGELOG.md index 0035f00..4220d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ * Ini files support flags (only read) * Ini files support subcommands (only read) * Ini files support vectors (only read) -* Added CodeCov code coverage reports, increased coverage +* Added CodeCov code coverage reports +* Lots of small bugfixes related to adding tests to increase coverage +* Error handling now uses scoped enum in errors ## Version 0.6 diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 91096c6..76ae579 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -482,7 +482,7 @@ public: /// Print a nice error message and return the exit code int exit(const Error& e) const { - if(e.exit_code != 0) { + if(e.exit_code != ErrorCodes::Success) { std::cerr << "ERROR: "; std::cerr << e.what() << std::endl; if(e.print_help) @@ -491,7 +491,7 @@ public: if(e.print_help) std::cout << help(); } - return e.exit_code; + return e.get_exit_code(); } /// Reset the parsed data @@ -757,7 +757,7 @@ protected: try { std::vector values = detail::parse_ini(config_name_); while(values.size() > 0) { - if(!_parse_env(values)) { + if(!_parse_ini(values)) { throw ExtrasError(values.back().name()); } } @@ -821,11 +821,11 @@ protected: } } - /// Parse one env param, ignore if not applicable, remove if it is + /// Parse one ini param, return false if not found in any subcommand, remove if it is /// /// If this has more than one dot.separated.name, go into the subcommand matching it /// Returns true if it managed to find the option, if false you'll need to remove the arg manully. - bool _parse_env(std::vector &args) { + bool _parse_ini(std::vector &args) { detail::ini_ret_t& current = args.back(); std::string parent = current.parent(); std::string name = current.name(); @@ -833,8 +833,9 @@ protected: current.level++; for(const App_p &com : subcommands_) if(com->check_name(parent)) - return com->_parse_env(args); + return com->_parse_ini(args); return false; + //throw CLI::ExtraINIError(current.fullname); } auto op_ptr = std::find_if(std::begin(options_), std::end(options_), diff --git a/include/CLI/Error.hpp b/include/CLI/Error.hpp index 55b0304..c3bbeb1 100644 --- a/include/CLI/Error.hpp +++ b/include/CLI/Error.hpp @@ -9,6 +9,25 @@ namespace CLI { +enum class ErrorCodes { + Success = 0, + IncorrectConstruction=100, + BadNameString, + OptionAlreadyAdded, + File, + Conversion, + Validation, + Required, + Requires, + Excludes, + Extras, + ExtrasINI, + Invalid, + Horrible, + OptionNotFound, + BaseClass = 255 +}; + // Error definitions /// @defgroup error_group Errors @@ -19,102 +38,127 @@ namespace CLI { /// All errors derive from this one struct Error : public std::runtime_error { - int exit_code; + ErrorCodes exit_code; bool print_help; - Error(std::string parent, std::string name, int exit_code=255, bool print_help=true) : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {} + int get_exit_code() const {return static_cast(exit_code);} + Error(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) + : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {} }; /// Construction errors (not in parsing) struct ConstructionError : public Error { // Using Error::Error constructors seem to not work on GCC 4.7 - ConstructionError(std::string parent, std::string name, int exit_code=255, bool print_help=true) : Error(parent, name, exit_code, print_help) {} + ConstructionError(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) + : Error(parent, name, exit_code, print_help) {} }; /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) struct IncorrectConstruction : public ConstructionError { - IncorrectConstruction(std::string name) : ConstructionError("IncorrectConstruction", name, 8) {} + IncorrectConstruction(std::string name) + : ConstructionError("IncorrectConstruction", name, ErrorCodes::IncorrectConstruction) {} }; /// Thrown on construction of a bad name struct BadNameString : public ConstructionError { - BadNameString(std::string name) : ConstructionError("BadNameString", name, 1) {} + BadNameString(std::string name) + : ConstructionError("BadNameString", name, ErrorCodes::BadNameString) {} }; /// Thrown when an option already exists struct OptionAlreadyAdded : public ConstructionError { - OptionAlreadyAdded(std::string name) : ConstructionError("OptionAlreadyAdded", name, 3) {} + OptionAlreadyAdded(std::string name) + : ConstructionError("OptionAlreadyAdded", name, ErrorCodes::OptionAlreadyAdded) {} }; // Parsing errors /// Anything that can error in Parse struct ParseError : public Error { - ParseError(std::string parent, std::string name, int exit_code=255, bool print_help=true) : Error(parent, name, exit_code, print_help) {} + ParseError(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) + : Error(parent, name, exit_code, print_help) {} }; // Not really "errors" /// This is a successful completion on parsing, supposed to exit struct Success : public ParseError { - Success() : ParseError("Success", "Successfully completed, should be caught and quit", 0, false) {} + Success() + : ParseError("Success", "Successfully completed, should be caught and quit", ErrorCodes::Success, false) {} }; /// -h or --help on command line struct CallForHelp : public ParseError { - CallForHelp() : ParseError("CallForHelp", "This should be caught in your main function, see examples", 0) {} + CallForHelp() + : ParseError("CallForHelp", "This should be caught in your main function, see examples", ErrorCodes::Success) {} }; /// Thrown when parsing an INI file and it is missing struct FileError : public ParseError { - FileError (std::string name) : ParseError("FileError", name, 10) {} + FileError (std::string name) + : ParseError("FileError", name, ErrorCodes::File) {} }; /// Thrown when conversion call back fails, such as when an int fails to coerse to a string struct ConversionError : public ParseError { - ConversionError(std::string name) : ParseError("ConversionError", name, 2) {} + ConversionError(std::string name) + : ParseError("ConversionError", name, ErrorCodes::Conversion) {} }; /// Thrown when validation of results fails struct ValidationError : public ParseError { - ValidationError(std::string name) : ParseError("ValidationError", name, 2) {} + ValidationError(std::string name) + : ParseError("ValidationError", name, ErrorCodes::Validation) {} }; /// Thrown when a required option is missing struct RequiredError : public ParseError { - RequiredError(std::string name) : ParseError("RequiredError", name, 5) {} + RequiredError(std::string name) + : ParseError("RequiredError", name, ErrorCodes::Required) {} }; /// Thrown when a requires option is missing struct RequiresError : public ParseError { - RequiresError(std::string name, std::string subname) : ParseError("RequiresError", name + " requires " + subname, 13) {} + RequiresError(std::string name, std::string subname) + : ParseError("RequiresError", name + " requires " + subname, ErrorCodes::Requires) {} }; /// Thrown when a exludes option is present struct ExcludesError : public ParseError { - ExcludesError(std::string name, std::string subname) : ParseError("ExcludesError", name + " excludes " + subname, 14) {} + ExcludesError(std::string name, std::string subname) + : ParseError("ExcludesError", name + " excludes " + subname, ErrorCodes::Excludes) {} }; /// Thrown when too many positionals or options are found struct ExtrasError : public ParseError { - ExtrasError(std::string name) : ParseError("ExtrasError", name, 6) {} + ExtrasError(std::string name) + : ParseError("ExtrasError", name, ErrorCodes::Extras) {} +}; + +/// Thrown when extra values are found in an INI file +struct ExtrasINIError : public ParseError { + ExtrasINIError(std::string name) + : ParseError("ExtrasINIError", name, ErrorCodes::ExtrasINI) {} }; /// Thrown when validation fails before parsing struct InvalidError : public ParseError { - InvalidError(std::string name) : ParseError("InvalidError", name, 15) {} + InvalidError(std::string name) + : ParseError("InvalidError", name, ErrorCodes::Invalid) {} }; /// This is just a safety check to verify selection and parsing match struct HorribleError : public ParseError { - HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {} + HorribleError(std::string name) + : ParseError("HorribleError", "(You should never see this error) " + name, ErrorCodes::Horrible) {} }; // After parsing /// Thrown when counting a non-existent option struct OptionNotFound : public Error { - OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {} + OptionNotFound(std::string name) + : Error("OptionNotFound", name, ErrorCodes::OptionNotFound) {} }; /// @}