bux API Reference 1.9.0
Static library of whatever are seen required in general purpose but not directly supported from Modern C++. Or whatever reusable originated from my side projects.
Loading...
Searching...
No Matches
EZArgs.cpp
Go to the documentation of this file.
1#include "EZArgs.h"
2#include <cstring> // strchr()
3#include <filesystem> // std::filesystem::path
4
5namespace bux {
6
7//
8// Implement Classes
9//
10std::string C_ErrorOrIndex::message() const
11{
12 return m_optIndex? std::format("argv[{}]: {}",*m_optIndex,m_message): m_message;
13}
14
15C_EZArgs::C_FlagDef &C_EZArgs::create_flag_def(std::string_view name, char short_name, std::string_view description)
22{
23 auto &dst = m_flags.emplace_back();
24 if (!name.empty() && name[0] == '-')
25 if (name[1] == '-')
26 dst.m_name = name.substr(2);
27 else
28 {
29 m_flags.pop_back();
30 RUNTIME_ERROR("Invalid flag name: {}", name);
31 }
32 else
33 dst.m_name = name;
34
35 dst.m_descOneLiner = description;
36 dst.m_shortName = short_name;
37 if (dst.m_name == "help")
38 m_helpShielded = true;
39 if (dst.m_shortName == 'h')
40 m_hShielded = true;
41
42 return dst;
43}
44
45std::string C_EZArgs::retro_path(const char *const argv[]) const
46{
47 std::vector<const char*> polish_args;
48 for (auto cur = this; cur; cur = cur->m_owner)
49 polish_args.emplace_back(*(argv--));
50
51 std::string ret;
52 for (auto i = polish_args.rbegin(); i != polish_args.rend(); ++i)
53 {
54 if (ret.empty())
55 ret = std::filesystem::path{*i}.filename().string();
56 else
57 {
58 ret += ' ';
59 ret += *i;
60 }
61 }
62 return ret;
63}
64
65C_ErrorOrIndex C_EZArgs::help_full(const char *const argv[]) const
66{
67 // Synthesize USAGE
68 std::string help = "USAGE: " + retro_path(argv);
69 std::string validActions;
70 switch (m_up2u.index())
71 {
72 case UP2U_NULL:
73 break;
74 case UP2U_LAYOUT:
75 {
76 auto &lo = std::get<UP2U_LAYOUT>(m_up2u);
77 const auto minPosArgs = lo.m_posCounts.empty()? lo.m_posArgs.size(): lo.m_posCounts.front();
78 for (size_t i = 0; i < minPosArgs; ++i)
79 ((help += " <") += lo.m_posArgs[i]) += '>';
80
81 std::string optionals;
82 if (lo.m_unlimited)
83 optionals = "...";
84
85 size_t ub = lo.m_posArgs.size();
86 for (auto i: lo.m_posCounts)
87 {
88 std::string t;
89 for (auto j = i; j < ub; ++j)
90 t += (t.empty()? "[<": "> <") + lo.m_posArgs[j];
91
92 if (!t.empty())
93 {
94 if (optionals.empty())
95 optionals = t + ">]";
96 else
97 optionals = t + "> " + optionals + ']';
98 }
99 ub = i;
100 }
101 if (!optionals.empty())
102 (help += ' ') += optionals;
103 }
104 break;
105 case UP2U_SUBCMD:
106 validActions += "VALID ACTIONS:\n";
107 {
108 auto &subcmds = std::get<UP2U_SUBCMD>(m_up2u);
109 std::string actions;
110 size_t positionalCount{};
111 for (auto &i: subcmds)
112 {
113 actions += actions.empty()? '(': '|';
114 actions += i.first;
115 ((validActions += " ") += i.first) += '\n';
116 if (!i.second.m_desc.empty())
117 ((validActions += '\t') += i.second.m_desc) += '\n';
118 if (i.second.m_up2u.index() != UP2U_NULL)
119 ++positionalCount;
120 }
121 (help += ' ') += actions;
122 if (!positionalCount)
123 help += ')';
124 else if (positionalCount < subcmds.size())
125 help += ") [...]";
126 else
127 help += ") ...";
128 }
129 }
130
131 for (auto cur = this; cur; cur = cur->m_owner)
132 for (auto &def: cur->m_flags)
133 {
134 help += " [-";
135 if (def.m_shortName)
136 help += def.m_shortName;
137 else
138 (help += '-') += def.m_name;
139
140 if (def.m_parse)
141 help += def.m_trigger? " [ARG]": " ARG";
142
143 help += ']';
144 }
145 if (!m_hShielded)
146 help += " [-h]";
147 else if (!m_helpShielded)
148 help += " [--help]";
149
150 help += '\n';
151
152 // Append DESCRIPTION if any
153 if (!m_desc.empty())
154 {
155 help += "\n"
156 "DESCRIPTION:\n"
157 " ";
158 help += m_desc;
159 }
160 if (help.back() != '\n')
161 help += '\n';
162
163 // '\n'-ended guaranteed
164 if (!validActions.empty())
165 help += validActions;
166 else
167 {
168 // Append flag helps if any
169 std::string flags = help_flags();
170 const char *help_flag{};
171 switch ((m_hShielded?0:2) + (m_helpShielded?0:1))
172 {
173 case 1:
174 help_flag = " --help\n";
175 break;
176 case 2:
177 help_flag = " -h\n";
178 break;
179 case 3:
180 help_flag = " -h, --help\n";
181 break;
182 }
183 if (help_flag)
184 flags.append(help_flag).append("\tDisplay this help and exit\n");
185 if (!flags.empty())
186 help += "\n"
187 "VALID FLAGS:\n" + flags;
188
189 flags.clear();
190 for (auto cur = m_owner; cur; cur = cur->m_owner)
191 flags += cur->help_flags();
192 if (!flags.empty())
193 help += "\n"
194 "INHERITED FLAGS:\n" + flags;
195 }
196
197 // '\n'-ended guaranteed
198 if (!m_details.empty())
199 {
200 help += "\n"
201 "DETAILED:\n"
202 " ";
203 help += m_details;
204 if (m_details.back() != '\n')
205 help += '\n';
206 }
207
208 // '\n'-ended guaranteed
209 return help;
210}
211
212std::string C_EZArgs::help_flags() const
213{
214 std::string help;
215 for (auto &def: m_flags)
216 {
217 help += " -";
218 if (def.m_shortName)
219 {
220 help += def.m_shortName;
221 if (!def.m_name.empty())
222 (help += ", --") += def.m_name;
223 }
224 else
225 (help += '-') += def.m_name;
226
227 if (def.m_parse)
228 help += def.m_trigger? " [ARG]": " ARG";
229
230 help += '\n';
231 if (!def.m_descOneLiner.empty())
232 (help += '\t').append(def.m_descOneLiner) += '\n';
233 }
234 return help;
235}
236
237std::string C_EZArgs::help_tip(const std::string &error, const char *const argv[]) const
238{
239 const char *helpFlag{};
240 if (!m_hShielded)
241 helpFlag = "-h";
242 else if (!m_helpShielded)
243 helpFlag = "--help";
244
245 if (helpFlag)
246 return std::format("{}\nType \"{} {}\" to read the help", error, retro_path(argv), helpFlag);
247
248 return error;
249}
250
251const C_EZArgs::C_FlagDef* C_EZArgs::find_shortname_def(char sname) const
252{
253 for (auto cur = this; cur; cur = cur->m_owner)
254 for (auto& def : cur->m_flags)
255 {
256 if (def.m_shortName == sname)
257 // Matched
258 return &def;
259 }
260 return nullptr;
261}
262
263const C_EZArgs::C_FlagDef* C_EZArgs::find_longname_def(std::string_view name) const
264{
265 for (auto cur = this; cur; cur = cur->m_owner)
266 for (auto& def : cur->m_flags)
267 {
268 if (def.m_name == name)
269 // Matched
270 return &def;
271 }
272 return nullptr;
273}
274
275bool C_EZArgs::is_valid_flag(const char *const *argv_rest, int argc_rest) const
276{
277 const auto arg = *argv_rest;
278 if (*arg != '-')
279 return false;
280
281 if (arg[1] == '-')
282 // Match full flag name
283 {
284 const auto flag = arg + 2;
285 const auto eqsign = strchr(flag, '=');
286 const auto flag_name = eqsign ? std::string_view(flag, size_t(eqsign - flag)) : std::string_view(flag);
287 return nullptr != find_longname_def(flag_name);
288 }
289
290 // Match every short flag
291 for (auto p = arg; *++p;)
292 if (auto const def = find_shortname_def(*p))
293 {
294 if (p[1])
295 {
296 if (!def->m_trigger)
297 return false;
298 }
299 else
300 {
301 if (def->m_trigger)
302 return true;
303
304 // (def->m_parse != nullptr) implied
305 if (argc_rest < 2 || is_valid_flag(argv_rest+1, argc_rest-1))
306 return false;
307 }
308 }
309 else
310 return false;
311
312 return true;
313}
314} //namespace bux
#define RUNTIME_ERROR(fmtStr,...)
Wrap FILE(DATE)#__LINE__ FUNCTION: msg into std::runtime_error.
Definition XException.h:32
THE common namespace of bux library.
Definition AtomiX.cpp:3
void append(const T &src, std::string &dst)
Definition Serialize.h:14
std::string m_message
Definition EZArgs.h:25
std::string message() const
Definition EZArgs.cpp:10
std::optional< size_t > m_optIndex
Definition EZArgs.h:26