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