1 #ifndef parameters_parser_hh_INCLUDED
2 #define parameters_parser_hh_INCLUDED
3 
4 #include "exception.hh"
5 #include "hash_map.hh"
6 #include "meta.hh"
7 #include "array_view.hh"
8 #include "optional.hh"
9 #include "flags.hh"
10 #include "string.hh"
11 #include "string_utils.hh"
12 
13 namespace Kakoune
14 {
15 
16 using ParameterList = ConstArrayView<String>;
17 
18 struct parameter_error : public runtime_error
19 {
20     using runtime_error::runtime_error;
21 };
22 
23 struct unknown_option : public parameter_error
24 {
unknown_optionKakoune::unknown_option25     unknown_option(StringView name)
26         : parameter_error(format("unknown option '{}'", name)) {}
27 };
28 
29 struct missing_option_value: public parameter_error
30 {
missing_option_valueKakoune::missing_option_value31     missing_option_value(StringView name)
32         : parameter_error(format("missing value for option '{}'", name)) {}
33 };
34 
35 struct wrong_argument_count : public parameter_error
36 {
wrong_argument_countKakoune::wrong_argument_count37     wrong_argument_count() : parameter_error("wrong argument count") {}
38 };
39 
40 struct SwitchDesc
41 {
42     bool takes_arg;
43     String description;
44 };
45 
46 using SwitchMap = HashMap<String, SwitchDesc, MemoryDomain::Commands>;
47 
48 String generate_switches_doc(const SwitchMap& opts);
49 
50 struct ParameterDesc
51 {
52     enum class Flags
53     {
54         None = 0,
55         SwitchesOnlyAtStart   = 0b0001,
56         SwitchesAsPositional  = 0b0010,
57         IgnoreUnknownSwitches = 0b0100
58     };
with_bit_ops(Meta::Type<Flags>)59     friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
60 
61     SwitchMap switches;
62     Flags flags = Flags::None;
63     size_t min_positionals = 0;
64     size_t max_positionals = -1;
65 };
66 
67 // ParametersParser provides tools to parse command parameters.
68 // There are 3 types of parameters:
69 //  * unnamed options, which are accessed by position (ignoring named ones)
70 //  * named boolean options, which are enabled using '-name' syntax
71 //  * named string options,  which are defined using '-name value' syntax
72 struct ParametersParser
73 {
74     // the options defines named options, if they map to true, then
75     // they are understood as string options, else they are understood as
76     // boolean option.
77     ParametersParser(ParameterList params, const ParameterDesc& desc);
78 
79     // Return a valid optional if the switch was given, with
80     // a non empty StringView value if the switch took an argument.
81     Optional<StringView> get_switch(StringView name) const;
82 
83     struct iterator : std::iterator<std::forward_iterator_tag, String>
84     {
iteratorKakoune::ParametersParser::iterator85         iterator(const ParametersParser& parser, size_t index)
86             : m_parser(parser), m_index(index) {}
87 
operator *Kakoune::ParametersParser::iterator88         const String& operator*() const { return m_parser[m_index]; }
operator ->Kakoune::ParametersParser::iterator89         const String* operator->() const { return &m_parser[m_index]; }
90 
operator ++Kakoune::ParametersParser::iterator91         iterator& operator++() { ++m_index; return *this; }
operator ++Kakoune::ParametersParser::iterator92         iterator operator++(int) { auto copy = *this; ++m_index; return copy; }
93 
operator ==Kakoune::ParametersParser::iterator94         bool operator==(const iterator& other) const
95         {
96             kak_assert(&m_parser == &other.m_parser);
97             return m_index == other.m_index;
98         }
99 
operator !=Kakoune::ParametersParser::iterator100         bool operator!=(const iterator& other) const
101         {
102             return not (*this == other);
103         }
104 
105     private:
106         const ParametersParser& m_parser;
107         size_t                  m_index;
108     };
109 
110     // positional parameters count
positional_countKakoune::ParametersParser111     size_t positional_count() const { return m_positional_indices.size(); }
112 
113     // access positional parameter by index
operator []Kakoune::ParametersParser114     const String& operator[] (size_t index) const
115     {
116         kak_assert(index < positional_count());
117         return m_params[m_positional_indices[index]];
118     }
119 
positionals_fromKakoune::ParametersParser120     ConstArrayView<String> positionals_from(size_t first) const
121     {
122         // kak_assert(m_desc.flags & (ParameterDesc::Flags::SwitchesOnlyAtStart | ParameterDesc::Flags::SwitchesAsPositional));
123         return m_params.subrange(first < m_positional_indices.size() ? m_positional_indices[first] : -1);
124     }
125 
beginKakoune::ParametersParser126     iterator begin() const { return iterator(*this, 0); }
endKakoune::ParametersParser127     iterator end() const { return iterator(*this, m_positional_indices.size()); }
128 
129 private:
130     ParameterList m_params;
131     Vector<size_t, MemoryDomain::Commands> m_positional_indices;
132     HashMap<String, StringView> m_switches;
133 };
134 
135 }
136 
137 #endif // parameters_parser_hh_INCLUDED
138