2 * Copyright (C) 2016 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
19 #ifndef BIOMETRY_UTIL_CLI_H_
20 #define BIOMETRY_UTIL_CLI_H_
29 #include <unordered_map>
31 #include "anbox/do_not_copy_or_move.h"
32 #include "anbox/optional.h"
34 #include <boost/program_options.hpp>
39 template <std::size_t max>
40 class SizeConstrainedString {
42 SizeConstrainedString(const std::string& s) : s{s} {
44 throw std::logic_error{"Max size exceeded " + std::to_string(max)};
47 const std::string& as_string() const { return s; }
49 operator std::string() const { return s; }
55 template <std::size_t max>
56 bool operator<(const SizeConstrainedString<max>& lhs,
57 const SizeConstrainedString<max>& rhs) {
58 return lhs.as_string() < rhs.as_string();
61 template <std::size_t max>
62 bool operator==(const SizeConstrainedString<max>& lhs,
63 const SizeConstrainedString<max>& rhs) {
64 return lhs.as_string() == rhs.as_string();
67 template <std::size_t max>
68 std::ostream& operator<<(std::ostream& out,
69 const SizeConstrainedString<max>& scs) {
70 return out << std::setw(max) << std::left << scs.as_string();
73 // We are imposing size constraints to ensure a consistent CLI layout.
74 typedef SizeConstrainedString<30> Name;
75 typedef SizeConstrainedString<60> Usage;
76 typedef SizeConstrainedString<100> Description;
78 /// @brief Flag models an input parameter to a command.
79 class Flag : public DoNotCopyOrMove {
81 typedef boost::program_options::value_semantic* Specification;
83 // Safe us some typing.
84 typedef std::shared_ptr<Flag> Ptr;
86 /// @brief name returns the name of the Flag.
87 const Name& name() const;
88 /// @brief description returns a human-readable description of the flag.
89 const Description& description() const;
91 /// @brief specify the program option of the flag.
92 virtual void specify_option(Specification& spec) = 0;
95 /// @brief Flag creates a new instance, initializing name and description
96 /// from the given values.
97 Flag(const Name& name, const Description& description);
101 Description description_;
104 /// @brief TypedFlag implements Flag relying on operator<< and operator>> to
105 /// read/write values to/from strings.
106 template <typename T>
107 class TypedFlag : public Flag {
109 typedef std::shared_ptr<TypedFlag<T>> Ptr;
111 TypedFlag(const Name& name, const Description& description)
112 : Flag{name, description} {}
114 /// @brief value installs the given value in the flag.
115 TypedFlag& value(const T& value) {
120 /// @brief value returns the optional value associated with the flag.
121 const Optional<T>& value() const { return value_; }
123 /// @brief Option generated by specify_option tries to unwrap a value
124 /// of type T from value.
125 void specify_option(Flag::Specification& spec) override {
126 spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
127 std::stringstream ss{s};
138 /// @brief TypedReferenceFlag implements Flag, relying on operator<</>> to
139 /// convert to/from string representations,
140 /// updating the given mutable reference to a value of type T.
141 template <typename T>
142 class TypedReferenceFlag : public Flag {
144 // Safe us some typing.
145 typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
147 /// @brief TypedReferenceFlag initializes a new instance with name,
148 /// description and value.
149 TypedReferenceFlag(const Name& name, const Description& description, T& value)
150 : Flag{name, description}, value_{value} {}
152 /// @brief Option generated by specify_option tries to unwrap a value of type T
153 /// from value, relying on operator>> to read from given string s.
154 void specify_option(Flag::Specification& spec) override {
155 spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
156 std::stringstream ss{s};
162 std::reference_wrapper<T> value_;
165 /// @brief OptionalTypedReferenceFlag handles Optional<T> references, making
166 /// sure that a value is always read on notify, even if the Optional<T> wasn't
167 /// initialized previously.
168 template <typename T>
169 class OptionalTypedReferenceFlag : public Flag {
171 typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
173 OptionalTypedReferenceFlag(const Name& name, const Description& description,
175 : Flag{name, description}, value_{value} {}
177 /// @brief Option generated by specify_option tries to unwrap a value of
178 /// type T from value.
179 void specify_option(Flag::Specification& spec) override {
180 spec = boost::program_options::value<std::string>()->notifier([&](const std::string& s) {
181 std::stringstream ss{s};
184 value_.get() = value;
189 std::reference_wrapper<Optional<T>> value_;
192 /// @brief BoolSwitchFlag implements Flag, updating the given mutable reference
193 /// to a boolean value.
194 class BoolSwitchFlag : public Flag {
196 typedef std::shared_ptr<BoolSwitchFlag> Ptr;
198 BoolSwitchFlag(const Name& name, const Description& description, bool& value)
199 : Flag{name, description}, value_(value) {}
201 /// @brief Option generated by specify_option tries to unwrap a boolean
202 /// value from value.
203 void specify_option(Flag::Specification& spec) override {
204 spec = boost::program_options::bool_switch(&value_.get());
208 std::reference_wrapper<bool> value_;
211 /// @brief Command abstracts an individual command available from the daemon.
212 class Command : public DoNotCopyOrMove {
214 // Safe us some typing
215 typedef std::shared_ptr<Command> Ptr;
217 /// @brief FlagsMissing is thrown if at least one required flag is missing.
218 struct FlagsMissing : public std::runtime_error {
219 /// @brief FlagsMissing initializes a new instance.
223 /// @brief FlagsWithWrongValue is thrown if a value passed on the command line
225 struct FlagsWithInvalidValue : public std::runtime_error {
226 /// @brief FlagsWithInvalidValue initializes a new instance.
227 FlagsWithInvalidValue();
230 /// @brief Context bundles information passed to Command::run invocations.
232 std::istream& cin; ///< The std::istream that should be used for reading.
233 std::ostream& cout; ///< The std::ostream that should be used for writing.
234 std::vector<std::string> args; ///< The command line args.
237 /// @brief name returns the Name of the command.
238 virtual Name name() const;
240 /// @brief usage returns a short usage string for the command.
241 virtual Usage usage() const;
243 /// @brief description returns a longer string explaining the command.
244 virtual Description description() const;
246 /// @brief hidden returns if the command is hidden from the user or not.
247 virtual bool hidden() const;
249 /// @brief run puts the command to execution.
250 virtual int run(const Context& context) = 0;
252 /// @brief help prints information about a command to out.
253 virtual void help(std::ostream& out) = 0;
256 /// @brief Command initializes a new instance with the given name, usage and
258 Command(const Name& name, const Usage& usage, const Description& description, bool hidden = false);
260 /// @brief name adjusts the name of the command to n.
261 // virtual void name(const Name& n);
262 /// @brief usage adjusts the usage string of the comand to u.
263 // virtual void usage(const Usage& u);
264 /// @brief description adjusts the description string of the command to d.
265 // virtual void description(const Description& d);
270 Description description_;
274 /// @brief CommandWithSubcommands implements Command, selecting one of a set of
276 class CommandWithSubcommands : public Command {
278 typedef std::shared_ptr<CommandWithSubcommands> Ptr;
279 typedef std::function<int(const Context&)> Action;
281 /// @brief CommandWithSubcommands initializes a new instance with the given
282 /// name, usage and description.
283 CommandWithSubcommands(const Name& name, const Usage& usage,
284 const Description& description);
286 /// @brief command adds the given command to the set of known commands.
287 CommandWithSubcommands& command(const Command::Ptr& command);
289 /// @brief flag adds the given flag to the set of known flags.
290 CommandWithSubcommands& flag(const Flag::Ptr& flag);
293 int run(const Context& context) override;
294 void help(std::ostream& out) override;
297 std::unordered_map<std::string, Command::Ptr> commands_;
298 std::set<Flag::Ptr> flags_;
301 /// @brief CommandWithFlagsAction implements Command, executing an Action after
303 class CommandWithFlagsAndAction : public Command {
305 typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
306 typedef std::function<int(const Context&)> Action;
308 /// @brief CommandWithFlagsAndAction initializes a new instance with the given
309 /// name, usage and description. Optionally the command can be marked as hidden.
310 CommandWithFlagsAndAction(const Name& name, const Usage& usage,
311 const Description& description, bool hidden = false);
313 /// @brief flag adds the given flag to the set of known flags.
314 CommandWithFlagsAndAction& flag(const Flag::Ptr& flag);
316 /// @brief action installs the given action.
317 CommandWithFlagsAndAction& action(const Action& action);
320 int run(const Context& context) override;
321 void help(std::ostream& out) override;
324 std::set<Flag::Ptr> flags_;
329 /// @brief HelpFor prints a help message for the given command on execution.
330 class Help : public Command {
332 /// @brief HelpFor initializes a new instance with the given reference to a
334 explicit Help(Command& cmd);
337 int run(const Context& context) override;
338 void help(std::ostream& out) override;
347 /// @brief args returns a vector of strings assembled from argc and argv.
348 std::vector<std::string> args(int argc, char** argv);
350 /// @brief make_flag returns a flag with the given name and description.
351 template <typename T>
352 typename TypedFlag<T>::Ptr make_flag(const Name& name,
353 const Description& description) {
354 return std::make_shared<TypedFlag<T>>(name, description);
357 /// @brief make_flag returns a flag with the given name and description,
358 /// notifying updates to value.
359 template <typename T>
360 typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name,
361 const Description& desc,
363 return std::make_shared<TypedReferenceFlag<T>>(name, desc, value);
366 /// @brief make_flag returns a flag with the given name and description,
367 /// updating the given optional value.
368 template <typename T>
369 typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name,
370 const Description& desc,
371 Optional<T>& value) {
372 return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
375 /// @brief make_flag returns a flag with the given name and description,
376 /// updating the given boolean value.
377 inline BoolSwitchFlag::Ptr make_flag(const Name& name,
378 const Description &desc,
380 return std::make_shared<BoolSwitchFlag>(name, desc, value);