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