1 // Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner 2 // under NSF AWARD 1414736 and by the respective contributors. 3 // All rights reserved. 4 // 5 // SPDX-License-Identifier: BSD-3-Clause 6 7 #pragma once 8 9 // [CLI11:public_includes:set] 10 #include <exception> 11 #include <stdexcept> 12 #include <string> 13 #include <utility> 14 #include <vector> 15 // [CLI11:public_includes:end] 16 17 // CLI library includes 18 #include "StringTools.hpp" 19 20 namespace CLI { 21 // [CLI11:error_hpp:verbatim] 22 23 // Use one of these on all error classes. 24 // These are temporary and are undef'd at the end of this file. 25 #define CLI11_ERROR_DEF(parent, name) \ 26 protected: \ 27 name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ 28 name(std::string ename, std::string msg, ExitCodes exit_code) \ 29 : parent(std::move(ename), std::move(msg), exit_code) {} \ 30 \ 31 public: \ 32 name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ 33 name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} 34 35 // This is added after the one above if a class is used directly and builds its own message 36 #define CLI11_ERROR_SIMPLE(name) \ 37 explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} 38 39 /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, 40 /// int values from e.get_error_code(). 41 enum class ExitCodes { 42 Success = 0, 43 IncorrectConstruction = 100, 44 BadNameString, 45 OptionAlreadyAdded, 46 FileError, 47 ConversionError, 48 ValidationError, 49 RequiredError, 50 RequiresError, 51 ExcludesError, 52 ExtrasError, 53 ConfigError, 54 InvalidError, 55 HorribleError, 56 OptionNotFound, 57 ArgumentMismatch, 58 BaseClass = 127 59 }; 60 61 // Error definitions 62 63 /// @defgroup error_group Errors 64 /// @brief Errors thrown by CLI11 65 /// 66 /// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. 67 /// @{ 68 69 /// All errors derive from this one 70 class Error : public std::runtime_error { 71 int actual_exit_code; 72 std::string error_name{"Error"}; 73 74 public: get_exit_code() const75 int get_exit_code() const { return actual_exit_code; } 76 get_name() const77 std::string get_name() const { return error_name; } 78 Error(std::string name,std::string msg,int exit_code=static_cast<int> (ExitCodes::BaseClass))79 Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass)) 80 : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} 81 Error(std::string name,std::string msg,ExitCodes exit_code)82 Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {} 83 }; 84 85 // Note: Using Error::Error constructors does not work on GCC 4.7 86 87 /// Construction errors (not in parsing) 88 class ConstructionError : public Error { 89 CLI11_ERROR_DEF(Error, ConstructionError) 90 }; 91 92 /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) 93 class IncorrectConstruction : public ConstructionError { CLI11_ERROR_DEF(ConstructionError,IncorrectConstruction)94 CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) 95 CLI11_ERROR_SIMPLE(IncorrectConstruction) 96 static IncorrectConstruction PositionalFlag(std::string name) { 97 return IncorrectConstruction(name + ": Flags cannot be positional"); 98 } Set0Opt(std::string name)99 static IncorrectConstruction Set0Opt(std::string name) { 100 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); 101 } SetFlag(std::string name)102 static IncorrectConstruction SetFlag(std::string name) { 103 return IncorrectConstruction(name + ": Cannot set an expected number for flags"); 104 } ChangeNotVector(std::string name)105 static IncorrectConstruction ChangeNotVector(std::string name) { 106 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); 107 } AfterMultiOpt(std::string name)108 static IncorrectConstruction AfterMultiOpt(std::string name) { 109 return IncorrectConstruction( 110 name + ": You can't change expected arguments after you've changed the multi option policy!"); 111 } MissingOption(std::string name)112 static IncorrectConstruction MissingOption(std::string name) { 113 return IncorrectConstruction("Option " + name + " is not defined"); 114 } MultiOptionPolicy(std::string name)115 static IncorrectConstruction MultiOptionPolicy(std::string name) { 116 return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); 117 } 118 }; 119 120 /// Thrown on construction of a bad name 121 class BadNameString : public ConstructionError { CLI11_ERROR_DEF(ConstructionError,BadNameString)122 CLI11_ERROR_DEF(ConstructionError, BadNameString) 123 CLI11_ERROR_SIMPLE(BadNameString) 124 static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } BadLongName(std::string name)125 static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } DashesOnly(std::string name)126 static BadNameString DashesOnly(std::string name) { 127 return BadNameString("Must have a name, not just dashes: " + name); 128 } MultiPositionalNames(std::string name)129 static BadNameString MultiPositionalNames(std::string name) { 130 return BadNameString("Only one positional name allowed, remove: " + name); 131 } 132 }; 133 134 /// Thrown when an option already exists 135 class OptionAlreadyAdded : public ConstructionError { CLI11_ERROR_DEF(ConstructionError,OptionAlreadyAdded)136 CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) 137 explicit OptionAlreadyAdded(std::string name) 138 : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} Requires(std::string name,std::string other)139 static OptionAlreadyAdded Requires(std::string name, std::string other) { 140 return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); 141 } Excludes(std::string name,std::string other)142 static OptionAlreadyAdded Excludes(std::string name, std::string other) { 143 return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); 144 } 145 }; 146 147 // Parsing errors 148 149 /// Anything that can error in Parse 150 class ParseError : public Error { 151 CLI11_ERROR_DEF(Error, ParseError) 152 }; 153 154 // Not really "errors" 155 156 /// This is a successful completion on parsing, supposed to exit 157 class Success : public ParseError { CLI11_ERROR_DEF(ParseError,Success)158 CLI11_ERROR_DEF(ParseError, Success) 159 Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} 160 }; 161 162 /// -h or --help on command line 163 class CallForHelp : public Success { CLI11_ERROR_DEF(Success,CallForHelp)164 CLI11_ERROR_DEF(Success, CallForHelp) 165 CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} 166 }; 167 168 /// Usually something like --help-all on command line 169 class CallForAllHelp : public Success { CLI11_ERROR_DEF(Success,CallForAllHelp)170 CLI11_ERROR_DEF(Success, CallForAllHelp) 171 CallForAllHelp() 172 : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} 173 }; 174 175 /// -v or --version on command line 176 class CallForVersion : public Success { CLI11_ERROR_DEF(Success,CallForVersion)177 CLI11_ERROR_DEF(Success, CallForVersion) 178 CallForVersion() 179 : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {} 180 }; 181 182 /// Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code. 183 class RuntimeError : public ParseError { CLI11_ERROR_DEF(ParseError,RuntimeError)184 CLI11_ERROR_DEF(ParseError, RuntimeError) 185 explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} 186 }; 187 188 /// Thrown when parsing an INI file and it is missing 189 class FileError : public ParseError { CLI11_ERROR_DEF(ParseError,FileError)190 CLI11_ERROR_DEF(ParseError, FileError) 191 CLI11_ERROR_SIMPLE(FileError) 192 static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } 193 }; 194 195 /// Thrown when conversion call back fails, such as when an int fails to coerce to a string 196 class ConversionError : public ParseError { CLI11_ERROR_DEF(ParseError,ConversionError)197 CLI11_ERROR_DEF(ParseError, ConversionError) 198 CLI11_ERROR_SIMPLE(ConversionError) 199 ConversionError(std::string member, std::string name) 200 : ConversionError("The value " + member + " is not an allowed value for " + name) {} ConversionError(std::string name,std::vector<std::string> results)201 ConversionError(std::string name, std::vector<std::string> results) 202 : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} TooManyInputsFlag(std::string name)203 static ConversionError TooManyInputsFlag(std::string name) { 204 return ConversionError(name + ": too many inputs for a flag"); 205 } TrueFalse(std::string name)206 static ConversionError TrueFalse(std::string name) { 207 return ConversionError(name + ": Should be true/false or a number"); 208 } 209 }; 210 211 /// Thrown when validation of results fails 212 class ValidationError : public ParseError { CLI11_ERROR_DEF(ParseError,ValidationError)213 CLI11_ERROR_DEF(ParseError, ValidationError) 214 CLI11_ERROR_SIMPLE(ValidationError) 215 explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} 216 }; 217 218 /// Thrown when a required option is missing 219 class RequiredError : public ParseError { CLI11_ERROR_DEF(ParseError,RequiredError)220 CLI11_ERROR_DEF(ParseError, RequiredError) 221 explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} Subcommand(std::size_t min_subcom)222 static RequiredError Subcommand(std::size_t min_subcom) { 223 if(min_subcom == 1) { 224 return RequiredError("A subcommand"); 225 } 226 return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", 227 ExitCodes::RequiredError); 228 } 229 static RequiredError Option(std::size_t min_option,std::size_t max_option,std::size_t used,const std::string & option_list)230 Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) { 231 if((min_option == 1) && (max_option == 1) && (used == 0)) 232 return RequiredError("Exactly 1 option from [" + option_list + "]"); 233 if((min_option == 1) && (max_option == 1) && (used > 1)) { 234 return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + 235 " were given", 236 ExitCodes::RequiredError); 237 } 238 if((min_option == 1) && (used == 0)) 239 return RequiredError("At least 1 option from [" + option_list + "]"); 240 if(used < min_option) { 241 return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " + 242 std::to_string(used) + "were given from [" + option_list + "]", 243 ExitCodes::RequiredError); 244 } 245 if(max_option == 1) 246 return RequiredError("Requires at most 1 options be given from [" + option_list + "]", 247 ExitCodes::RequiredError); 248 249 return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " + 250 std::to_string(used) + "were given from [" + option_list + "]", 251 ExitCodes::RequiredError); 252 } 253 }; 254 255 /// Thrown when the wrong number of arguments has been received 256 class ArgumentMismatch : public ParseError { CLI11_ERROR_DEF(ParseError,ArgumentMismatch)257 CLI11_ERROR_DEF(ParseError, ArgumentMismatch) 258 CLI11_ERROR_SIMPLE(ArgumentMismatch) 259 ArgumentMismatch(std::string name, int expected, std::size_t received) 260 : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + 261 ", got " + std::to_string(received)) 262 : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + 263 ", got " + std::to_string(received)), 264 ExitCodes::ArgumentMismatch) {} 265 AtLeast(std::string name,int num,std::size_t received)266 static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) { 267 return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + 268 std::to_string(received)); 269 } AtMost(std::string name,int num,std::size_t received)270 static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) { 271 return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + 272 std::to_string(received)); 273 } TypedAtLeast(std::string name,int num,std::string type)274 static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { 275 return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); 276 } FlagOverride(std::string name)277 static ArgumentMismatch FlagOverride(std::string name) { 278 return ArgumentMismatch(name + " was given a disallowed flag override"); 279 } 280 }; 281 282 /// Thrown when a requires option is missing 283 class RequiresError : public ParseError { CLI11_ERROR_DEF(ParseError,RequiresError)284 CLI11_ERROR_DEF(ParseError, RequiresError) 285 RequiresError(std::string curname, std::string subname) 286 : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} 287 }; 288 289 /// Thrown when an excludes option is present 290 class ExcludesError : public ParseError { CLI11_ERROR_DEF(ParseError,ExcludesError)291 CLI11_ERROR_DEF(ParseError, ExcludesError) 292 ExcludesError(std::string curname, std::string subname) 293 : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} 294 }; 295 296 /// Thrown when too many positionals or options are found 297 class ExtrasError : public ParseError { CLI11_ERROR_DEF(ParseError,ExtrasError)298 CLI11_ERROR_DEF(ParseError, ExtrasError) 299 explicit ExtrasError(std::vector<std::string> args) 300 : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " 301 : "The following argument was not expected: ") + 302 detail::rjoin(args, " "), 303 ExitCodes::ExtrasError) {} ExtrasError(const std::string & name,std::vector<std::string> args)304 ExtrasError(const std::string &name, std::vector<std::string> args) 305 : ExtrasError(name, 306 (args.size() > 1 ? "The following arguments were not expected: " 307 : "The following argument was not expected: ") + 308 detail::rjoin(args, " "), 309 ExitCodes::ExtrasError) {} 310 }; 311 312 /// Thrown when extra values are found in an INI file 313 class ConfigError : public ParseError { CLI11_ERROR_DEF(ParseError,ConfigError)314 CLI11_ERROR_DEF(ParseError, ConfigError) 315 CLI11_ERROR_SIMPLE(ConfigError) 316 static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } NotConfigurable(std::string item)317 static ConfigError NotConfigurable(std::string item) { 318 return ConfigError(item + ": This option is not allowed in a configuration file"); 319 } 320 }; 321 322 /// Thrown when validation fails before parsing 323 class InvalidError : public ParseError { CLI11_ERROR_DEF(ParseError,InvalidError)324 CLI11_ERROR_DEF(ParseError, InvalidError) 325 explicit InvalidError(std::string name) 326 : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { 327 } 328 }; 329 330 /// This is just a safety check to verify selection and parsing match - you should not ever see it 331 /// Strings are directly added to this error, but again, it should never be seen. 332 class HorribleError : public ParseError { 333 CLI11_ERROR_DEF(ParseError, HorribleError) 334 CLI11_ERROR_SIMPLE(HorribleError) 335 }; 336 337 // After parsing 338 339 /// Thrown when counting a non-existent option 340 class OptionNotFound : public Error { CLI11_ERROR_DEF(Error,OptionNotFound)341 CLI11_ERROR_DEF(Error, OptionNotFound) 342 explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} 343 }; 344 345 #undef CLI11_ERROR_DEF 346 #undef CLI11_ERROR_SIMPLE 347 348 /// @} 349 350 // [CLI11:error_hpp:end] 351 } // namespace CLI 352