Argx 1.2.2-build
Simple yet powerful argument parser made in C++
Loading...
Searching...
No Matches
Argx.cpp
Go to the documentation of this file.
1/* src/argx.cpp
2 * Owned and created by: pcannon09
3 */
4
5#include <string>
6#include <codecvt>
7#include <locale>
8#include <iostream>
9
10#include "../inc/Argx.hpp"
11#include "../inc/types.hpp"
12
13namespace argx
14{
15 // PRIVATE
16 std::vector<ARGXOptions> Argx::options;
17 std::vector<std::string> *Argx::mainArgs = nullptr;
18
19 unsigned int Argx::mainArgc;
20
21 // PUBLIC:
22#if defined(ARGX_AS_PYTHON_PACKAGE)
23 Argx::Argx(const std::string &id, const std::vector<std::string> &args)
24 : id(id)
25 {
26 this->mainArgs = new std::vector<std::string>(args);
27 this->mainArgc = args.size();
28 }
29
30 void Argx::destroy()
31 { this->~Argx(); }
32
33#else
34 Argx::Argx(const std::string &id, int argc, char *argv[])
35 : id(id)
36 {
37 this->mainArgs = new std::vector<std::string>(argv, argv + argc);
38 this->mainArgc = argc;
39
40 if (!this->mainArgs)
41 std::cerr << "`Args::mainArgs` is not valid for ID of " + id + " variable is NULL";
42 }
43#endif
44
46 { }
47
49 {
50 if (this->mainArgs) { delete this->mainArgs; this->mainArgs = nullptr; }
51 }
52
53 int Argx::getArgIDPos(const std::string &arg)
54 {
55 ARGXOptions option = this->getOption(arg);
56
57 int argPos = this->getArgPos(option.param);
58 int shortArgPos = this->getArgPos(option.sparam);
59
60 if (argPos >= 0) return argPos;
61 if (shortArgPos >= 0) return shortArgPos;
62
63 return -1;
64 }
65
66 std::string Argx::paramToID(const std::string &param)
67 {
68 std::string id;
69
70 for (const auto &option : this->options)
71 {
72 if (option.param == param || option.sparam == param)
73 return option.id;
74 }
75
76 return id;
77 }
78
79 int Argx::getArgPos(const std::string &arg)
80 {
81 if (!this->mainArgs)
82 return -2;
83
84 for (size_t i = 0; i < this->mainArgs->size(); ++i)
85 {
86 if (this->mainArgs->at(i) == arg)
87 return i;
88 }
89
90 return -1;
91 }
92
93 void Argx::add(ARGXOptions option) const
94 { this->options.emplace_back(option); }
95
96 int Argx::findParam(const std::string &id)
97 {
98 // First check if it's a main parameter
99 for (size_t i = 0; i < this->options.size(); i++)
100 {
101 if (this->options[i].id == id)
102 {
103 // Check if this main parameter exists in arguments
104 for (const std::string &arg : *this->mainArgs)
105 {
106 if (arg == this->options[i].param || arg == this->options[i].sparam)
107 {
108 return static_cast<int>(i);
109 }
110 }
111 }
112 }
113
114 // Then look for sub-parameters
115 for (const auto &opt : this->options)
116 {
117 // Check if the parent option exists in the arguments
118 bool parentExists = false;
119
120 for (const std::string &arg : *this->mainArgs)
121 {
122 if (arg == opt.param || arg == opt.sparam)
123 {
124 parentExists = true;
125 break;
126 }
127 }
128
129 if (parentExists)
130 {
131 // Find the index of the requested sub-parameter
132 for (size_t i = 0; i < opt.subParams.size(); i++)
133 {
134 if (opt.subParams[i].id == id) return static_cast<int>(i);
135 }
136 }
137 }
138
139 return -1; // Not found
140 }
141
142 bool Argx::paramExists(const std::string &id)
143 {
144 if (this->findParam(id) >= 0) return true;
145
146 return false;
147 }
148
149 bool Argx::subParamExists(const std::string &id)
150 {
151 for (const auto &p : this->options)
152 {
153 if (p.sparam == id) return true;
154 }
155
156 return false;
157 }
158
159 bool Argx::hasTag(const std::string &id, const std::string &tag)
160 {
161 int paramID = this->findParam(id);
162
163 if (paramID < 0) return false;
164
165 // Validate if tag from options is equal to this tag from function param
166 if (this->options[paramID].tag == tag) return true;
167
168 return false;
169 }
170
171 ARGXParam Argx::getParam(const std::string &id)
172 {
173 if (this->mainArgc <= 1) return {};
174
175 ARGXParam result;
176
177 // First, check if this is a top-level option
178 for (const auto &opt : this->options)
179 {
180 if (opt.id == id)
181 {
182 // Find the position of the main option in arguments
183 int mainOptionPos = -1;
184
185 for (size_t i = 0; i < this->mainArgs->size(); ++i)
186 {
187 if ((*this->mainArgs)[i] == opt.param || (*this->mainArgs)[i] == opt.sparam)
188 {
189 result.exists = true;
190 mainOptionPos = i;
191 break;
192 }
193 }
194
195 if (result.exists)
196 {
197 if (opt.hasSubParams || opt.hasAnySubParams)
198 {
199 // Check each sub-parameter
200 for (const auto &sub : opt.subParams)
201 {
202 bool subMatched = false;
203
204 // Look for sub-parameters after the main option
205 for (size_t i = mainOptionPos + 1; i < this->mainArgs->size(); ++i)
206 {
207 if ((*this->mainArgs)[i] == sub.param || (*this->mainArgs)[i] == sub.sparam)
208 {
209 subMatched = true;
210 break;
211 }
212 }
213
214 result.subExists.push_back(subMatched);
215 }
216 }
217
218 return result;
219 }
220 }
221 }
222
223 // If not found as top-level, check if it's a sub-parameter
224 for (const auto &opt : this->options)
225 {
226 // Find if the parent option exists and get its position
227 size_t parentPos = -1;
228
229 for (size_t i = 0; i < this->mainArgs->size(); ++i)
230 {
231 if ((*this->mainArgs)[i] == opt.param || (*this->mainArgs)[i] == opt.sparam)
232 {
233 parentPos = i;
234 break;
235 }
236 }
237
238 if (parentPos > -1 && (opt.hasSubParams || opt.hasAnySubParams))
239 {
240 // Check if the requested sub-parameter exists after the parent
241 for (const auto &sub : opt.subParams)
242 {
243 if (sub.id == id)
244 {
245 for (size_t i = parentPos + 1 ; i < this->mainArgs->size(); ++i)
246 {
247 if ((*this->mainArgs)[i] == sub.param || (*this->mainArgs)[i] == sub.sparam)
248 {
249 result.exists = true;
250 break;
251 }
252 }
253
254 if (!result.exists && parentPos + 1 < this->mainArgs->size())
255 {
256 std::string nextArg = (*this->mainArgs)[parentPos + 1];
257
258 if (nextArg == sub.param || nextArg == sub.sparam) result.exists = true;
259 }
260
261 // Handle any sub-sub-parameters if they exist
262 if (result.exists && (sub.hasSubParams || sub.hasAnySubParams))
263 {
264 for (const auto &subsub : sub.subParams)
265 {
266 bool subsubMatched = false;
267
268 for (size_t i = 0; i < this->mainArgs->size(); ++i)
269 {
270 if ((*this->mainArgs)[i] == subsub.param || (*this->mainArgs)[i] == subsub.sparam)
271 {
272 subsubMatched = true;
273 break;
274 }
275 }
276
277 result.subExists.push_back(subsubMatched);
278 }
279 }
280
281 return result;
282 }
283 }
284 }
285 }
286
287 return result;
288 }
289
290 bool Argx::getSubParam(const argx::ARGXParam &param, const std::string &id)
291 { return this->paramExists(id) && param.subExists[this->findParam(id)]; }
292
293 std::string Argx::createDocs(ARGXStyle style, const std::string &title, const std::string &mainInfo)
294 {
295 std::string contentStr;
296
297 if (style == ARGXStyle::Professional)
298 {
299 for (const auto &x : this->options)
300 {
301 // Main option header line
302 contentStr += "ID: " + x.id + "\n";
303 contentStr += "[ " + x.sparam + " | " + x.param;
304
305 if (x.hasSubParams && !x.subParams.empty())
306 {
307 contentStr += " [ ";
308
309 for (size_t i = 0; i < x.subParams.size(); ++i)
310 {
311 const auto &sub = x.subParams[i];
312
313 contentStr += sub.param;
314
315 if (i < x.subParams.size() - 1) contentStr += " | ";
316 else if (i <= x.subParams.size()) contentStr += ' ';
317 }
318
319 contentStr += "] ] ";
320 }
321
322 else contentStr += " ] ";
323
324 contentStr += x.info + "\n";
325
326 // Print all sub-options with sparam and param, aligned with ideographic spaces if there are
327 if (x.hasSubParams && !x.subParams.empty())
328 {
329 for (const auto &sub : x.subParams)
330 {
331 // Create ideographic spaces matching the length of main param for alignment
332 std::wstring wideSpaces(x.param.size(), L'\u3000');
333 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
334 std::string spacing = converter.to_bytes(wideSpaces);
335
336 contentStr += spacing + " [ " + sub.sparam + " | " + sub.param + " ] " + sub.info + "\n";
337 }
338 }
339 }
340 }
341
342 else if (style == ARGXStyle::Simple)
343 {
344 for (const auto &x : this->options)
345 {
346 contentStr += x.sparam + ", " + x.param + " - " + x.info + "\n";
347
348 if (x.hasSubParams && !x.subParams.empty())
349 {
350 for (const auto &sub : x.subParams)
351 {
352 contentStr += " " + sub.sparam + ", " + sub.param + " - " + sub.info + "\n";
353 }
354 }
355 }
356 } // ARGXStyle
357
358 return title + "\n" + mainInfo + "\n" + contentStr;
359 }
360
361 // int Argx::getWrongArgs(const std::vector<std::string> &argv)
362 // {
363 // int pos = 1;
364 // bool isNormalParam = true;
365 //
366 // for (unsigned int i = 1 ; i < argv.size() ; ++i)
367 // {
368 // const auto arg = argv[pos];
369 //
370 // pos = i;
371 //
372 // if (this->options.size() > i)
373 // {
374 // if (this->options[i].param == arg || this->options[i].sparam == arg)
375 // {
376 // if (this->options[i].hasSubParams || this->options[i].hasAnySubParams)
377 // {
378 // isNormalParam = false;
379 //
380 // break;
381 // }
382 // }
383 //
384 // else break;
385 // }
386 // }
387 //
388 // // if (this->mainArgs->size() > 2)
389 // // if (isNormalParam) return 1;
390 //
391 // return (isNormalParam ? pos : -pos);
392 // }
393
394 // Replace your existing getWrongArgs(...) with this (signature unchanged)
395 int Argx::getWrongArgs(const std::vector<std::string> &argv)
396 {
397 int pos = 1; // skip program name
398 bool isNormalParam = true;
399
400 while (pos < (int)argv.size())
401 {
402 const std::string &arg = argv[pos];
403
404 // Find matching top-level option
405 const ARGXOptions *matched = nullptr;
406
407 for (const auto &opt : this->options)
408 {
409 if (arg == opt.param || arg == opt.sparam)
410 {
411 matched = &opt;
412 break;
413 }
414 }
415
416 // unknown top-level arg
417 if (!matched) return (isNormalParam ? pos : -pos);
418
419 // matched a top-level option
420 isNormalParam = true;
421
422 if (matched->hasSubParams || matched->hasAnySubParams)
423 {
424 isNormalParam = false;
425
426 int scanPos = pos + 1;
427
428 while (scanPos < (int)argv.size())
429 {
430 const std::string &nextArg = argv[scanPos];
431
432 // Is it a declared subparam for this option?
433 bool isSub = false;
434
435 for (const auto &subOpt : matched->subParams)
436 {
437 if (nextArg == subOpt.param || nextArg == subOpt.sparam)
438 {
439 isSub = true;
440 break;
441 }
442 }
443
444 if (isSub)
445 {
446 // Consume that subparam and continue scanning
447 pos = scanPos;
448 ++scanPos;
449
450 continue;
451 }
452
453 // If not a subparam, is it a global option? (do **NOT** consume it)
454 bool isGlobalOpt = false;
455
456 for (const auto &globalOpt : this->options)
457 {
458 if (nextArg == globalOpt.param || nextArg == globalOpt.sparam)
459 {
460 isGlobalOpt = true;
461
462 break;
463 }
464 }
465
466 // valid global option follows; stop subparam scan and let outer loop handle it
467 if (isGlobalOpt) break;
468
469 // neither subparam nor global option; it's an invalid sub-parameter token
470 return -scanPos;
471 }
472 }
473
474 ++pos;
475 }
476
477 // If nothing wrong found; return your existing success codes
478 if (isNormalParam) return (this->mainArgs->size() > 2 ? 2 : 1);
479 else return (this->mainArgs->size() > 2 ? -2 : -1);
480 }
481
482 int Argx::formatWrongArgs(const int &_int)
483 {
484 if (_int < 0) return -_int; // Convert to unsigned SAFELY
485
486 return _int;
487 }
488
489 bool Argx::compareArgs(std::vector<ARGXOptions> options, std::vector<std::string> argv)
490 {
491 // iterate over argv and skip program name
492 for (size_t i = 1; i < argv.size(); ++i)
493 {
494 const std::string &arg = argv[i];
495
496 // find a matching top-level option
497 const ARGXOptions *matched = nullptr;
498
499 for (const auto &opt : options)
500 {
501 if (opt.sparam == arg || opt.param == arg)
502 {
503 matched = &opt;
504
505 break;
506 }
507 }
508
509 // No matched top-level option
510 if (!matched) return false;
511
512 // if option supports subparams, try to get them
513 if (matched->hasSubParams || matched->hasAnySubParams)
514 {
515 size_t j = i + 1;
516
517 while (j < argv.size())
518 {
519 const std::string &next = argv[j];
520
521 // Check the next declared subparam of `matched`
522 bool isSub = false;
523
524 for (const auto &sub : matched->subParams)
525 {
526 if (next == sub.param || next == sub.sparam)
527 {
528 isSub = true;
529
530 break;
531 }
532 }
533
534 // Get it and continue scanning for more subparams
535 if (isSub)
536 {
537 ++j;
538
539 continue;
540 }
541
542 // not a subparam then check if it's a known top-level option
543 bool isGlobal = false;
544
545 for (const auto &g : options)
546 {
547 if (next == g.param || next == g.sparam)
548 {
549 isGlobal = true;
550 break;
551 }
552 }
553
554
555 // stop scanning subparams; outer loop will handle this global option
556 if (isGlobal) break;
557
558 // neither a subparam nor a global option to an invalid sequence
559 return false;
560 }
561
562 // advance outer index to the last consumed token ( j - 1 ).
563 // outer for-loop will increment i, so set `i = j - 1` to continue at j
564 if (j > i + 1)
565 i = j - 1;
566 }
567 }
568
569 return true;
570 }
571
572 ARGXOptions Argx::getOption(const std::string &id)
573 {
574 for (const auto &x : this->options)
575 if (x.id == id) return x;
576
577 return {};
578 }
579
580 std::vector<std::string> Argx::getSubValue(const std::string &id)
581 {
582 // Use `Argx::getArgPos()` function for sub-params
583 size_t idPos = this->getArgPos(id) + 1;
584
585 if (idPos < 0 || idPos == std::string::npos)
586 return {this->getOption(id).defaultValue};
587
588 std::vector<std::string> values;
589
590 for (size_t i = idPos ; i < this->getMainArgs().size() ; i++)
591 {
592 // End of the sub-parameter finding
593 // Assume the search is done due to an existsing sub-param
594 if (i != idPos && this->subParamExists(this->getMainArgs()[i]))
595 break;
596
597 values.emplace_back(this->getMainArgs()[i]);
598 }
599
600 std::string defaultValue;
601
602 {
603 bool breakOut = false;
604
605 for (size_t i = 0; i < this->mainArgs->size(); ++i)
606 {
607 for (size_t j = 0 ; j < this->options.size() ; ++j)
608 {
609 if (this->options[i].subParams.size() > j)
610 {
611 if (this->options[i].subParams[j].id == id)
612 {
613 defaultValue = this->options[i].subParams[j].defaultValue;
614
615
616 breakOut = true;
617
618 break;
619 }
620 }
621 }
622
623 if (breakOut) break;
624 }
625 }
626
627 if (values.empty())
628 values.emplace_back(defaultValue);
629
630 return values;
631 }
632
633 std::vector<std::string> Argx::getMainArgs() const
634 { return *this->mainArgs; }
635
636 int Argx::getArgc() const
637 { return this->mainArgc; }
638
639 std::vector<ARGXOptions> Argx::getOptions() const
640 { return this->options; }
641
642 std::string Argx::getID() const
643 { return this->id; }
644}
645
std::string paramToID(const std::string &param)
Normal parameter or sub-paramter to its corresponding ID.
Definition Argx.cpp:66
Argx(const std::string &id, int argc, char *argv[])
Create Argx with the specific id, argc and argv
Definition Argx.cpp:34
ARGXParam getParam(const std::string &id)
Get the param from id
Definition Argx.cpp:171
std::vector< std::string > getSubValue(const std::string &id)
Get sub-parameter values, starting from the first value found until the first found value that corres...
Definition Argx.cpp:580
static unsigned int mainArgc
Definition Argx.hpp:25
std::vector< ARGXOptions > getOptions() const
Get main set options as ARGXOptions.
Definition Argx.cpp:639
static std::vector< std::string > * mainArgs
Definition Argx.hpp:23
std::vector< std::string > getMainArgs() const
Get main arguments from main() function argv
Definition Argx.cpp:633
std::string getID() const
Get Argx ID.
Definition Argx.cpp:642
int getArgPos(const std::string &arg)
Get argument position with specified arg
Definition Argx.cpp:79
int getArgIDPos(const std::string &arg)
Get argument using ID.
Definition Argx.cpp:53
int getArgc() const
Get the main options from the main() function as argc.
Definition Argx.cpp:636
bool paramExists(const std::string &id)
Get if param exists in the param options.
Definition Argx.cpp:142
bool hasTag(const std::string &id, const std::string &tag)
Check if the tag exists in option with ID of id
Definition Argx.cpp:159
int findParam(const std::string &id)
Find parameter and sub-parameter index.
Definition Argx.cpp:96
~Argx()
Deconstruct allocated objects.
Definition Argx.cpp:48
std::string createDocs(ARGXStyle style, const std::string &title, const std::string &mainInfo)
Create documentation for the parameters with the specific style, title and main information.
Definition Argx.cpp:293
bool subParamExists(const std::string &id)
Get if sub-param exists in the param options.
Definition Argx.cpp:149
void add(ARGXOptions option) const
Add param options.
Definition Argx.cpp:93
static std::vector< ARGXOptions > options
Definition Argx.hpp:22
int getWrongArgs(const std::vector< std::string > &argv)
Get the incorrect arguments and sub-arguments that were not registered.
Definition Argx.cpp:395
static int formatWrongArgs(const int &_int)
Format to a positive number if number is negative for a correct execution of code.
Definition Argx.cpp:482
bool compareArgs(std::vector< ARGXOptions > options, std::vector< std::string > argv)
Compare if options contains the required id, if the ID does not exist, return false.
Definition Argx.cpp:489
bool getSubParam(const argx::ARGXParam &param, const std::string &id)
Get the sub-param from id
Definition Argx.cpp:290
std::string id
Definition Argx.hpp:20
ARGXOptions getOption(const std::string &id)
Get Options from specified ID.
Definition Argx.cpp:572
Definition Argx.hpp:16
ARGXStyle
Definition types.hpp:10
std::string param
Param (param).
Definition types.hpp:36
std::vector< ARGXOptions > subParams
Definition types.hpp:54
std::string sparam
Short Param (sparam):
Definition types.hpp:41
std::string defaultValue
Definition types.hpp:48
bool hasAnySubParams
Definition types.hpp:51
std::vector< bool > subExists
Definition types.hpp:60