Add Option tests.
[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      *  ./myprog -f=/some/file
18      *  ./myprog --level=15
19      */
20     template<typename OptionType>
21     class Option
22         : public Parameter, public Callable<OptionType>  {
23     public:
24         typedef std::string OptionName;
25     protected:
26         /**
27          * Current Option name
28          */
29         const OptionName name;
30
31         /**
32          * Current Option value
33          */
34         OptionType value;
35
36         /** Variable indicating if current Option was already used or not */
37         bool used = false;
38
39     public:
40         /**
41          * Default constructor.
42          *
43          * @param name Name of the current Option
44          * @param description Description of current Option
45          * @param function Function used to handle current Option.
46          */
47         Option(const std::string & name, const std::string & description, void (*function)(OptionType))
48             : Parameter(description), Callable<OptionType>(function), name(name) {
49         }
50
51         /**
52          *
53          */
54         virtual ~Option() { }
55
56         /**
57          *
58          */
59         virtual void handle() {
60             this->call(value);
61         }
62
63         /**
64          * Method used for checking if Option understands given user value.
65          * If so current Option is flagged as used and no more checks against
66          * it will be done in future.
67          *
68          * Passed value should be in form of:
69          *      OptionName=OptionValue
70          *
71          * If no equal sign is after OptionName part,
72          * std::invalid_argument exception with appropriate message is thrown
73          *
74          * If conversion of OptionValue part to OptionType failed,
75          * std::invalid_argument exception with appropriate message is thrown
76          *
77          * @param argv command line value against which test will be made.
78          *  User value should be in format: OptionName=OptionValue.
79          *
80          * @return If passed argv succesfully detected OptionName part as a
81          *  current option and its OptionValue part has been succesfully
82          *  converted to OptionType, returns true and Option is set as used one.
83          *  Otherwise returns false and can be used to check against next value.
84          *
85          * @throw std::invalid_argument when OptionName part has no equal sign
86          *  after itself
87          * @throw std::invalid_argument when OptionValue part failed conversion
88          *  to OptionType
89          */
90         virtual bool understand(const std::string & argv)
91             throw(std::invalid_argument) {
92
93             if ((!used) && (argv.find(name) == 0)) {
94                 std::size_t pos = argv.find("=");
95
96                 if (pos != name.size()) {
97                     throw std::invalid_argument("Option: " + name + " requires value but no one has been provided");
98                 }
99
100                 std::stringstream ss;
101                 ss << argv.substr(pos + 1);
102                 ss >> value;// memory leak? when uncommented and exception is
103                             // thrown, valgrind shows e.g.:
104                             //  possibly lost: 380 bytes in 7 blocks
105
106                 if (ss.fail()) {
107                     throw std::invalid_argument("Value for option: " + name + " failed conversion to the required type");
108                 }
109
110                 used = true;
111                 return true;
112             }
113             return false;
114         }
115     };
116
117     /**
118      * Template class responsible for handling commandline options.
119      * Options are non-required, named parameters of program.
120      * This template specialization allows Options to work like switches.
121      * It means that just named parameter is needed to invoke command. No value
122      * is used.
123      *
124      * Example:
125      *  ./myprog OptionName
126      *  ./myprog -h
127      *  ./myprog --help
128      */
129     template<>
130     class Option<void>
131         : public Parameter, public Callable<void>  {
132     public:
133         typedef std::string OptionName;
134     protected:
135         /**
136          * Current Option name
137          */
138         const OptionName name;
139
140         /** Variable indicating if current Option was already used or not */
141         bool used = false;
142
143     public:
144         /**
145          * Default constructor.
146          *
147          * @param name Name of the current Option
148          * @param description Description of current Option
149          * @param function Function used to handle current Option.
150          */
151         Option(const std::string & name, const std::string & description, void (*function)(void))
152             : Parameter(description), Callable<void>(function), name(name) {
153         }
154
155         /**
156          *
157          */
158         virtual void handle() {
159             this->call();
160         }
161
162         /**
163          * Method used for checking if Option understands given user value.
164          * If so, current Option is flagged as used and no more checks against
165          * it will be done in future.
166          *
167          * Passed value should be in form of:
168          *      OptionName
169          *
170          * @param argv command line value against which test will be made.
171          *  User value should be in format: OptionName.
172          *
173          * @return If passed argv succesfully detected OptionName returns true
174          *  and Option is set as used one. Otherwise returns false and can be
175          *  used to check against next value.
176          */
177         virtual bool understand(const std::string & argv) {
178             if ((!used) &&
179                 (argv == name)) {
180                 used = true;
181                 return true;
182             }
183             return false;
184         }
185     };
186 }
187
188 #endif /* __COMMAND_OPTION_H */