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 | 3 | * Ini files support flags (only read) |
| 4 | 4 | * Ini files support subcommands (only read) |
| 5 | 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 | 10 | ## Version 0.6 |
| 9 | 11 | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -482,7 +482,7 @@ public: |
| 482 | 482 | |
| 483 | 483 | /// Print a nice error message and return the exit code |
| 484 | 484 | int exit(const Error& e) const { |
| 485 | - if(e.exit_code != 0) { | |
| 485 | + if(e.exit_code != ErrorCodes::Success) { | |
| 486 | 486 | std::cerr << "ERROR: "; |
| 487 | 487 | std::cerr << e.what() << std::endl; |
| 488 | 488 | if(e.print_help) |
| ... | ... | @@ -491,7 +491,7 @@ public: |
| 491 | 491 | if(e.print_help) |
| 492 | 492 | std::cout << help(); |
| 493 | 493 | } |
| 494 | - return e.exit_code; | |
| 494 | + return e.get_exit_code(); | |
| 495 | 495 | } |
| 496 | 496 | |
| 497 | 497 | /// Reset the parsed data |
| ... | ... | @@ -757,7 +757,7 @@ protected: |
| 757 | 757 | try { |
| 758 | 758 | std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_); |
| 759 | 759 | while(values.size() > 0) { |
| 760 | - if(!_parse_env(values)) { | |
| 760 | + if(!_parse_ini(values)) { | |
| 761 | 761 | throw ExtrasError(values.back().name()); |
| 762 | 762 | } |
| 763 | 763 | } |
| ... | ... | @@ -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 | 826 | /// If this has more than one dot.separated.name, go into the subcommand matching it |
| 827 | 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 | 829 | detail::ini_ret_t& current = args.back(); |
| 830 | 830 | std::string parent = current.parent(); |
| 831 | 831 | std::string name = current.name(); |
| ... | ... | @@ -833,8 +833,9 @@ protected: |
| 833 | 833 | current.level++; |
| 834 | 834 | for(const App_p &com : subcommands_) |
| 835 | 835 | if(com->check_name(parent)) |
| 836 | - return com->_parse_env(args); | |
| 836 | + return com->_parse_ini(args); | |
| 837 | 837 | return false; |
| 838 | + //throw CLI::ExtraINIError(current.fullname); | |
| 838 | 839 | } |
| 839 | 840 | |
| 840 | 841 | auto op_ptr = std::find_if(std::begin(options_), std::end(options_), | ... | ... |
include/CLI/Error.hpp
| ... | ... | @@ -9,6 +9,25 @@ |
| 9 | 9 | |
| 10 | 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 | 31 | // Error definitions |
| 13 | 32 | |
| 14 | 33 | /// @defgroup error_group Errors |
| ... | ... | @@ -19,102 +38,127 @@ namespace CLI { |
| 19 | 38 | |
| 20 | 39 | /// All errors derive from this one |
| 21 | 40 | struct Error : public std::runtime_error { |
| 22 | - int exit_code; | |
| 41 | + ErrorCodes exit_code; | |
| 23 | 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 | 48 | /// Construction errors (not in parsing) |
| 28 | 49 | struct ConstructionError : public Error { |
| 29 | 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 | 55 | /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) |
| 34 | 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 | 61 | /// Thrown on construction of a bad name |
| 39 | 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 | 67 | /// Thrown when an option already exists |
| 44 | 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 | 73 | // Parsing errors |
| 49 | 74 | |
| 50 | 75 | /// Anything that can error in Parse |
| 51 | 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 | 81 | // Not really "errors" |
| 56 | 82 | |
| 57 | 83 | /// This is a successful completion on parsing, supposed to exit |
| 58 | 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 | 89 | /// -h or --help on command line |
| 63 | 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 | 96 | /// Thrown when parsing an INI file and it is missing |
| 69 | 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 | 102 | /// Thrown when conversion call back fails, such as when an int fails to coerse to a string |
| 74 | 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 | 108 | /// Thrown when validation of results fails |
| 79 | 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 | 114 | /// Thrown when a required option is missing |
| 84 | 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 | 120 | /// Thrown when a requires option is missing |
| 89 | 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 | 126 | /// Thrown when a exludes option is present |
| 94 | 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 | 132 | /// Thrown when too many positionals or options are found |
| 99 | 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 | 144 | /// Thrown when validation fails before parsing |
| 104 | 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 | 150 | /// This is just a safety check to verify selection and parsing match |
| 109 | 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 | 156 | // After parsing |
| 114 | 157 | |
| 115 | 158 | /// Thrown when counting a non-existent option |
| 116 | 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 | /// @} | ... | ... |