/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2013-2016 Danny Robson */ #ifndef CRUFT_UTIL_CMDLINE_HPP #define CRUFT_UTIL_CMDLINE_HPP #include "introspection.hpp" #include "iterator/infix.hpp" #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// namespace cruft::cmdopt { /////////////////////////////////////////////////////////////////////////// class error : public std::exception { }; //------------------------------------------------------------------------- class invalid_key : public error { public: explicit invalid_key (std::string _key); const char* what (void) const noexcept override; private: const std::string m_key; }; //------------------------------------------------------------------------- class invalid_value : public error { public: explicit invalid_value (std::string _value); const char* what (void) const noexcept override; private: const std::string m_value; }; //------------------------------------------------------------------------- class invalid_null : public error { public: const char* what (void) const noexcept override; }; //------------------------------------------------------------------------- class invalid_required : public error { public: const char* what (void) const noexcept override; }; //------------------------------------------------------------------------- class unhandled_argument : public error { public: explicit unhandled_argument (int index); const char* what (void) const noexcept override; int index (void) const noexcept; private: const int m_index; }; /////////////////////////////////////////////////////////////////////////// namespace option { class base { public: // we deal almost exclusively with vtables, so disable copying // just in case we do something stupid. base () = default; base (const base&) = delete; base& operator= (const base&) = delete; virtual ~base (); virtual void execute (void); virtual void execute (const char *restrict); virtual void start (void); virtual void finish (void); virtual const std::string& example (void) const = 0; bool required (void) const; bool required (bool); bool seen (void) const; bool seen (bool); private: bool m_required = false; bool m_seen = false; }; class null : public base { public: virtual void execute (void) override; virtual void execute (const char *restrict) override; virtual const std::string& example (void) const override; }; class present : public base { public: explicit present (bool&); explicit present (bool&&) = delete; using base::execute; virtual void execute (void) override; virtual const std::string& example (void) const override; virtual void finish (void) override; private: bool &m_data; }; namespace detail { template std::enable_if_t::value, const std::string&> value_example (void) { static const std::string EXAMPLE = std::string {"<"} + std::string {type_name ()} + std::string {">"}; return EXAMPLE; } template std::enable_if_t::value, const std::string&> value_example (void) { static const std::string EXAMPLE = [] (void) { std::ostringstream os; std::copy (std::cbegin (enum_traits::names), std::cend (enum_traits::names), iterator::infix_iterator (os, "|")); return os.str (); } (); return EXAMPLE; } } template class value : public base { public: explicit value (T &_data): m_data (_data) { } explicit value (T&&) = delete; using base::execute; void execute (const char *restrict str) override { try { std::istringstream is (str); is.exceptions ( std::istringstream::failbit | std::istringstream::badbit ); is >> m_data; } catch (...) { throw invalid_value (__func__); } seen (true); } const std::string& example (void) const override { return detail::value_example (); } const T& data (void) const& { return m_data; } T& data (void) & { return m_data; } T& data (T _data) & { return m_data = _data; } private: T& m_data; }; template <> inline void value::execute (const char *restrict str) { static const std::string TRUE_STRING[] = { "true", "yes", "y", "1" }; if (std::any_of (std::begin (TRUE_STRING), std::end (TRUE_STRING), [str] (auto i) { return i == str; })) { m_data = true; return; } static const std::string FALSE_STRING[] = { "false", "no", "n", "0" }; if (std::any_of (std::begin (FALSE_STRING), std::end (FALSE_STRING), [str] (auto i) { return i == str; })) { m_data = false; return; } base::execute (str); seen (true); } template class count : public value { public: explicit count (T&); explicit count (T&&) = delete; using value::execute; void execute (void) override; }; class bytes : public value { public: explicit bytes (size_t &_value): value (_value) { } using value::execute; void execute (const char *restrict) override; }; } //------------------------------------------------------------------------- class parser { public: template T& add (char shortname, std::string longname, std::string description, Args&&... args) { auto handler = std::make_unique (std::forward (args)...); T& ref = *handler; m_short.insert({ shortname, ref }); m_long.insert({ std::move (longname), ref }); m_options.push_back ({ std::move (description), std::move (handler) }); return ref; } template T& append (std::string description, Args&&...args) { auto handler = std::make_unique (std::forward (args)...); auto &ref = *handler; m_positional.push_back (ref); m_options.push_back ({ std::move (description), std::move (handler) }); return ref; } int scan (int argc, const char *const *argv); private: int parse_long (int pos, int argc, const char *const *argv); int parse_short (int pos, int argc, const char *const *argv); void print_help [[noreturn]] (int argc, const char *const *argv) const; std::map> m_short; std::map> m_long; std::vector> m_positional; struct entry { std::string description; std::unique_ptr handler; }; std::vector m_options; }; } #endif