X-Git-Url: https://git.dlugolecki.net.pl/?a=blobdiff_plain;f=include%2Foption.h;h=7e9917b589c7021a0c5852695e90eecb5fa36908;hb=f2d683504e2dc76868ec84d7c1fd858edc0df5a4;hp=270bb76410f3c8bfead32c44adf0a6928cea707c;hpb=254fa86ed70ecceab3ed3b91470d15e672783ee4;p=command.git diff --git a/include/option.h b/include/option.h index 270bb76..7e9917b 100644 --- a/include/option.h +++ b/include/option.h @@ -2,25 +2,39 @@ #define __COMMAND_OPTION_H #include +#include +#include #include "parameter.h" +#include "exception/missingOptionValue.h" +#include "exception/optionFailedConversion.h" +#include "exception/optionValueNotSpecified.h" namespace command { /** * Class responsible for handling commandline options. - * Options are non-required, named parameters of program. + * Options are named parameters of program. * * Example: - * ./myprog OptionName OptionValue + * - ./myprog OptionName=OptionValue + * - ./myprog -f=/some/file + * - ./myprog --level=15 */ - template + template class Option - : public Parameter, public Callable { + : public Parameter, public Callable { + public: + typedef std::string OptionName; protected: /** - * Option name + * Current Option name + */ + const OptionName name; + + /** + * Current Option value */ - std::string name; + ParameterType value; public: /** @@ -30,22 +44,160 @@ namespace command { * @param description Description of current Option * @param function Function used to handle current Option. */ - Option(std::string name, const std::string & description, void (*function)(OptionType)) - : Parameter(description), Callable(function), name(name) { + Option(const std::string & name, const std::string & description, void (*function)(ParameterType)) + : Parameter(description), Callable(function), name(name) { } + + /** + * + */ virtual ~Option() { } + /** + * \inheritdoc + */ virtual void handle() { - this->call(std::string("O")); + this->call(value); + used = true; } - virtual bool understand(const std::string & argVal) { - if (argVal.find(name) != std::string::npos) { + /** + * Method used for checking if Option understands given user value. + * If so current Option is flagged as used and no more checks against + * it will be done in future. + * + * Passed value should be in form of: + * OptionName=OptionValue + * + * If no equal sign is after OptionName part, + * std::invalid_argument exception with appropriate message is thrown + * + * If conversion of OptionValue part to ParameterType failed, + * std::invalid_argument exception with appropriate message is thrown + * + * @param argv command line value against which test will be made. + * User value should be in format: OptionName=OptionValue. + * + * @return If passed argv succesfully detected OptionName part as a + * current option and its OptionValue part has been succesfully + * converted to ParameterType, returns true and Option is set as used one. + * Otherwise returns false and can be used to check against next value. + * + * @throw MissingOptionValue when OptionValue part is missing after + * equal sign + * @throw OptionFailedConversion when OptionValue part failed conversion + * to ParameterType + */ + virtual bool understand(const std::string & argv) { + if (this->hasName(argv)) { + std::size_t pos = this->valuePosition(argv); + + if (pos != name.size()) { + throw MissingOptionValue("Option: " + name + " requires value but no one has been provided"); + } + + std::stringstream ss; + ss << std::fixed << argv.substr(pos + 1); + ss >> value; + + if (ss.fail()) { + throw OptionFailedConversion("Option: " + name + " failed value conversion to the required type"); + } + return true; } + return false; + } + + /** + * \inheritdoc + */ + virtual unsigned int valuePosition(const std::string & value) { + std::size_t pos = value.find("="); + + if ((this->hasName(value)) && (pos == std::string::npos)) { + throw OptionValueNotSpecified("Option: " + name + " requires value to be specified after equal sign, but no equal sign was found"); + } + + return pos; + } + + protected: + bool hasName(const std::string & argv) { + return argv.find(name) == 0; + } + }; + /** + * Template class responsible for handling commandline options. + * Options are non-required, named parameters of program. + * This template specialization allows Options to work like switches. + * It means that just named parameter is needed to invoke command. No value + * is used. + * + * Example: + * ./myprog OptionName + * ./myprog -h + * ./myprog --help + */ + template<> + class Option + : public Parameter, public Callable { + public: + typedef std::string OptionName; + protected: + /** + * Current Option name + */ + const OptionName name; + public: + /** + * Default constructor. + * + * @param name Name of the current Option + * @param description Description of current Option + * @param function Function used to handle current Option. + */ + Option(const std::string & name, const std::string & description, void (*function)(void)) + : Parameter(description), Callable(function), name(name) { + } + + /** + * + */ + virtual void handle() { + this->call(); + used = true; + } + + /** + * Method used for checking if Option understands given user value. + * If so, current Option is flagged as used and no more checks against + * it will be done in future. + * + * Passed value should be in form of: + * OptionName + * + * @param argv command line value against which test will be made. + * User value should be in format: OptionName. + * + * @return If passed argv succesfully detected OptionName returns true + * and Option is set as used one. Otherwise returns false and can be + * used to check against next value. + */ + virtual bool understand(const std::string & argv) { + if (argv == name) { + return true; + } return false; } + + /** + * \inheritdoc + */ + virtual unsigned int valuePosition(const std::string & ) { + throw new std::invalid_argument(this->describe() + " is void Option, so it does not have value part"); + } }; }