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