Commit 17564cec05d862b79643092b876036186005a050
1 parent
e49cc11f
Scoped enum for errors
Showing
3 changed files
with
73 additions
and
26 deletions
CHANGELOG.md
| @@ -3,7 +3,9 @@ | @@ -3,7 +3,9 @@ | ||
| 3 | * Ini files support flags (only read) | 3 | * Ini files support flags (only read) |
| 4 | * Ini files support subcommands (only read) | 4 | * Ini files support subcommands (only read) |
| 5 | * Ini files support vectors (only read) | 5 | * Ini files support vectors (only read) |
| 6 | -* Added CodeCov code coverage reports, increased coverage | 6 | +* Added CodeCov code coverage reports |
| 7 | +* Lots of small bugfixes related to adding tests to increase coverage | ||
| 8 | +* Error handling now uses scoped enum in errors | ||
| 7 | 9 | ||
| 8 | ## Version 0.6 | 10 | ## Version 0.6 |
| 9 | 11 |
include/CLI/App.hpp
| @@ -482,7 +482,7 @@ public: | @@ -482,7 +482,7 @@ public: | ||
| 482 | 482 | ||
| 483 | /// Print a nice error message and return the exit code | 483 | /// Print a nice error message and return the exit code |
| 484 | int exit(const Error& e) const { | 484 | int exit(const Error& e) const { |
| 485 | - if(e.exit_code != 0) { | 485 | + if(e.exit_code != ErrorCodes::Success) { |
| 486 | std::cerr << "ERROR: "; | 486 | std::cerr << "ERROR: "; |
| 487 | std::cerr << e.what() << std::endl; | 487 | std::cerr << e.what() << std::endl; |
| 488 | if(e.print_help) | 488 | if(e.print_help) |
| @@ -491,7 +491,7 @@ public: | @@ -491,7 +491,7 @@ public: | ||
| 491 | if(e.print_help) | 491 | if(e.print_help) |
| 492 | std::cout << help(); | 492 | std::cout << help(); |
| 493 | } | 493 | } |
| 494 | - return e.exit_code; | 494 | + return e.get_exit_code(); |
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | /// Reset the parsed data | 497 | /// Reset the parsed data |
| @@ -757,7 +757,7 @@ protected: | @@ -757,7 +757,7 @@ protected: | ||
| 757 | try { | 757 | try { |
| 758 | std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_); | 758 | std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_); |
| 759 | while(values.size() > 0) { | 759 | while(values.size() > 0) { |
| 760 | - if(!_parse_env(values)) { | 760 | + if(!_parse_ini(values)) { |
| 761 | throw ExtrasError(values.back().name()); | 761 | throw ExtrasError(values.back().name()); |
| 762 | } | 762 | } |
| 763 | } | 763 | } |
| @@ -821,11 +821,11 @@ protected: | @@ -821,11 +821,11 @@ protected: | ||
| 821 | } | 821 | } |
| 822 | } | 822 | } |
| 823 | 823 | ||
| 824 | - /// Parse one env param, ignore if not applicable, remove if it is | 824 | + /// Parse one ini param, return false if not found in any subcommand, remove if it is |
| 825 | /// | 825 | /// |
| 826 | /// If this has more than one dot.separated.name, go into the subcommand matching it | 826 | /// If this has more than one dot.separated.name, go into the subcommand matching it |
| 827 | /// Returns true if it managed to find the option, if false you'll need to remove the arg manully. | 827 | /// Returns true if it managed to find the option, if false you'll need to remove the arg manully. |
| 828 | - bool _parse_env(std::vector<detail::ini_ret_t> &args) { | 828 | + bool _parse_ini(std::vector<detail::ini_ret_t> &args) { |
| 829 | detail::ini_ret_t& current = args.back(); | 829 | detail::ini_ret_t& current = args.back(); |
| 830 | std::string parent = current.parent(); | 830 | std::string parent = current.parent(); |
| 831 | std::string name = current.name(); | 831 | std::string name = current.name(); |
| @@ -833,8 +833,9 @@ protected: | @@ -833,8 +833,9 @@ protected: | ||
| 833 | current.level++; | 833 | current.level++; |
| 834 | for(const App_p &com : subcommands_) | 834 | for(const App_p &com : subcommands_) |
| 835 | if(com->check_name(parent)) | 835 | if(com->check_name(parent)) |
| 836 | - return com->_parse_env(args); | 836 | + return com->_parse_ini(args); |
| 837 | return false; | 837 | return false; |
| 838 | + //throw CLI::ExtraINIError(current.fullname); | ||
| 838 | } | 839 | } |
| 839 | 840 | ||
| 840 | auto op_ptr = std::find_if(std::begin(options_), std::end(options_), | 841 | auto op_ptr = std::find_if(std::begin(options_), std::end(options_), |
include/CLI/Error.hpp
| @@ -9,6 +9,25 @@ | @@ -9,6 +9,25 @@ | ||
| 9 | 9 | ||
| 10 | namespace CLI { | 10 | namespace CLI { |
| 11 | 11 | ||
| 12 | +enum class ErrorCodes { | ||
| 13 | + Success = 0, | ||
| 14 | + IncorrectConstruction=100, | ||
| 15 | + BadNameString, | ||
| 16 | + OptionAlreadyAdded, | ||
| 17 | + File, | ||
| 18 | + Conversion, | ||
| 19 | + Validation, | ||
| 20 | + Required, | ||
| 21 | + Requires, | ||
| 22 | + Excludes, | ||
| 23 | + Extras, | ||
| 24 | + ExtrasINI, | ||
| 25 | + Invalid, | ||
| 26 | + Horrible, | ||
| 27 | + OptionNotFound, | ||
| 28 | + BaseClass = 255 | ||
| 29 | +}; | ||
| 30 | + | ||
| 12 | // Error definitions | 31 | // Error definitions |
| 13 | 32 | ||
| 14 | /// @defgroup error_group Errors | 33 | /// @defgroup error_group Errors |
| @@ -19,102 +38,127 @@ namespace CLI { | @@ -19,102 +38,127 @@ namespace CLI { | ||
| 19 | 38 | ||
| 20 | /// All errors derive from this one | 39 | /// All errors derive from this one |
| 21 | struct Error : public std::runtime_error { | 40 | struct Error : public std::runtime_error { |
| 22 | - int exit_code; | 41 | + ErrorCodes exit_code; |
| 23 | bool print_help; | 42 | bool print_help; |
| 24 | - 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) {} | 43 | + int get_exit_code() const {return static_cast<int>(exit_code);} |
| 44 | + Error(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) | ||
| 45 | + : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {} | ||
| 25 | }; | 46 | }; |
| 26 | 47 | ||
| 27 | /// Construction errors (not in parsing) | 48 | /// Construction errors (not in parsing) |
| 28 | struct ConstructionError : public Error { | 49 | struct ConstructionError : public Error { |
| 29 | // Using Error::Error constructors seem to not work on GCC 4.7 | 50 | // Using Error::Error constructors seem to not work on GCC 4.7 |
| 30 | - ConstructionError(std::string parent, std::string name, int exit_code=255, bool print_help=true) : Error(parent, name, exit_code, print_help) {} | 51 | + ConstructionError(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) |
| 52 | + : Error(parent, name, exit_code, print_help) {} | ||
| 31 | }; | 53 | }; |
| 32 | 54 | ||
| 33 | /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) | 55 | /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) |
| 34 | struct IncorrectConstruction : public ConstructionError { | 56 | struct IncorrectConstruction : public ConstructionError { |
| 35 | - IncorrectConstruction(std::string name) : ConstructionError("IncorrectConstruction", name, 8) {} | 57 | + IncorrectConstruction(std::string name) |
| 58 | + : ConstructionError("IncorrectConstruction", name, ErrorCodes::IncorrectConstruction) {} | ||
| 36 | }; | 59 | }; |
| 37 | 60 | ||
| 38 | /// Thrown on construction of a bad name | 61 | /// Thrown on construction of a bad name |
| 39 | struct BadNameString : public ConstructionError { | 62 | struct BadNameString : public ConstructionError { |
| 40 | - BadNameString(std::string name) : ConstructionError("BadNameString", name, 1) {} | 63 | + BadNameString(std::string name) |
| 64 | + : ConstructionError("BadNameString", name, ErrorCodes::BadNameString) {} | ||
| 41 | }; | 65 | }; |
| 42 | 66 | ||
| 43 | /// Thrown when an option already exists | 67 | /// Thrown when an option already exists |
| 44 | struct OptionAlreadyAdded : public ConstructionError { | 68 | struct OptionAlreadyAdded : public ConstructionError { |
| 45 | - OptionAlreadyAdded(std::string name) : ConstructionError("OptionAlreadyAdded", name, 3) {} | 69 | + OptionAlreadyAdded(std::string name) |
| 70 | + : ConstructionError("OptionAlreadyAdded", name, ErrorCodes::OptionAlreadyAdded) {} | ||
| 46 | }; | 71 | }; |
| 47 | 72 | ||
| 48 | // Parsing errors | 73 | // Parsing errors |
| 49 | 74 | ||
| 50 | /// Anything that can error in Parse | 75 | /// Anything that can error in Parse |
| 51 | struct ParseError : public Error { | 76 | struct ParseError : public Error { |
| 52 | - ParseError(std::string parent, std::string name, int exit_code=255, bool print_help=true) : Error(parent, name, exit_code, print_help) {} | 77 | + ParseError(std::string parent, std::string name, ErrorCodes exit_code=ErrorCodes::BaseClass, bool print_help=true) |
| 78 | + : Error(parent, name, exit_code, print_help) {} | ||
| 53 | }; | 79 | }; |
| 54 | 80 | ||
| 55 | // Not really "errors" | 81 | // Not really "errors" |
| 56 | 82 | ||
| 57 | /// This is a successful completion on parsing, supposed to exit | 83 | /// This is a successful completion on parsing, supposed to exit |
| 58 | struct Success : public ParseError { | 84 | struct Success : public ParseError { |
| 59 | - Success() : ParseError("Success", "Successfully completed, should be caught and quit", 0, false) {} | 85 | + Success() |
| 86 | + : ParseError("Success", "Successfully completed, should be caught and quit", ErrorCodes::Success, false) {} | ||
| 60 | }; | 87 | }; |
| 61 | 88 | ||
| 62 | /// -h or --help on command line | 89 | /// -h or --help on command line |
| 63 | struct CallForHelp : public ParseError { | 90 | struct CallForHelp : public ParseError { |
| 64 | - CallForHelp() : ParseError("CallForHelp", "This should be caught in your main function, see examples", 0) {} | 91 | + CallForHelp() |
| 92 | + : ParseError("CallForHelp", "This should be caught in your main function, see examples", ErrorCodes::Success) {} | ||
| 65 | }; | 93 | }; |
| 66 | 94 | ||
| 67 | 95 | ||
| 68 | /// Thrown when parsing an INI file and it is missing | 96 | /// Thrown when parsing an INI file and it is missing |
| 69 | struct FileError : public ParseError { | 97 | struct FileError : public ParseError { |
| 70 | - FileError (std::string name) : ParseError("FileError", name, 10) {} | 98 | + FileError (std::string name) |
| 99 | + : ParseError("FileError", name, ErrorCodes::File) {} | ||
| 71 | }; | 100 | }; |
| 72 | 101 | ||
| 73 | /// Thrown when conversion call back fails, such as when an int fails to coerse to a string | 102 | /// Thrown when conversion call back fails, such as when an int fails to coerse to a string |
| 74 | struct ConversionError : public ParseError { | 103 | struct ConversionError : public ParseError { |
| 75 | - ConversionError(std::string name) : ParseError("ConversionError", name, 2) {} | 104 | + ConversionError(std::string name) |
| 105 | + : ParseError("ConversionError", name, ErrorCodes::Conversion) {} | ||
| 76 | }; | 106 | }; |
| 77 | 107 | ||
| 78 | /// Thrown when validation of results fails | 108 | /// Thrown when validation of results fails |
| 79 | struct ValidationError : public ParseError { | 109 | struct ValidationError : public ParseError { |
| 80 | - ValidationError(std::string name) : ParseError("ValidationError", name, 2) {} | 110 | + ValidationError(std::string name) |
| 111 | + : ParseError("ValidationError", name, ErrorCodes::Validation) {} | ||
| 81 | }; | 112 | }; |
| 82 | 113 | ||
| 83 | /// Thrown when a required option is missing | 114 | /// Thrown when a required option is missing |
| 84 | struct RequiredError : public ParseError { | 115 | struct RequiredError : public ParseError { |
| 85 | - RequiredError(std::string name) : ParseError("RequiredError", name, 5) {} | 116 | + RequiredError(std::string name) |
| 117 | + : ParseError("RequiredError", name, ErrorCodes::Required) {} | ||
| 86 | }; | 118 | }; |
| 87 | 119 | ||
| 88 | /// Thrown when a requires option is missing | 120 | /// Thrown when a requires option is missing |
| 89 | struct RequiresError : public ParseError { | 121 | struct RequiresError : public ParseError { |
| 90 | - RequiresError(std::string name, std::string subname) : ParseError("RequiresError", name + " requires " + subname, 13) {} | 122 | + RequiresError(std::string name, std::string subname) |
| 123 | + : ParseError("RequiresError", name + " requires " + subname, ErrorCodes::Requires) {} | ||
| 91 | }; | 124 | }; |
| 92 | 125 | ||
| 93 | /// Thrown when a exludes option is present | 126 | /// Thrown when a exludes option is present |
| 94 | struct ExcludesError : public ParseError { | 127 | struct ExcludesError : public ParseError { |
| 95 | - ExcludesError(std::string name, std::string subname) : ParseError("ExcludesError", name + " excludes " + subname, 14) {} | 128 | + ExcludesError(std::string name, std::string subname) |
| 129 | + : ParseError("ExcludesError", name + " excludes " + subname, ErrorCodes::Excludes) {} | ||
| 96 | }; | 130 | }; |
| 97 | 131 | ||
| 98 | /// Thrown when too many positionals or options are found | 132 | /// Thrown when too many positionals or options are found |
| 99 | struct ExtrasError : public ParseError { | 133 | struct ExtrasError : public ParseError { |
| 100 | - ExtrasError(std::string name) : ParseError("ExtrasError", name, 6) {} | 134 | + ExtrasError(std::string name) |
| 135 | + : ParseError("ExtrasError", name, ErrorCodes::Extras) {} | ||
| 136 | +}; | ||
| 137 | + | ||
| 138 | +/// Thrown when extra values are found in an INI file | ||
| 139 | +struct ExtrasINIError : public ParseError { | ||
| 140 | + ExtrasINIError(std::string name) | ||
| 141 | + : ParseError("ExtrasINIError", name, ErrorCodes::ExtrasINI) {} | ||
| 101 | }; | 142 | }; |
| 102 | 143 | ||
| 103 | /// Thrown when validation fails before parsing | 144 | /// Thrown when validation fails before parsing |
| 104 | struct InvalidError : public ParseError { | 145 | struct InvalidError : public ParseError { |
| 105 | - InvalidError(std::string name) : ParseError("InvalidError", name, 15) {} | 146 | + InvalidError(std::string name) |
| 147 | + : ParseError("InvalidError", name, ErrorCodes::Invalid) {} | ||
| 106 | }; | 148 | }; |
| 107 | 149 | ||
| 108 | /// This is just a safety check to verify selection and parsing match | 150 | /// This is just a safety check to verify selection and parsing match |
| 109 | struct HorribleError : public ParseError { | 151 | struct HorribleError : public ParseError { |
| 110 | - HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {} | 152 | + HorribleError(std::string name) |
| 153 | + : ParseError("HorribleError", "(You should never see this error) " + name, ErrorCodes::Horrible) {} | ||
| 111 | }; | 154 | }; |
| 112 | 155 | ||
| 113 | // After parsing | 156 | // After parsing |
| 114 | 157 | ||
| 115 | /// Thrown when counting a non-existent option | 158 | /// Thrown when counting a non-existent option |
| 116 | struct OptionNotFound : public Error { | 159 | struct OptionNotFound : public Error { |
| 117 | - OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {} | 160 | + OptionNotFound(std::string name) |
| 161 | + : Error("OptionNotFound", name, ErrorCodes::OptionNotFound) {} | ||
| 118 | }; | 162 | }; |
| 119 | 163 | ||
| 120 | /// @} | 164 | /// @} |