• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDABASE_UTILS_PANDARGS_H_
17 #define LIBPANDABASE_UTILS_PANDARGS_H_
18 
19 #include <algorithm>
20 #include <array>
21 #include <list>
22 #include <set>
23 #include <map>
24 #include <string>
25 #include <string_view>
26 #include <sstream>
27 #include <type_traits>
28 #include <vector>
29 #include <cerrno>
30 #include <optional>
31 #include <utility>
32 
33 #include "macros.h"
34 #include "string_helpers.h"
35 
36 namespace ark {
37 class PandArgBase;
38 // NOLINTNEXTLINE(readability-identifier-naming)
39 using sub_args_t = std::vector<PandArgBase *>;
40 // NOLINTNEXTLINE(readability-identifier-naming)
41 using arg_list_t = std::vector<std::string>;
42 using std::enable_if_t;
43 using std::is_same_v;
44 
45 enum class PandArgType : uint8_t { STRING, INTEGER, DOUBLE, BOOL, LIST, UINT32, UINT64, COMPOUND, NOTYPE };
46 
47 // Base class for panda argument
48 class PandArgBase {
49 public:
50     explicit PandArgBase(std::string name, std::string desc, PandArgType type = PandArgType::NOTYPE)
name_(std::move (name))51         : name_(std::move(name)), desc_(std::move(desc)), type_(type)
52     {
53     }
54 
GetType()55     PandArgType GetType() const
56     {
57         return type_;
58     }
59 
GetName()60     std::string GetName() const
61     {
62         return name_;
63     }
64 
GetDesc()65     std::string GetDesc() const
66     {
67         return desc_;
68     }
69 
WasSet()70     bool WasSet() const
71     {
72         return wasSet_;
73     }
74 
SetWasSet(bool value)75     void SetWasSet(bool value)
76     {
77         wasSet_ = value;
78     }
79 
80     virtual void ResetDefaultValue() = 0;
81 
82 private:
83     std::string name_;
84     std::string desc_;
85     PandArgType type_;
86     bool wasSet_ {false};
87 };
88 
89 template <typename T>
90 struct ArgView {
91     using Type = T;
92 };
93 
94 template <>
95 struct ArgView<std::string> {
96     // Use 'const &' instead of `string_view` because of `c_str()` use-cases.
97     using Type = const std::string &;
98 };
99 
100 template <>
101 struct ArgView<arg_list_t> {
102     // Use 'const &' instead of `string_view` because of `c_str()` use-cases.
103     using Type = const arg_list_t &;
104 };
105 
106 template <typename T,
107           enable_if_t<is_same_v<std::string, T> || is_same_v<double, T> || is_same_v<bool, T> || is_same_v<int, T> ||
108                       // CC-OFFNXT(G.FMT.10) project code style
109                       is_same_v<uint32_t, T> || is_same_v<uint64_t, T> || is_same_v<arg_list_t, T>> * = nullptr>
110 class PandArg : public PandArgBase {
111 public:
112     using ViewT = typename ArgView<T>::Type;
113 
114     explicit PandArg(const std::string &name, T defaultVal, const std::string &desc)
115         : PandArgBase(name, desc, this->EvalType()), defaultVal_(defaultVal), realVal_(defaultVal)
116     {
117     }
118 
119     explicit PandArg(const std::string &name, T defaultVal, const std::string &desc, PandArgType type)
120         : PandArgBase(name, desc, type), defaultVal_(defaultVal), realVal_(defaultVal)
121     {
122     }
123 
124     explicit PandArg(const std::string &name, int defaultVal, const std::string &desc, T minVal, T maxVal)
125         : PandArgBase(name, desc, this->EvalType()),
126           defaultVal_(defaultVal),
127           realVal_(defaultVal),
128           minMaxVal_(std::pair<T, T>(minVal, maxVal))
129     {
130     }
131 
132     explicit PandArg(const std::string &name, const arg_list_t &defaultVal, const std::string &desc,
133                      std::string delimiter)
134         : PandArgBase(name, desc, PandArgType::LIST),
135           defaultVal_ {defaultVal},
136           realVal_ {defaultVal},
137           delimiter_ {std::move(delimiter)}
138     {
139     }
140 
141     ViewT GetValue() const
142     {
143         return realVal_;
144     }
145 
146     ViewT GetDefaultValue() const
147     {
148         return defaultVal_;
149     }
150 
151     template <bool UPDATE_FLAG = true>
152     void SetValue(T val)
153     {
154         realVal_ = val;
155         // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
156         if constexpr (UPDATE_FLAG) {  // NOLINT(readability-braces-around-statements)
157             SetWasSet(true);
158         }
159     }
160 
161     void ResetDefaultValue() override
162     {
163         realVal_ = defaultVal_;
164     }
165 
166     std::optional<std::string> GetDelimiter() const
167     {
168         return delimiter_;
169     }
170     std::optional<std::pair<T, T>> GetMinMaxVal()
171     {
172         return minMaxVal_;
173     }
174 
175 private:
176     constexpr PandArgType EvalType()
177     {
178         // NOLINTNEXTLINE(bugprone-branch-clone)
179         if constexpr (is_same_v<std::string, T>) {  // NOLINT(readability-braces-around-statements)
180             return PandArgType::STRING;
181             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
182         } else if constexpr (is_same_v<double, T>) {
183             return PandArgType::DOUBLE;
184             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
185         } else if constexpr (is_same_v<bool, T>) {
186             return PandArgType::BOOL;
187             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
188         } else if constexpr (is_same_v<int, T>) {
189             return PandArgType::INTEGER;
190             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
191         } else if constexpr (is_same_v<uint32_t, T>) {
192             return PandArgType::UINT32;
193             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
194         } else if constexpr (is_same_v<uint64_t, T>) {
195             return PandArgType::UINT64;
196             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
197         } else if constexpr (is_same_v<arg_list_t, T>) {
198             return PandArgType::LIST;
199         }
200         UNREACHABLE();
201     }
202 
203     T defaultVal_;
204     T realVal_;
205 
206     // Only for integer arguments with range
207     std::optional<std::pair<T, T>> minMaxVal_;
208 
209     // Only for strings with delimiter
210     std::optional<std::string> delimiter_;
211 };
212 
213 class PandArgCompound : public PandArg<bool> {
214 public:
215     PandArgCompound(const std::string &name, const std::string &desc, std::initializer_list<PandArgBase *> subArgs)
216         : PandArg<bool>(name, false, desc, PandArgType::COMPOUND), subArgs_ {subArgs}
217     {
218     }
219 
220     PandArgBase *FindSubArg(std::string_view name)
221     {
222         auto res =
223             std::find_if(subArgs_.begin(), subArgs_.end(), [name](const auto &arg) { return arg->GetName() == name; });
224         return res == subArgs_.end() ? nullptr : *res;
225     }
226 
227     void ResetDefaultValue() override
228     {
229         PandArg<bool>::ResetDefaultValue();
230         std::for_each(subArgs_.begin(), subArgs_.end(), [](auto &arg) { arg->ResetDefaultValue(); });
231     }
232 
233     const auto &GetSubArgs() const
234     {
235         return subArgs_;
236     }
237 
238 private:
239     sub_args_t subArgs_;
240 };
241 
242 class PandArgParser {
243 public:
244     bool Add(PandArgBase *arg)
245     {
246         if (arg == nullptr) {
247             errstr_ += "pandargs: Can't add `nullptr` as an argument\n";
248             return false;
249         }
250         bool success = args_.insert(arg).second;
251         if (!success) {
252             errstr_ += "pandargs: Argument " + arg->GetName() + " has duplicate\n";
253         }
254         return success;
255     }
256 
257     bool PushBackTail(PandArgBase *arg)
258     {
259         if (arg == nullptr) {
260             errstr_ += "pandargs: Can't add `nullptr` as a tail argument\n";
261             return false;
262         }
263         if (std::find(tailArgs_.begin(), tailArgs_.end(), arg) != tailArgs_.end()) {
264             errstr_ += "pandargs: Tail argument " + arg->GetName() + " is already in tail arguments list\n";
265             return false;
266         }
267         tailArgs_.emplace_back(arg);
268         return true;
269     }
270 
271     bool PopBackTail()
272     {
273         if (tailArgs_.empty()) {
274             errstr_ += "pandargs: Nothing to pop back from tail arguments\n";
275             return false;
276         }
277         tailArgs_.pop_back();
278         return true;
279     }
280 
281     void EraseTail()
282     {
283         tailArgs_.erase(tailArgs_.begin(), tailArgs_.end());
284     }
285 
286     bool Parse(const std::vector<std::string> &argvVec)
287     {
288         InitDefault();
289         std::copy(argvVec.begin(), argvVec.end(), std::back_inserter(argvVec_));
290         return ParseArgs();
291     }
292 
293     bool Parse(int argc, const char *const argv[])  // NOLINT(modernize-avoid-c-arrays, hicpp-avoid-c-arrays)
294     {
295         InitDefault();
296         for (int i = 1; i < argc; i++) {
297             argvVec_.emplace_back(argv[i]);  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
298         }
299         return ParseArgs();
300     }
301 
302     /// Parses a string to the option's value.
303     bool ParseSingleArg(PandArgBase *option, const std::string_view &optionValue)
304     {
305         ASSERT(option != nullptr);
306         argvVec_ = {std::string(optionValue)};
307         argvIndex_ = 0;
308         argvIndex_ += ParseNextParam(option, argvVec_[argvIndex_]);
309         return errstr_.empty();
310     }
311 
312     /// Parses option's name and returns corresponding pointer.
313     PandArgBase *GetPandArg(const std::string_view &argName)
314     {
315         auto argIt = args_.find(argName);
316         return (argIt != args_.end()) ? *argIt : nullptr;
317     }
318 
319     std::string GetErrorString() const
320     {
321         return errstr_;
322     }
323 
324     void EnableTail()
325     {
326         tailFlag_ = true;
327     }
328 
329     void DisableTail()
330     {
331         tailFlag_ = false;
332     }
333 
334     bool IsTailEnabled() const
335     {
336         return tailFlag_;
337     }
338 
339     std::size_t GetTailSize() const
340     {
341         return tailArgs_.size();
342     }
343 
344     void EnableRemainder() noexcept
345     {
346         remainderFlag_ = true;
347     }
348 
349     void DisableRemainder() noexcept
350     {
351         remainderFlag_ = false;
352     }
353 
354     bool IsRemainderEnabled() const
355     {
356         return remainderFlag_;
357     }
358 
359     arg_list_t GetRemainder()
360     {
361         return remainder_;
362     }
363 
364     bool IsArgSet(PandArgBase *arg) const
365     {
366         return args_.find(arg) != args_.end();
367     }
368 
369     bool IsArgSet(const std::string &argName) const
370     {
371         return args_.find(argName) != args_.end();
372     }
373 
374     std::string GetHelpString() const
375     {
376         std::ostringstream helpstr;
377         for (auto i : args_) {
378             if (i->GetType() == PandArgType::COMPOUND) {
379                 auto arg = static_cast<PandArgCompound *>(i);
380                 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n";
381                 helpstr << "  Sub arguments:\n";
382                 for (auto subArg : arg->GetSubArgs()) {
383                     helpstr << "    " << subArg->GetName() << ": " << subArg->GetDesc() << "\n";
384                 }
385             } else {
386                 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n";
387             }
388         }
389         if (!tailArgs_.empty()) {
390             helpstr << "Tail arguments:\n";
391             for (auto i : tailArgs_) {
392                 helpstr << i->GetName() << ": " << i->GetDesc() << "\n";
393             }
394         }
395         return helpstr.str();
396     }
397 
398     std::string GetRegularArgs()
399     {
400         std::string argsStr;
401         std::string value;
402         for (auto i : args_) {
403             switch (i->GetType()) {
404                 case PandArgType::STRING:
405                     value = static_cast<PandArg<std::string> *>(i)->GetValue();
406                     break;
407                 case PandArgType::INTEGER:
408                     value = std::to_string(static_cast<PandArg<int> *>(i)->GetValue());
409                     break;
410                 case PandArgType::DOUBLE:
411                     value = std::to_string(static_cast<PandArg<double> *>(i)->GetValue());
412                     break;
413                 case PandArgType::BOOL:
414                     // NOLINTNEXTLINE(readability-implicit-bool-conversion)
415                     value = std::to_string(static_cast<PandArg<bool> *>(i)->GetValue());
416                     break;
417                 case PandArgType::UINT32:
418                     value = std::to_string(static_cast<PandArg<uint32_t> *>(i)->GetValue());
419                     break;
420                 case PandArgType::UINT64:
421                     value = std::to_string(static_cast<PandArg<uint64_t> *>(i)->GetValue());
422                     break;
423                 case PandArgType::LIST: {
424                     value = "";
425                     std::vector<std::string> valuesBuf = static_cast<PandArg<arg_list_t> *>(i)->GetValue();
426                     for (const auto &j : valuesBuf) {
427                         value += j + ", ";
428                     }
429                     break;
430                 }
431                 default:
432                     errstr_ += "Invalid argument type \"" + i->GetName() + "\"\n";
433                     break;
434             }
435             argsStr += DOUBLE_DASH + i->GetName() + "=" + value + "\n";
436         }
437         return argsStr;
438     }
439 
440 private:
441     struct PandArgPtrComparator {
442         // NOLINTNEXTLINE(readability-identifier-naming)
443         using is_transparent = void;
444         bool operator()(const PandArgBase *lhs, const PandArgBase *rhs) const
445         {
446             return lhs->GetName() < rhs->GetName();
447         }
448         bool operator()(std::string_view lhs, const PandArgBase *rhs) const
449         {
450             return lhs < rhs->GetName();
451         }
452         bool operator()(const PandArgBase *lhs, std::string_view rhs) const
453         {
454             return lhs->GetName() < rhs;
455         }
456     };
457 
458     bool ParseArgs()
459     {
460         while (argvIndex_ < argvVec_.size()) {
461             PandArgBase *parsedArg = ParseNextArg();
462             if (!errstr_.empty()) {
463                 return false;
464             }
465             if (argvIndex_ < argvVec_.size()) {
466                 argvIndex_ += ParseNextParam(parsedArg, argvVec_[argvIndex_]);
467                 if (!errstr_.empty()) {
468                     return false;
469                 }
470             }
471         }
472         return true;
473     }
474 
475     void InitDefault()
476     {
477         equalFlag_ = false;
478         tailParsedFlag_ = false;
479         argvVec_.clear();
480         argvIndex_ = 0;
481         errstr_ = "";
482         // reset tail
483         for (auto tailArg : tailArgs_) {
484             switch (tailArg->GetType()) {
485                 case PandArgType::STRING:
486                     static_cast<PandArg<std::string> *>(tailArg)->ResetDefaultValue();
487                     break;
488                 case PandArgType::INTEGER:
489                     static_cast<PandArg<int> *>(tailArg)->ResetDefaultValue();
490                     break;
491                 case PandArgType::DOUBLE:
492                     static_cast<PandArg<double> *>(tailArg)->ResetDefaultValue();
493                     break;
494                 case PandArgType::BOOL:
495                     static_cast<PandArg<bool> *>(tailArg)->ResetDefaultValue();
496                     break;
497                 case PandArgType::UINT32:
498                     static_cast<PandArg<uint32_t> *>(tailArg)->ResetDefaultValue();
499                     break;
500                 case PandArgType::UINT64:
501                     static_cast<PandArg<uint64_t> *>(tailArg)->ResetDefaultValue();
502                     break;
503                 case PandArgType::LIST:
504                     static_cast<PandArg<arg_list_t> *>(tailArg)->ResetDefaultValue();
505                     break;
506                 default:
507                     break;
508             }
509         }
510         // reset remainder
511         remainder_ = arg_list_t();
512     }
513 
514     PandArgBase *FindArg(std::string_view argName)
515     {
516         auto argIt = args_.find(argName);
517         if (argIt == args_.end()) {
518             errstr_.append("pandargs: Invalid option \"");
519             errstr_.append(argName);
520             errstr_.append("\"\n");
521             return nullptr;
522         }
523         return *argIt;
524     }
525 
526     bool ParseSubArgument(PandArgCompound *parentArg, std::string_view str)
527     {
528         size_t assignPos = str.find('=');
529         std::string_view argName = str.substr(0, assignPos);
530         auto arg = parentArg->FindSubArg(argName);
531         if (arg == nullptr) {
532             errstr_.append("pandargs: Invalid sub-argument \"");
533             errstr_.append(argName);
534             errstr_.append("\"\n");
535             return false;
536         }
537 
538         if (assignPos != std::string_view::npos) {
539             std::string_view valueStr = str.substr(assignPos + 1);
540             ParseNextParam(arg, valueStr);
541             if (!errstr_.empty()) {
542                 return false;
543             }
544         } else {
545             if (arg->GetType() != PandArgType::BOOL) {
546                 errstr_.append("pandargs: Only boolean arguments might have no value \"");
547                 errstr_.append(argName);
548                 errstr_.append("\"\n");
549                 return false;
550             }
551             static_cast<PandArg<bool> *>(arg)->SetValue(true);
552         }
553 
554         return true;
555     }
556 
557     PandArgBase *ParseCompoundArg(std::string_view argstr, size_t sepPos)
558     {
559         auto argName = argstr.substr(0, sepPos);
560 
561         auto arg = static_cast<PandArgCompound *>(FindArg(argName));
562         if (arg == nullptr) {
563             return nullptr;
564         }
565         if (arg->GetType() != PandArgType::COMPOUND) {
566             errstr_.append("pandargs: Following argument is not compound one or syntax error: \"");
567             errstr_.append(argName);
568             errstr_.append("\"\n");
569             return nullptr;
570         }
571 
572         arg->SetValue(true);
573 
574         auto subArgsStr = argstr.substr(sepPos + 1);
575         size_t start = 0;
576         for (size_t pos = subArgsStr.find(',', 0); pos != std::string_view::npos;
577              start = pos + 1, pos = subArgsStr.find(',', start)) {
578             auto argStr = subArgsStr.substr(start, pos - start);
579             if (!ParseSubArgument(arg, argStr)) {
580                 return nullptr;
581             }
582         }
583         if (start < subArgsStr.size()) {
584             if (!ParseSubArgument(arg, subArgsStr.substr(start))) {
585                 return nullptr;
586             }
587         }
588         argvIndex_++;
589         return arg;
590     }
591 
592     PandArgBase *ParseNextRegularArg()
593     {
594         std::string argstr = argvVec_[argvIndex_];
595 
596         const std::size_t sepFound = NextSeparator(argstr);
597 
598         const std::size_t sepCompound = argstr.find_first_of(':', 0);
599         if (sepCompound != std::string::npos && (sepFound == std::string::npos || sepCompound < sepFound)) {
600             return ParseCompoundArg(std::string_view(argstr).substr(DASH_COUNT), sepCompound - DASH_COUNT);
601         }
602 
603         std::string argName;
604 
605         if (sepFound != std::string::npos) {
606             equalFlag_ = true;
607             argvVec_[argvIndex_] = argstr.substr(sepFound + 1);
608             argName = argstr.substr(DASH_COUNT, sepFound - DASH_COUNT);
609         } else {
610             argName = argstr.substr(DASH_COUNT, sepFound);
611             // check if there is next argv element to iterate into
612             if (argvIndex_ + 1 < argvVec_.size()) {
613                 argvIndex_++;
614             } else {
615                 argvVec_[argvIndex_] = "";
616             }
617         }
618 
619         PandArgBase *arg = FindArg(argName);
620         if (arg == nullptr) {
621             return nullptr;
622         }
623 
624         if (arg->GetType() == PandArgType::COMPOUND) {
625             // It is forbidden to explicitly set compound option, e.g. `--compound=true`, must be `--compound`.
626             if (sepFound != std::string::npos) {
627                 errstr_.append("pandargs: Compound option can not be explicitly set \"");
628                 errstr_.append(argName);
629                 errstr_.append("\"\n");
630                 return nullptr;
631             }
632             static_cast<PandArgCompound *>(arg)->SetValue(true);
633         }
634 
635         return arg;
636     }
637 
638     PandArgBase *ParseNextArg()
639     {
640         PandArgBase *arg = nullptr;
641         std::string argstr = argvVec_[argvIndex_];
642         equalFlag_ = false;
643 
644         // NOTE: currently we have only double dash argument prefix
645         std::size_t dashesFound = argstr.find(DOUBLE_DASH);
646         if (dashesFound == 0 && argstr.size() > DASH_COUNT) {
647             // regular argument
648             return ParseNextRegularArg();
649         }
650 
651         if (dashesFound == 0 && argstr.size() == DASH_COUNT) {
652             // remainder argument
653             if (!remainderFlag_) {
654                 errstr_.append("pandargs: Remainder arguments are not enabled\n");
655                 errstr_.append("pandargs: Remainder found at literal \"");
656                 errstr_.append(argstr);
657                 errstr_.append("\"\n");
658                 return nullptr;
659             }
660 
661             argvIndex_++;
662             ParseRemainder();
663         } else if (dashesFound > 0) {
664             // tail argument, N.B. std::string::npos > 0
665             if (!tailFlag_) {
666                 errstr_.append("pandargs: Tail arguments are not enabled\n");
667                 errstr_.append("pandargs: Tail found at literal \"");
668                 errstr_.append(argstr);
669                 errstr_.append("\"\n");
670                 return nullptr;
671             }
672             if (tailParsedFlag_) {
673                 errstr_.append("pandargs: Too many tail arguments\n");
674                 return nullptr;
675             }
676             ParseTail();
677 
678             if (argvIndex_ < argvVec_.size()) {
679                 if (argvVec_[argvIndex_] != DOUBLE_DASH && !remainderFlag_) {
680                     errstr_ += "pandargs: Too many tail arguments given\n";
681                 }
682             }
683         } else {
684             errstr_.append("pandargs: Invalid option \"");
685             errstr_.append(argstr);
686             errstr_.append("\"\n");
687             UNREACHABLE();
688         }
689         return arg;
690     }
691 
692     void ParseTail()
693     {
694         for (auto &tailArg : tailArgs_) {
695             switch (tailArg->GetType()) {
696                 case PandArgType::STRING:
697                     argvIndex_ +=
698                         ParseStringArgParam(static_cast<PandArg<std::string> *>(tailArg), argvVec_[argvIndex_]);
699                     break;
700                 case PandArgType::INTEGER:
701                     argvIndex_ += ParseIntArgParam(static_cast<PandArg<int> *>(tailArg), argvVec_[argvIndex_]);
702                     break;
703                 case PandArgType::DOUBLE:
704                     argvIndex_ += ParseDoubleArgParam(static_cast<PandArg<double> *>(tailArg), argvVec_[argvIndex_]);
705                     break;
706                 case PandArgType::BOOL:
707                     argvIndex_ += ParseBoolArgParam(static_cast<PandArg<bool> *>(tailArg), argvVec_[argvIndex_], true);
708                     break;
709                 case PandArgType::UINT32:
710                     argvIndex_ += ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(tailArg), argvVec_[argvIndex_]);
711                     break;
712                 case PandArgType::UINT64:
713                     argvIndex_ += ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(tailArg), argvVec_[argvIndex_]);
714                     break;
715                 case PandArgType::LIST:
716                     argvIndex_ += ParseListArgParam(static_cast<PandArg<arg_list_t> *>(tailArg), argvVec_[argvIndex_]);
717                     break;
718                 default:
719                     errstr_.append("pandargs: Invalid tail option type: \"");
720                     errstr_.append(tailArg->GetName());
721                     errstr_.append("\"\n");
722                     UNREACHABLE();
723                     break;
724             }
725             if (argvIndex_ >= argvVec_.size() || !errstr_.empty()) {
726                 break;
727             }
728         }
729         tailParsedFlag_ = true;
730     }
731 
732     void ParseRemainder()
733     {
734         remainder_ = arg_list_t(argvVec_.begin() + static_cast<arg_list_t::iterator::difference_type>(argvIndex_),
735                                 argvVec_.end());
736         argvIndex_ = argvVec_.size();
737     }
738 
739     size_t ParseNextParam(PandArgBase *arg, std::string_view argstr)
740     {
741         if (argvIndex_ >= argvVec_.size() || arg == nullptr) {
742             return 0;
743         }
744         switch (arg->GetType()) {
745             case PandArgType::STRING:
746                 return ParseStringArgParam(static_cast<PandArg<std::string> *>(arg), argstr);
747             case PandArgType::INTEGER:
748                 return ParseIntArgParam(static_cast<PandArg<int> *>(arg), argstr);
749             case PandArgType::DOUBLE:
750                 return ParseDoubleArgParam(static_cast<PandArg<double> *>(arg), argstr);
751             case PandArgType::BOOL:
752                 return ParseBoolArgParam(static_cast<PandArg<bool> *>(arg), argstr);
753             case PandArgType::UINT32:
754                 return ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(arg), argstr);
755             case PandArgType::UINT64:
756                 return ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(arg), argstr);
757             case PandArgType::LIST:
758                 return ParseListArgParam(static_cast<PandArg<arg_list_t> *>(arg), argstr);
759             case PandArgType::COMPOUND:
760                 return argstr.empty() ? 1 : 0;
761             case PandArgType::NOTYPE:
762                 errstr_.append("pandargs: Invalid option type: \"");
763                 errstr_.append(arg->GetName());
764                 errstr_.append("\"\n");
765                 UNREACHABLE();
766                 break;
767             default:
768                 UNREACHABLE();
769                 break;
770         }
771         return 0;
772     }
773 
774     std::size_t ParseStringArgParam(PandArg<std::string> *arg, std::string_view argstr)
775     {
776         arg->SetValue(std::string(argstr));
777         return 1;
778     }
779 
780     std::size_t ParseIntArgParam(PandArg<int> *arg, std::string_view argstr)
781     {
782         std::string paramStr(argstr);
783         if (IsIntegerNumber(paramStr)) {
784             int64_t result = 0;
785             errno = 0;
786             if (StartsWith(paramStr, "0x")) {
787                 const int hex = 16;
788                 result = std::strtol(paramStr.c_str(), nullptr, hex);
789             } else {
790                 const int dec = 10;
791                 result = std::strtol(paramStr.c_str(), nullptr, dec);
792             }
793 
794             int num = static_cast<int>(result);
795             if (num != result || errno == ERANGE) {
796                 errstr_ +=
797                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
798 
799                 return 1;
800             }
801 
802             if (IsIntegerArgInRange(arg, num)) {
803                 arg->SetValue(num);
804             } else {
805                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
806                            paramStr + "\"\n";
807             }
808         } else {
809             errstr_ +=
810                 "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + paramStr + "\"\n";
811         }
812         return 1;
813     }
814 
815     std::size_t ParseDoubleArgParam(PandArg<double> *arg, std::string_view argstr)
816     {
817         std::string paramStr(argstr);
818         if (IsRationalNumber(paramStr)) {
819             arg->SetValue(strtod(paramStr.data(), nullptr));
820         } else {
821             errstr_ +=
822                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
823         }
824         return 1;
825     }
826 
827     std::size_t ParseBoolArgParam(PandArg<bool> *arg, std::string_view argstr, bool isTailParam = false)
828     {
829         std::string paramStr(argstr);
830 
831         // if not a tail argument, assume two following cases
832         if (!isTailParam) {
833             arg->SetValue(true);
834             // bool with no param, next argument comes right after
835             if (StartsWith(paramStr, DOUBLE_DASH)) {
836                 // check that bool param comes without "="
837                 if (equalFlag_) {
838                     SetBoolUnexpectedValueError(arg, paramStr);
839                 }
840                 return 0;
841             }
842             // OR bool arg at the end of arguments line
843             if (paramStr.empty()) {
844                 // check that bool param comes without "="
845                 if (equalFlag_) {
846                     SetBoolUnexpectedValueError(arg, paramStr);
847                 }
848                 return 1;
849             }
850         }
851 
852         constexpr std::array<std::string_view, 3> TRUE_VALUES = {"on", "true", "1"};
853         constexpr std::array<std::string_view, 3> FALSE_VALUES = {"off", "false", "0"};
854 
855         for (const auto &i : TRUE_VALUES) {
856             if (paramStr == i) {
857                 arg->SetValue(true);
858                 return 1;
859             }
860         }
861         for (const auto &i : FALSE_VALUES) {
862             if (paramStr == i) {
863                 arg->SetValue(false);
864                 return 1;
865             }
866         }
867 
868         // if it's not a part of tail argument,
869         // assume that it's bool with no param,
870         // preceiding tail argument
871         if (!isTailParam) {
872             // check that bool param came without "="
873             if (equalFlag_) {
874                 SetBoolUnexpectedValueError(arg, paramStr);
875             } else {
876                 arg->SetValue(true);
877             }
878         } else {
879             errstr_ +=
880                 "pandargs: Tail argument " + arg->GetName() + " has unexpected parameter value " + paramStr + "\n";
881             arg->ResetDefaultValue();
882         }
883 
884         return 0;
885     }
886 
887     std::size_t ParseUint64ArgParam(PandArg<uint64_t> *arg, std::string_view argstr)
888     {
889         std::string paramStr(argstr);
890         if (IsUintNumber(paramStr)) {
891             errno = 0;
892             uint64_t num;
893             if (StartsWith(paramStr, "0x")) {
894                 const int hex = 16;
895                 num = std::strtoull(paramStr.c_str(), nullptr, hex);
896             } else {
897                 const int dec = 10;
898                 num = std::strtoull(paramStr.c_str(), nullptr, dec);
899             }
900             if (errno == ERANGE) {
901                 errstr_ +=
902                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
903 
904                 return 1;
905             }
906 
907             if (IsIntegerArgInRange<uint64_t>(arg, num)) {
908                 arg->SetValue(num);
909             } else {
910                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
911                            paramStr + "\"\n";
912             }
913         } else {
914             errstr_ +=
915                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
916         }
917         return 1;
918     }
919 
920     std::size_t ParseUint32ArgParam(PandArg<uint32_t> *arg, std::string_view argstr)
921     {
922         std::string paramStr(argstr);
923         if (IsUintNumber(paramStr)) {
924             uint64_t result = 0;
925             errno = 0;
926             if (StartsWith(paramStr, "0x")) {
927                 const int hex = 16;
928                 result = std::strtoull(paramStr.c_str(), nullptr, hex);
929             } else {
930                 const int dec = 10;
931                 result = std::strtoull(paramStr.c_str(), nullptr, dec);
932             }
933 
934             auto num = static_cast<uint32_t>(result);
935             if (num != result || errno == ERANGE) {
936                 errstr_ +=
937                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
938 
939                 return 1;
940             }
941 
942             if (IsIntegerArgInRange<uint32_t>(arg, num)) {
943                 arg->SetValue(num);
944             } else {
945                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
946                            paramStr + "\"\n";
947             }
948         } else {
949             errstr_ +=
950                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + paramStr + "\"\n";
951         }
952         return 1;
953     }
954 
955     std::size_t ParseListArgParam(PandArg<arg_list_t> *arg, std::string_view argstr)
956     {
957         std::string paramStr(argstr);
958         arg_list_t value;
959         if (arg->WasSet()) {
960             value = arg->GetValue();
961         } else {
962             value = arg_list_t();
963         }
964         if (!arg->GetDelimiter().has_value()) {
965             value.push_back(paramStr);
966             arg->SetValue(value);
967             return 1;
968         }
969         std::string delimiter = arg->GetDelimiter().value();
970         std::size_t paramStrIndex = 0;
971         std::size_t pos = paramStr.find_first_of(delimiter, paramStrIndex);
972         while (pos < paramStr.size()) {
973             value.push_back(paramStr.substr(paramStrIndex, pos - paramStrIndex));
974             paramStrIndex = pos;
975             paramStrIndex = paramStr.find_first_not_of(delimiter, paramStrIndex);
976             pos = paramStr.find_first_of(delimiter, paramStrIndex);
977         }
978 
979         if (paramStrIndex <= paramStr.size()) {
980             value.push_back(paramStr.substr(paramStrIndex, pos - paramStrIndex));
981         }
982         arg->SetValue(value);
983         return 1;
984     }
985 
986     static std::size_t NextSeparator(std::string_view argstr, std::size_t pos = 0,
987                                      const std::string &separarors = EQ_SEPARATOR)
988     {
989         return argstr.find_first_of(separarors, pos);
990     }
991 
992     static bool IsIntegerNumber(const std::string_view &str)
993     {
994         if (str.empty()) {
995             return false;
996         }
997         std::size_t pos = 0;
998         // look for dash if it's negative one
999         if (str[0] == '-') {
1000             pos++;
1001         }
1002         // look for hex-style integer
1003         if ((str.size() > pos + 2U) && (str.compare(pos, 2U, "0x") == 0)) {
1004             pos += HEX_PREFIX_WIDTH;
1005             return str.find_first_not_of("0123456789abcdefABCDEF", pos) == std::string::npos;
1006         }
1007         return str.find_first_not_of("0123456789", pos) == std::string::npos;
1008     }
1009 
1010     static bool IsRationalNumber(const std::string_view &str)
1011     {
1012         if (str.empty()) {
1013             return false;
1014         }
1015         std::size_t pos = 0;
1016         // look for dash if it's negative one
1017         if (str[0] == '-') {
1018             pos++;
1019         }
1020         return str.find_first_not_of(".0123456789", pos) == std::string::npos;
1021     }
1022 
1023     static bool IsUintNumber(const std::string_view &str)
1024     {
1025         if (str.empty()) {
1026             return false;
1027         }
1028 
1029         constexpr std::string_view CHAR_SEARCH = "0123456789";
1030         constexpr std::string_view CHAR_SEARCH_HEX = "0123456789abcdef";
1031         std::size_t pos = 0;
1032         // look for hex-style uint_t integer
1033         if ((str.size() > 2U) && (str.compare(0, 2U, "0x") == 0)) {
1034             pos += HEX_PREFIX_WIDTH;
1035         }
1036         return str.find_first_not_of((pos != 0) ? CHAR_SEARCH_HEX.data() : CHAR_SEARCH.data(), pos) ==
1037                std::string::npos;
1038     }
1039 
1040     template <typename T,
1041               // CC-OFFNXT(G.FMT.10) project code style
1042               enable_if_t<is_same_v<T, int> || is_same_v<T, uint32_t> || is_same_v<T, uint64_t>> * = nullptr>
1043     bool IsIntegerArgInRange(PandArg<T> *arg, T num)
1044     {
1045         if (!(arg->GetMinMaxVal().has_value())) {
1046             return true;
1047         }
1048         std::pair<T, T> minMax = arg->GetMinMaxVal().value();
1049         return ((num >= std::get<0>(minMax)) && (num <= std::get<1>(minMax)));
1050     }
1051 
1052     static bool StartsWith(const std::string &haystack, const std::string &needle)
1053     {
1054         return std::equal(needle.begin(), needle.end(), haystack.begin());
1055     }
1056 
1057     void SetBoolUnexpectedValueError(PandArg<bool> *arg, const std::string &wrongvalue)
1058     {
1059         errstr_ += "pandargs: Bool argument " + arg->GetName() + " has unexpected parameter value " + wrongvalue + "\n";
1060         arg->ResetDefaultValue();
1061     }
1062 
1063     constexpr static size_t HEX_PREFIX_WIDTH = 2;
1064     constexpr static unsigned int DASH_COUNT = 2;
1065 
1066     std::vector<std::string> argvVec_ {};
1067     std::size_t argvIndex_ = 0;
1068     std::string errstr_ {};
1069     bool tailFlag_ = false;
1070     bool remainderFlag_ = false;
1071     bool equalFlag_ = false;
1072     bool tailParsedFlag_ = false;
1073     static constexpr const char *DOUBLE_DASH = "--";
1074     static constexpr const char *EQ_SEPARATOR = "=";
1075     std::set<PandArgBase *, PandArgPtrComparator> args_ {};
1076     std::vector<PandArgBase *> tailArgs_ {};
1077     arg_list_t remainder_ = arg_list_t();
1078 };
1079 
1080 }  // namespace ark
1081 
1082 #endif  // LIBPANDABASE_UTILS_PANDARGS_H_
1083