1 /** @file 2 3 Powerful and easy-to-use command line parsing for ATS 4 5 @section license License 6 7 Licensed to the Apache Software Foundation (ASF) under one 8 or more contributor license agreements. See the NOTICE file 9 distributed with this work for additional information 10 regarding copyright ownership. The ASF licenses this file 11 to you under the Apache License, Version 2.0 (the 12 "License"); you may not use this file except in compliance 13 with the License. You may obtain a copy of the License at 14 15 http://www.apache.org/licenses/LICENSE-2.0 16 17 Unless required by applicable law or agreed to in writing, software 18 distributed under the License is distributed on an "AS IS" BASIS, 19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 See the License for the specific language governing permissions and 21 limitations under the License. 22 */ 23 24 #pragma once 25 26 #include <iostream> 27 #include <string> 28 #include <map> 29 #include <vector> 30 #include <functional> 31 #include <string_view> 32 33 // more than zero arguments 34 constexpr unsigned MORE_THAN_ZERO_ARG_N = ~0; 35 // more than one arguments 36 constexpr unsigned MORE_THAN_ONE_ARG_N = ~0 - 1; 37 // customizable indent for help message 38 constexpr int INDENT_ONE = 32; 39 constexpr int INDENT_TWO = 46; 40 41 namespace ts 42 { 43 using AP_StrVec = std::vector<std::string>; 44 // The class holding both the ENV and String arguments 45 class ArgumentData 46 { 47 public: 48 // bool to check if certain command/option is called 49 operator bool() const noexcept { return _is_called; } 50 // index accessing [] 51 std::string const & 52 operator[](int x) const 53 { 54 return _values.at(x); 55 } 56 // return the Environment variable 57 std::string const &env() const noexcept; 58 // iterator for arguments 59 AP_StrVec::const_iterator begin() const noexcept; 60 AP_StrVec::const_iterator end() const noexcept; 61 // index accessing 62 std::string const &at(unsigned index) const; 63 // access the first index, equivalent to at(0) 64 std::string const &value() const noexcept; 65 // size of _values 66 size_t size() const noexcept; 67 // return true if _values and _env_value are both empty 68 bool empty() const noexcept; 69 70 private: 71 bool _is_called = false; 72 // the environment variable 73 std::string _env_value; 74 // the arguments stored 75 AP_StrVec _values; 76 77 friend class Arguments; 78 }; 79 80 // The class holding all the parsed data after ArgParser::parse() 81 class Arguments 82 { 83 public: 84 Arguments(); 85 ~Arguments(); 86 87 ArgumentData get(std::string const &name); 88 89 void append(std::string const &key, ArgumentData const &value); 90 // Append value to the arg to the map of key 91 void append_arg(std::string const &key, std::string const &value); 92 // append env value to the map with key 93 void set_env(std::string const &key, std::string const &value); 94 // Print all we have in the parsed data to the console 95 void show_all_configuration() const; 96 /** Invoke the function associated with the parsed command. 97 @return The return value of the executed command (int). 98 */ 99 void invoke(); 100 // return true if there is any function to invoke 101 bool has_action() const; 102 103 private: 104 // A map of all the called parsed args/data 105 // Key: "command/option", value: ENV and args 106 std::map<std::string, ArgumentData> _data_map; 107 // The function associated. invoke() will call this func 108 std::function<void()> _action; 109 110 friend class ArgParser; 111 friend class ArgumentData; 112 }; 113 114 // Class of the ArgParser 115 class ArgParser 116 { 117 using Function = std::function<void()>; 118 119 public: 120 // Option structure: e.x. --arg -a 121 // Contains all information about certain option(--switch) 122 struct Option { 123 std::string long_option; // long option: --arg 124 std::string short_option; // short option: -a 125 std::string description; // help description 126 std::string envvar; // stored ENV variable 127 unsigned arg_num; // number of argument expected 128 std::string default_value; // default value of option 129 std::string key; // look-up key 130 }; 131 132 // Class for commands in a nested way 133 class Command 134 { 135 public: 136 // Constructor and destructor 137 Command(); 138 ~Command(); 139 /** Add an option to current command 140 @return The Option object. 141 */ 142 Command &add_option(std::string const &long_option, std::string const &short_option, std::string const &description, 143 std::string const &envvar = "", unsigned arg_num = 0, std::string const &default_value = "", 144 std::string const &key = ""); 145 146 /** Two ways of adding a sub-command to current command: 147 @return The new sub-command instance. 148 */ 149 Command &add_command(std::string const &cmd_name, std::string const &cmd_description, Function const &f = nullptr, 150 std::string const &key = ""); 151 Command &add_command(std::string const &cmd_name, std::string const &cmd_description, std::string const &cmd_envvar, 152 unsigned cmd_arg_num, Function const &f = nullptr, std::string const &key = ""); 153 /** Add an example usage of current command for the help message 154 @return The Command instance for chained calls. 155 */ 156 Command &add_example_usage(std::string const &usage); 157 /** Require subcommand/options for this command 158 @return The Command instance for chained calls. 159 */ 160 Command &require_commands(); 161 /** set the current command as default 162 @return The Command instance for chained calls. 163 */ 164 Command &set_default(); 165 166 protected: 167 // Main constructor called by add_command() 168 Command(std::string const &name, std::string const &description, std::string const &envvar, unsigned arg_num, Function const &f, 169 std::string const &key = ""); 170 // Helper method for add_option to check the validity of option 171 void check_option(std::string const &long_option, std::string const &short_option, std::string const &key) const; 172 // Helper method for add_command to check the validity of command 173 void check_command(std::string const &name, std::string const &key) const; 174 // Helper method for ArgParser::help_message 175 void output_command(std::ostream &out, std::string const &prefix) const; 176 // Helper method for ArgParser::help_message 177 void output_option() const; 178 // Helper method for ArgParser::parse 179 bool parse(Arguments &ret, AP_StrVec &args); 180 // The help & version messages 181 void help_message(std::string_view err = "") const; 182 void version_message() const; 183 // Helper method for parse() 184 void append_option_data(Arguments &ret, AP_StrVec &args, int index); 185 // The command name and help message 186 std::string _name; 187 std::string _description; 188 189 // Expected argument number 190 unsigned _arg_num = 0; 191 // Stored Env variable 192 std::string _envvar; 193 // An example usage can be added for the help message 194 std::string _example_usage; 195 // Function associated with this command 196 Function _f; 197 // look up key 198 std::string _key; 199 200 // list of all subcommands of current command 201 // Key: command name. Value: Command object 202 std::map<std::string, Command> _subcommand_list; 203 // list of all options of current command 204 // Key: option name. Value: Option object 205 std::map<std::string, Option> _option_list; 206 // Map for fast searching: <short option: long option> 207 std::map<std::string, std::string> _option_map; 208 209 // require command / option for this parser 210 bool _command_required = false; 211 212 friend class ArgParser; 213 }; 214 // Base class constructors and destructor 215 ArgParser(); 216 ArgParser(std::string const &name, std::string const &description, std::string const &envvar, unsigned arg_num, 217 Function const &f); 218 ~ArgParser(); 219 220 /** Add an option to current command with arguments 221 @return The Option object. 222 */ 223 Command &add_option(std::string const &long_option, std::string const &short_option, std::string const &description, 224 std::string const &envvar = "", unsigned arg_num = 0, std::string const &default_value = "", 225 std::string const &key = ""); 226 227 /** Two ways of adding command to the parser: 228 @return The new command instance. 229 */ 230 Command &add_command(std::string const &cmd_name, std::string const &cmd_description, Function const &f = nullptr, 231 std::string const &key = ""); 232 Command &add_command(std::string const &cmd_name, std::string const &cmd_description, std::string const &cmd_envvar, 233 unsigned cmd_arg_num, Function const &f = nullptr, std::string const &key = ""); 234 // give a default command to this parser 235 void set_default_command(std::string const &cmd); 236 /** Main parsing function 237 @return The Arguments object available for program using 238 */ 239 Arguments parse(const char **argv); 240 // Add the usage to global_usage for help_message(). Something like: traffic_blabla [--SWITCH [ARG]] 241 void add_global_usage(std::string const &usage); 242 // help message that can be called 243 void help_message(std::string_view err = "") const; 244 /** Require subcommand/options for this command 245 @return The Command instance for chained calls. 246 */ 247 Command &require_commands(); 248 // set the error message 249 void set_error(std::string e); 250 // get the error message 251 std::string get_error() const; 252 253 protected: 254 // Converted from 'const char **argv' for the use of parsing and help 255 AP_StrVec _argv; 256 // the top level command object for program use 257 Command _top_level_command; 258 // user-customized error message output 259 std::string _error_msg; 260 261 friend class Command; 262 friend class Arguments; 263 }; 264 265 } // namespace ts 266