VIPRA Documentation
Loading...
Searching...
No Matches
cli_arguments.hpp
1#pragma once
2
3#include <cstdint>
4#include <map>
5#include <set>
6#include <stdexcept>
7#include <string_view>
8#include <type_traits>
9#include <vector>
10#include "vipra/concepts/numeric.hpp"
11
12namespace VIPRA {
13enum ArgType : uint8_t {
14 OPTIONAL = 0x00,
15 REQUIRED = 0x01,
16 VALUE_REQUIRED = 0x02,
17 BOTH = 0x03
18};
19
20inline constexpr auto operator|(ArgType Lhs, ArgType Rhs) -> ArgType
21{
22 return static_cast<ArgType>(static_cast<std::underlying_type_t<ArgType>>(Lhs) |
23 static_cast<std::underlying_type_t<ArgType>>(Rhs));
24}
25
26inline constexpr auto operator&(ArgType Lhs, ArgType Rhs) -> ArgType
27{
28 return static_cast<ArgType>(static_cast<std::underlying_type_t<ArgType>>(Lhs) &
29 static_cast<std::underlying_type_t<ArgType>>(Rhs));
30}
31
32inline constexpr auto operator==(ArgType Lhs, ArgType Rhs) -> bool
33{
34 return static_cast<std::underlying_type_t<ArgType>>(Lhs) ==
35 static_cast<std::underlying_type_t<ArgType>>(Rhs);
36}
37
38inline constexpr auto operator!=(ArgType Lhs, ArgType Rhs) -> bool
39{
40 return static_cast<std::underlying_type_t<ArgType>>(Lhs) !=
41 static_cast<std::underlying_type_t<ArgType>>(Rhs);
42}
43
44class Args {
45 private:
46 using ArgsMap = std::map<std::string, std::string, std::less<>>;
47 using ArgsSet = std::set<std::string, std::less<>>;
48
49 public:
55 static void parse(int argc, const char* const* argv)
56 {
57 get_args().clear();
58 auto args = format(argc, argv);
59
60 for ( auto arg = std::next(args.begin()); arg != args.end(); ++arg ) {
61 if ( arg->length() < 2 ) throw std::runtime_error("Unknown Flag: " + *arg);
62 if ( (*arg)[0] != '-' ) throw std::runtime_error("Unknown Flag: " + *arg);
63
64 auto [flag, val] = split_arg(*arg);
65
66 auto [fullValid, fullError] = validate_flag(flag, val);
67 if ( fullValid ) {
68 get_args()[flag] = val;
69 continue;
70 }
71
72 auto [compositeValid, compositeError] = validate_composite_flag(flag);
73 if ( compositeValid ) {
74 continue;
75 }
76
77 throw std::runtime_error(fullError + ": " += flag);
78 }
79
80 check_required();
81 }
82
87 static void register_arg(std::string const& key)
88 {
89 get_arg_map()[key] = ArgType::OPTIONAL;
90 }
91
98 static void register_arg(std::string const& key, ArgType type)
99 {
100 if ( type & ArgType::REQUIRED ) get_req_set().insert(key);
101 get_arg_map()[key] = type;
102 }
103
110 static void register_arg(std::string const& key, std::string const& defaultValue,
111 ArgType type)
112 {
113 if ( type & ArgType::REQUIRED ) get_req_set().insert(key);
114 get_arg_map()[key] = type;
115 get_defaults()[key] = defaultValue;
116 }
117
125 static auto has(std::string_view key) -> bool
126 {
127 if ( get_defaults().find(key) != get_defaults().end() ) {
128 return true;
129 }
130
131 return get_args().find(key) != get_args().end();
132 }
133
140 template <typename val_t = std::string>
141 [[nodiscard]] static auto get(std::string_view key) -> val_t
142 {
143 auto iter = get_args().find(key);
144 if ( iter == get_args().end() ) {
145 iter = get_defaults().find(key);
146 if ( iter == get_defaults().end() )
147 throw std::out_of_range("Attempt To Access Missing Argument:" + std::string(key));
148 }
149
150 if constexpr ( Concepts::Numeric<val_t> ) {
151 return static_cast<val_t>(std::stof(iter->second));
152 }
153 else {
154 return iter->second;
155 }
156 }
157
163 static auto count() -> size_t { return get_args().size(); }
164
169 static void reset()
170 {
171 get_arg_map().clear();
172 get_req_set().clear();
173 }
174
175 private:
182 static auto split_arg(std::string_view arg) -> std::pair<std::string, std::string>
183 {
184 auto loc = arg.find('=');
185 if ( loc == std::string::npos )
186 return std::make_pair(std::string(arg.begin() + 1), "");
187
188 return std::make_pair(std::string(arg.begin() + 1, loc - 1),
189 std::string(arg.begin() + loc + 1, arg.length() - (loc + 1)));
190 }
191
192 static auto split_single_letter_args(std::string_view arg) -> std::vector<std::string>
193 {
194 std::vector<std::string> args;
195 for ( auto chr : arg ) {
196 args.emplace_back(1, chr);
197 }
198 return args;
199 }
200
207 static auto validate_composite_flag(std::string const& arg)
208 -> std::pair<bool, std::string>
209 {
210 auto flags = split_single_letter_args(arg);
211 for ( const auto& flag : flags ) {
212 auto valid = validate_flag(flag, "");
213 if ( ! valid.first ) return valid;
214 get_args()[flag] = "";
215 }
216
217 return {true, ""};
218 }
219
225 static auto validate_flag(std::string_view flag,
226 std::string_view value) -> std::pair<bool, std::string>
227 {
228 auto& argSet = get_arg_map();
229 auto iter = argSet.find(flag);
230 if ( iter == argSet.end() ) return {false, "Unknown Flag"};
231
232 auto type = iter->second;
233
234 if ( (type & ArgType::VALUE_REQUIRED) == ArgType::VALUE_REQUIRED ) {
235 if ( value.empty() ) return {false, "Required Value Missing"};
236 }
237
238 return {true, ""};
239 }
240
246 static void check_required()
247 {
248 for ( const auto& arg : get_req_set() ) {
249 if ( ! has(arg) ) {
250 throw std::runtime_error("Missing Flag: " + arg);
251 }
252 }
253 }
254
262 static auto format(int argc, const char* const* argv) -> std::vector<std::string>
263 {
264 return std::vector<std::string>{argv,
265 std::next(argv, static_cast<std::ptrdiff_t>(argc))};
266 }
267
268 static inline auto get_arg_map() -> std::map<std::string, ArgType, std::less<>>&
269 {
270 static std::map<std::string, ArgType, std::less<>> argSet;
271 return argSet;
272 }
273
274 static inline auto get_req_set() -> ArgsSet&
275 {
276 static ArgsSet reqSet;
277 return reqSet;
278 }
279
280 static inline auto get_args() -> ArgsMap&
281 {
282 static ArgsMap args;
283 return args;
284 }
285
286 static inline auto get_defaults() -> ArgsMap&
287 {
288 static ArgsMap defaults{};
289 return defaults;
290 }
291
292 public:
293 Args() = delete;
294};
295} // namespace VIPRA
static auto get(std::string_view key) -> val_t
Returns the argument with the given key.
Definition cli_arguments.hpp:141
static auto count() -> size_t
Returns the number of arguments.
Definition cli_arguments.hpp:163
static auto has(std::string_view key) -> bool
Returns true if the argument is present.
Definition cli_arguments.hpp:125
static void parse(int argc, const char *const *argv)
Parses out the arguments passed into the program.
Definition cli_arguments.hpp:55
static void reset()
Clears the registered arguments.
Definition cli_arguments.hpp:169
static void register_arg(std::string const &key, ArgType type)
Adds an argument flag that can be accepted, and sets its type.
Definition cli_arguments.hpp:98
static void register_arg(std::string const &key)
Adds an argument flag that can be accepted.
Definition cli_arguments.hpp:87
static void register_arg(std::string const &key, std::string const &defaultValue, ArgType type)
Adds an argument flag that can be accepted, sets its type and provides a default value.
Definition cli_arguments.hpp:110
Checks that a type is a numeric type.
Definition numeric.hpp:13