/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_CLI_H_ #define BIOMETRY_UTIL_CLI_H_ #include #include #include #include #include #include #include #include #include "anbox/do_not_copy_or_move.h" #include "anbox/optional.h" #include namespace anbox { namespace cli { template class SizeConstrainedString { public: SizeConstrainedString(const std::string& s) : s{s} { if (s.size() > max) throw std::logic_error{"Max size exceeded " + std::to_string(max)}; } const std::string& as_string() const { return s; } operator std::string() const { return s; } private: std::string s; }; template bool operator<(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) { return lhs.as_string() < rhs.as_string(); } template bool operator==(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) { return lhs.as_string() == rhs.as_string(); } template std::ostream& operator<<(std::ostream& out, const SizeConstrainedString& scs) { return out << std::setw(max) << std::left << scs.as_string(); } // We are imposing size constraints to ensure a consistent CLI layout. typedef SizeConstrainedString<30> Name; typedef SizeConstrainedString<60> Usage; typedef SizeConstrainedString<100> Description; /// @brief Flag models an input parameter to a command. class Flag : public DoNotCopyOrMove { public: typedef boost::program_options::value_semantic* Specification; // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief name returns the name of the Flag. const Name& name() const; /// @brief description returns a human-readable description of the flag. const Description& description() const; /// @brief specify the program option of the flag. virtual void specify_option(Specification& spec) = 0; protected: /// @brief Flag creates a new instance, initializing name and description /// from the given values. Flag(const Name& name, const Description& description); private: Name name_; Description description_; }; /// @brief TypedFlag implements Flag relying on operator<< and operator>> to /// read/write values to/from strings. template class TypedFlag : public Flag { public: typedef std::shared_ptr> Ptr; TypedFlag(const Name& name, const Description& description) : Flag{name, description} {} /// @brief value installs the given value in the flag. TypedFlag& value(const T& value) { value_ = value; return *this; } /// @brief value returns the optional value associated with the flag. const Optional& value() const { return value_; } /// @brief Option generated by specify_option tries to unwrap a value /// of type T from value. void specify_option(Flag::Specification& spec) override { spec = boost::program_options::value()->notifier([&](const std::string& s) { std::stringstream ss{s}; T value; ss >> value; value_ = value; }); } private: Optional value_; }; /// @brief TypedReferenceFlag implements Flag, relying on operator<> to /// convert to/from string representations, /// updating the given mutable reference to a value of type T. template class TypedReferenceFlag : public Flag { public: // Safe us some typing. typedef std::shared_ptr> Ptr; /// @brief TypedReferenceFlag initializes a new instance with name, /// description and value. TypedReferenceFlag(const Name& name, const Description& description, T& value) : Flag{name, description}, value_{value} {} /// @brief Option generated by specify_option tries to unwrap a value of type T /// from value, relying on operator>> to read from given string s. void specify_option(Flag::Specification& spec) override { spec = boost::program_options::value()->notifier([&](const std::string& s) { std::stringstream ss{s}; ss >> value_.get(); }); } private: std::reference_wrapper value_; }; /// @brief OptionalTypedReferenceFlag handles Optional references, making /// sure that a value is always read on notify, even if the Optional wasn't /// initialized previously. template class OptionalTypedReferenceFlag : public Flag { public: typedef std::shared_ptr> Ptr; OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional& value) : Flag{name, description}, value_{value} {} /// @brief Option generated by specify_option tries to unwrap a value of /// type T from value. void specify_option(Flag::Specification& spec) override { spec = boost::program_options::value()->notifier([&](const std::string& s) { std::stringstream ss{s}; T value; ss >> value; value_.get() = value; }); } private: std::reference_wrapper> value_; }; /// @brief BoolSwitchFlag implements Flag, updating the given mutable reference /// to a boolean value. class BoolSwitchFlag : public Flag { public: typedef std::shared_ptr Ptr; BoolSwitchFlag(const Name& name, const Description& description, bool& value) : Flag{name, description}, value_(value) {} /// @brief Option generated by specify_option tries to unwrap a boolean /// value from value. void specify_option(Flag::Specification& spec) override { spec = boost::program_options::bool_switch(&value_.get()); } private: std::reference_wrapper value_; }; /// @brief Command abstracts an individual command available from the daemon. class Command : public DoNotCopyOrMove { public: // Safe us some typing typedef std::shared_ptr Ptr; /// @brief FlagsMissing is thrown if at least one required flag is missing. struct FlagsMissing : public std::runtime_error { /// @brief FlagsMissing initializes a new instance. FlagsMissing(); }; /// @brief FlagsWithWrongValue is thrown if a value passed on the command line /// is invalid. struct FlagsWithInvalidValue : public std::runtime_error { /// @brief FlagsWithInvalidValue initializes a new instance. FlagsWithInvalidValue(); }; /// @brief Context bundles information passed to Command::run invocations. struct Context { std::istream& cin; ///< The std::istream that should be used for reading. std::ostream& cout; ///< The std::ostream that should be used for writing. std::vector args; ///< The command line args. }; /// @brief name returns the Name of the command. virtual Name name() const; /// @brief usage returns a short usage string for the command. virtual Usage usage() const; /// @brief description returns a longer string explaining the command. virtual Description description() const; /// @brief hidden returns if the command is hidden from the user or not. virtual bool hidden() const; /// @brief run puts the command to execution. virtual int run(const Context& context) = 0; /// @brief help prints information about a command to out. virtual void help(std::ostream& out) = 0; protected: /// @brief Command initializes a new instance with the given name, usage and /// description. Command(const Name& name, const Usage& usage, const Description& description, bool hidden = false); /// @brief name adjusts the name of the command to n. // virtual void name(const Name& n); /// @brief usage adjusts the usage string of the comand to u. // virtual void usage(const Usage& u); /// @brief description adjusts the description string of the command to d. // virtual void description(const Description& d); private: Name name_; Usage usage_; Description description_; bool hidden_; }; /// @brief CommandWithSubcommands implements Command, selecting one of a set of /// actions. class CommandWithSubcommands : public Command { public: typedef std::shared_ptr Ptr; typedef std::function Action; /// @brief CommandWithSubcommands initializes a new instance with the given /// name, usage and description. CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description); /// @brief command adds the given command to the set of known commands. CommandWithSubcommands& command(const Command::Ptr& command); /// @brief flag adds the given flag to the set of known flags. CommandWithSubcommands& flag(const Flag::Ptr& flag); // From Command int run(const Context& context) override; void help(std::ostream& out) override; private: std::unordered_map commands_; std::set flags_; }; /// @brief CommandWithFlagsAction implements Command, executing an Action after /// handling class CommandWithFlagsAndAction : public Command { public: typedef std::shared_ptr Ptr; typedef std::function Action; /// @brief CommandWithFlagsAndAction initializes a new instance with the given /// name, usage and description. Optionally the command can be marked as hidden. CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description, bool hidden = false); /// @brief flag adds the given flag to the set of known flags. CommandWithFlagsAndAction& flag(const Flag::Ptr& flag); /// @brief action installs the given action. CommandWithFlagsAndAction& action(const Action& action); // From Command int run(const Context& context) override; void help(std::ostream& out) override; private: std::set flags_; Action action_; }; namespace cmd { /// @brief HelpFor prints a help message for the given command on execution. class Help : public Command { public: /// @brief HelpFor initializes a new instance with the given reference to a /// cmd. explicit Help(Command& cmd); // From Command int run(const Context& context) override; void help(std::ostream& out) override; private: /// @cond Command& command; /// @endcond }; } /// @brief args returns a vector of strings assembled from argc and argv. std::vector args(int argc, char** argv); /// @brief make_flag returns a flag with the given name and description. template typename TypedFlag::Ptr make_flag(const Name& name, const Description& description) { return std::make_shared>(name, description); } /// @brief make_flag returns a flag with the given name and description, /// notifying updates to value. template typename TypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, T& value) { return std::make_shared>(name, desc, value); } /// @brief make_flag returns a flag with the given name and description, /// updating the given optional value. template typename OptionalTypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, Optional& value) { return std::make_shared>(name, desc, value); } /// @brief make_flag returns a flag with the given name and description, /// updating the given boolean value. inline BoolSwitchFlag::Ptr make_flag(const Name& name, const Description &desc, bool &value) { return std::make_shared(name, desc, value); } } // namespace cli } // namespace anbox #endif