/* * 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 */ #pragma once #include "introspection/name.hpp" #include "introspection/enum_manual.hpp" #include "iterator/infix.hpp" #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; } /////////////////////////////////////////////////////////////////////////// namespace option { class base { public: base (); // we deal almost exclusively with vtables, so disable copying // just in case we do something stupid. 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; /// Sets a callback query to determine if the option is required. /// /// The default is `return false;` void required (std::function); /// Sets a constant value function for the required query. /// The value provided will be unconditionally returned. void required (bool); /// Tests if the option is required by invoking the stored query. bool required (void) const; /// Tests if the option has been seen. /// /// Not to be confused with `fulfilled` which describes whether /// the `seen` and `required` constraints are both satisfied. bool seen (void) const; /// Sets the seen flag of the option to the value provided. bool seen (bool); /// Tests if all `required` and `seen` constraints have been /// satisfied. /// /// If there are no `required` constraints it return true. bool fulfilled (void) const; private: std::function m_required; 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 {cruft::introspection::name::bare ()} + 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 (introspection::enum_traits::names), std::cend (introspection::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); } /// Returns a callback that tests if this option has been set to /// a specified value. /// /// If the option has not been seen it is treated as /// unconditionally not equal. /// /// The returned function is handy to use in the `required` /// clauses of dependent options. template std::function is (TargetT &&_target)& { return [&, target = std::forward(_target)] (void) { return seen () && m_data == target; }; } 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 const &longname, std::string const &description, Args&&... args) { auto handler = std::make_unique (std::forward (args)...); T& ref = *handler; m_short.insert({ shortname, ref }); m_long.insert({ longname, ref }); m_options.push_back ({ description, std::move (handler) }); return ref; } template T& append (std::string const &description, Args&&...args) { auto handler = std::make_unique (std::forward (args)...); auto &ref = *handler; m_positional.push_back (ref); m_options.push_back ({ 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; }; }