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