aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/getopt/small/last_getopt.h
blob: 07687bc9148c81f0de0ce259c284ebde1a783218 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma once

#include "last_getopt_opts.h"
#include "last_getopt_easy_setup.h"
#include "last_getopt_parse_result.h"

#include <util/generic/function.h>
#include <util/string/split.h>

/// see some documentation in
/// https://wiki.yandex-team.ru/development/poisk/arcadia/util/lastgetopt/
/// https://wiki.yandex-team.ru/development/poisk/arcadia/library/getopt/
/// see examples in library/cpp/getopt/last_getopt_demo

//TODO: in most cases this include is unnecessary, but needed THandlerFunctor1<TpFunc, TpArg>::HandleOpt
#include "last_getopt_parser.h"

namespace NLastGetopt {
    /// Handler to split option value by delimiter into a target container and allow ranges.
    template <class Container>
    struct TOptRangeSplitHandler: public IOptHandler {
    public:
        using TContainer = Container;
        using TValue = typename TContainer::value_type;

        explicit TOptRangeSplitHandler(TContainer* target, const char elementsDelim, const char rangesDelim)
            : Target(target)
            , ElementsDelim(elementsDelim)
            , RangesDelim(rangesDelim)
        {
        }

        void HandleOpt(const TOptsParser* parser) override {
            const TStringBuf curval(parser->CurValOrDef());
            if (curval.IsInited()) {
                StringSplitter(curval).Split(ElementsDelim).Consume([&](const TStringBuf& val) {
                    TStringBuf mutableValue = val;

                    TValue first = NPrivate::OptFromString<TValue>(mutableValue.NextTok(RangesDelim), parser->CurOpt());
                    TValue last = mutableValue ? NPrivate::OptFromString<TValue>(mutableValue, parser->CurOpt()) : first;

                    if (last < first) {
                        throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(parser->CurOpt()) << " value " << TString(val).Quote() << ": the second argument is less than the first one";
                    }

                    for (++last; first < last; ++first) {
                        Target->insert(Target->end(), first);
                    }
                });
            }
        }

    private:
        TContainer* Target;
        char ElementsDelim;
        char RangesDelim;
    };

    template <class Container>
    struct TOptSplitHandler: public IOptHandler {
    public:
        using TContainer = Container;
        using TValue = typename TContainer::value_type;

        explicit TOptSplitHandler(TContainer* target, const char delim)
            : Target(target)
            , Delim(delim)
        {
        }

        void HandleOpt(const TOptsParser* parser) override {
            const TStringBuf curval(parser->CurValOrDef());
            if (curval.IsInited()) {
                StringSplitter(curval).Split(Delim).Consume([&](const TStringBuf& val) {
                    Target->insert(Target->end(), NPrivate::OptFromString<TValue>(val, parser->CurOpt()));
                });
            }
        }

    private:
        TContainer* Target;
        char Delim;
    };

    template <class TpFunc>
    struct TOptKVHandler: public IOptHandler {
    public:
        using TKey = typename TFunctionArgs<TpFunc>::template TGet<0>;
        using TValue = typename TFunctionArgs<TpFunc>::template TGet<1>;

        explicit TOptKVHandler(TpFunc func, const char kvdelim = '=')
            : Func(func)
            , KVDelim(kvdelim)
        {
        }

        void HandleOpt(const TOptsParser* parser) override {
            const TStringBuf curval(parser->CurValOrDef());
            const TOpt* curOpt(parser->CurOpt());
            if (curval.IsInited()) {
                TStringBuf key, value;
                if (!curval.TrySplit(KVDelim, key, value)) {
                    throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(curOpt)
                                             << " value " << TString(curval).Quote() << ": expected key" << KVDelim << "value format";
                }
                Func(NPrivate::OptFromString<TKey>(key, curOpt), NPrivate::OptFromString<TValue>(value, curOpt));
            }
        }

    private:
        TpFunc Func;
        char KVDelim;
    };

    namespace NPrivate {
        template <typename TpFunc, typename TpArg>
        void THandlerFunctor1<TpFunc, TpArg>::HandleOpt(const TOptsParser* parser) {
            const TStringBuf curval = parser->CurValOrDef(!HasDef_);
            const TpArg& arg = curval.IsInited() ? OptFromString<TpArg>(curval, parser->CurOpt()) : Def_;
            try {
                Func_(arg);
            } catch (const TUsageException&) {
                throw;
            } catch (...) {
                throw TUsageException() << "failed to handle opt " << OptToString(parser->CurOpt())
                                         << " value " << TString(curval).Quote() << ": " << CurrentExceptionMessage();
            }
        }

    }

}