--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
+ *
+ */
+#ifndef BIOMETRY_UTIL_CLI_H_
+#define BIOMETRY_UTIL_CLI_H_
+
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+
+#include "anbox/do_not_copy_or_move.h"
+#include "anbox/optional.h"
+
+#include <boost/program_options.hpp>
+
+namespace anbox {
+namespace cli {
+
+template <std::size_t max>
+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 <std::size_t max>
+bool operator<(const SizeConstrainedString<max>& lhs,
+ const SizeConstrainedString<max>& rhs) {
+ return lhs.as_string() < rhs.as_string();
+}
+
+template <std::size_t max>
+bool operator==(const SizeConstrainedString<max>& lhs,
+ const SizeConstrainedString<max>& rhs) {
+ return lhs.as_string() == rhs.as_string();
+}
+
+template <std::size_t max>
+std::ostream& operator<<(std::ostream& out,
+ const SizeConstrainedString<max>& 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<Flag> 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 <typename T>
+class TypedFlag : public Flag {
+ public:
+ typedef std::shared_ptr<TypedFlag<T>> 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<T>& 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<std::string>()->notifier([&](const std::string& s) {
+ std::stringstream ss{s};
+ T value;
+ ss >> value;
+ value_ = value;
+ });
+ }
+
+ private:
+ Optional<T> 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 <typename T>
+class TypedReferenceFlag : public Flag {
+ public:
+ // Safe us some typing.
+ typedef std::shared_ptr<TypedReferenceFlag<T>> 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<std::string>()->notifier([&](const std::string& s) {
+ std::stringstream ss{s};
+ ss >> value_.get();
+ });
+ }
+
+ private:
+ std::reference_wrapper<T> value_;
+};
+
+/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making
+/// sure that a value is always read on notify, even if the Optional<T> wasn't
+/// initialized previously.
+template <typename T>
+class OptionalTypedReferenceFlag : public Flag {
+ public:
+ typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
+
+ OptionalTypedReferenceFlag(const Name& name, const Description& description,
+ Optional<T>& 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<std::string>()->notifier([&](const std::string& s) {
+ std::stringstream ss{s};
+ T value;
+ ss >> value;
+ value_.get() = value;
+ });
+ }
+
+ private:
+ std::reference_wrapper<Optional<T>> value_;
+};
+
+/// @brief BoolSwitchFlag implements Flag, updating the given mutable reference
+/// to a boolean value.
+class BoolSwitchFlag : public Flag {
+ public:
+ typedef std::shared_ptr<BoolSwitchFlag> 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<bool> value_;
+};
+
+/// @brief Command abstracts an individual command available from the daemon.
+class Command : public DoNotCopyOrMove {
+ public:
+ // Safe us some typing
+ typedef std::shared_ptr<Command> 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<std::string> 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<CommandWithSubcommands> Ptr;
+ typedef std::function<int(const Context&)> 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<std::string, Command::Ptr> commands_;
+ std::set<Flag::Ptr> flags_;
+};
+
+/// @brief CommandWithFlagsAction implements Command, executing an Action after
+/// handling
+class CommandWithFlagsAndAction : public Command {
+ public:
+ typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
+ typedef std::function<int(const Context&)> 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<Flag::Ptr> 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<std::string> args(int argc, char** argv);
+
+/// @brief make_flag returns a flag with the given name and description.
+template <typename T>
+typename TypedFlag<T>::Ptr make_flag(const Name& name,
+ const Description& description) {
+ return std::make_shared<TypedFlag<T>>(name, description);
+}
+
+/// @brief make_flag returns a flag with the given name and description,
+/// notifying updates to value.
+template <typename T>
+typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name,
+ const Description& desc,
+ T& value) {
+ return std::make_shared<TypedReferenceFlag<T>>(name, desc, value);
+}
+
+/// @brief make_flag returns a flag with the given name and description,
+/// updating the given optional value.
+template <typename T>
+typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name,
+ const Description& desc,
+ Optional<T>& value) {
+ return std::make_shared<OptionalTypedReferenceFlag<T>>(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<BoolSwitchFlag>(name, desc, value);
+}
+
+} // namespace cli
+} // namespace anbox
+
+#endif