Added mechanism to understand passed Option value.
[command.git] / include / option.h
1 #ifndef __COMMAND_OPTION_H
2 #define __COMMAND_OPTION_H
3
4 #include <string>
5 #include <sstream>
6 #include <stdexcept>
7
8 #include "parameter.h"
9
10 namespace command {
11     /**
12      * Class responsible for handling commandline options.
13      * Options are non-required, named parameters of program.
14      *
15      * Example:
16      *  ./myprog OptionName=OptionValue
17      */
18     template<typename OptionType>
19     class Option
20         : public Parameter, public Callable<OptionType>  {
21     public:
22         typedef std::string OptionName;
23     protected:
24         /**
25          * Current Option name
26          */
27         OptionName name;
28
29         /**
30          * Current Option value
31          */
32         OptionType value;
33
34         /** Variable indicating if current Option was already used or not */
35         bool used = false;
36
37     public:
38         /**
39          * Default constructor.
40          *
41          * @param name Name of the current Option
42          * @param description Description of current Option
43          * @param function Function used to handle current Option.
44          */
45         Option(std::string name, const std::string & description, void (*function)(OptionType))
46             : Parameter(description), Callable<OptionType>(function), name(name) {
47         }
48
49         /**
50          *
51          */
52         virtual ~Option() { }
53
54         /**
55          *
56          */
57         virtual void handle() {
58             this->call(value);
59         }
60
61         /**
62          * Method used for checking if Option understands given user value.
63          * If so current Option is flagged as used and no more checks against
64          * it will be done in future.
65          *
66          * Passed value should be in form of:
67          *      OptionName=OptionValue
68          *
69          * If no equal sign is after OptionName part,
70          * std::invalid_argument exception with appropriate message is thrown
71          *
72          * If conversion of OptionValue part to OptionType failed,
73          * std::invalid_argument exception with appropriate message is thrown
74          *
75          * @param argv command line value against which test will be made.
76          *  User value should be in format: OptionName=OptionValue.
77          *
78          * @return If passed argv succesfully detected OptionName part as a
79          *  current option and its OptionValue part has been succesfully
80          *  converted to OptionType, returns true and Option is set as used one.
81          *  Otherwise returns false and can be used to check against next value.
82          *
83          * @throw std::invalid_argument when OptionName part has no equal sign
84          *  after itself
85          * @throw std::invalid_argument when OptionValue part failed conversion
86          *  to OptionType
87          */
88         virtual bool understand(const std::string & argv) {
89             if ((!used) &&
90                 (argv.find(name) == 0)) {
91                 std::size_t pos = argv.find("=");
92                 if (pos != name.size()) {
93                     throw std::invalid_argument("Option: " + name + " requires value but no one has been provided");
94                 }
95
96                 std::stringstream ss;
97
98                 ss << argv.substr(pos + 1);
99                 ss >> value;
100
101                 if (ss.fail()) {
102                     throw std::invalid_argument("Value for option: " + name + " failed conversion to the required type");
103                 }
104
105                 used = true;
106                 return true;
107             }
108             return false;
109         }
110     };
111 }
112
113 #endif /* __COMMAND_OPTION_H */