aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/getopt/small/last_getopt_opts.h
blob: f7469ea313df10cb285365b44383c9e30a071091 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
#pragma once

#include "last_getopt_opt.h"

#include <library/cpp/colorizer/fwd.h>

#include <util/generic/map.h>

namespace NLastGetopt {
    enum EArgPermutation { 
        REQUIRE_ORDER, 
        PERMUTE, 
        RETURN_IN_ORDER, 
        DEFAULT_ARG_PERMUTATION = PERMUTE 
    }; 

    /** 
     * NLastGetopt::TOpts is a storage of program options' parse rules.
     * It contains information about all options, free args, some parsing options
     *    and rules about interaction between options.
     *
     * The main point for defining program options.
     *
     * The parsing rules determined by the following parts:
     *   - Arguments permutation. It is expected free args be after named args.
     *     This point adjusts how to treat breaking this expectation.
     *       if REQUIRE_ORDER is choosen, the exception during parsing will be raised,
     *            the special string " -- " will be treated as end of named
     *            options: all options after it will be parsed as free args
     *       if PERMUTE is choosen, arguments will be rearranged in correct order,
     *       if RETURN_IN_ORDER is choosen, all free args will be ommited (TODO: looks very strange)
     *   - Using '+' as a prefix instead '--' for long names
     *   - Using "-" as a prefix for both short and long names
     *   - Allowing unknown options
     *
     */
    class TOpts { 
        friend class TOptsParseResult; 
        friend class TOptsParser; 

    public: 
        static constexpr const ui32 UNLIMITED_ARGS = Max<ui32>();

        typedef TVector<TSimpleSharedPtr<TOpt>> TOptsVector; 
        TOptsVector Opts_; // infomation about named (short and long) options 
        TVector<std::function<void(TStringBuf)>> ArgBindings_;

        EArgPermutation ArgPermutation_ = DEFAULT_ARG_PERMUTATION; // determines how to parse positions of named and free options. See information below. 
        bool AllowSingleDashForLong_ = false;                      // 
        bool AllowPlusForLong_ = false;                            // using '+' instead '--' for long options 

        //Allows unknwon options: 
        bool AllowUnknownCharOptions_ = false; 
        bool AllowUnknownLongOptions_ = false; 

        ui32 Wrap_ = 80;

    private: 
        ui32 FreeArgsMin_; // minimal number of free args 
        ui32 FreeArgsMax_; // maximal number of free args 

        TMap<ui32, TFreeArgSpec> FreeArgSpecs_; // mapping [free arg position] -> [free arg specification]
        TFreeArgSpec TrailingArgSpec_;          // spec for the trailing argument (when arguments are unlimited)
        TString DefaultFreeArgTitle_ = "ARG"; // title that's used for free args without a title

        TString Title;              // title of the help string 
        TString CustomCmdLineDescr; // user defined help string 
        TString CustomUsage;        // user defined usage string

        TVector<std::pair<TString, TString>> Sections;  // additional help entries to print after usage

    public: 
        /** 
         * Constructs TOpts from string as in getopt(3)
         */
        TOpts(const TStringBuf& optstring = TStringBuf()); 

        /** 
         * Constructs TOpts from string as in getopt(3) and
         * additionally adds help option (for '?') and svn-verstion option (for 'V')
         */
        static TOpts Default(const TStringBuf& optstring = TStringBuf()) { 
            TOpts opts(optstring); 
            opts.AddHelpOption(); 
            opts.AddVersionOption(); 
            return opts; 
        } 

        /** 
         * Checks correctness of options' descriptions.
         * Throws TConfException if validation failed.
         * Check consist of:
         *    -not intersecting of names
         *    -compability of settings, that responsable for freeArgs parsing
         */
        void Validate() const; 

        /** 
         * Search for the option with given long name
         * @param name     long name for search
         * @return         ptr on result (nullptr if not found)
         */
        const TOpt* FindLongOption(const TStringBuf& name) const; 

        /** 
         * Search for the option with given short name
         * @param c        short name for search
         * @return         ptr on result (nullptr if not found)
         */
        const TOpt* FindCharOption(char c) const; 

        /** 
         * Search for the option with given long name
         * @param name     long name for search
         * @return         ptr on result (nullptr if not found)
         */
        TOpt* FindLongOption(const TStringBuf& name); 

        /** 
         * Search for the option with given short name
         * @param c        short name for search
         * @return         ptr on result (nullptr if not found)
         */
        TOpt* FindCharOption(char c); 

        /** 
         * Search for the option with given name
         * @param name     name for search
         * @return         ptr on result (nullptr if not found)
         */
        /// @{

        const TOpt* FindOption(const TStringBuf& name) const {
            return FindLongOption(name);
        }

        TOpt* FindOption(const TStringBuf& name) {
            return FindLongOption(name);
        }

        const TOpt* FindOption(char c) const {
            return FindCharOption(c);
        }

        TOpt* FindOption(char c) {
            return FindCharOption(c);
        }

        /// @}

        /**
         * Sets title of the help string
         * @param title        title to set
         */
        void SetTitle(const TString& title) { 
            Title = title; 
        } 

        /** 
         * @return true if there is an option with given long name
         *
         * @param name        long name for search
         */
        bool HasLongOption(const TString& name) const { 
            return FindLongOption(name) != nullptr; 
        } 

        /** 
         * @return true if there is an option with given short name
         *
         * @param char        short name for search
         */
        bool HasCharOption(char c) const { 
            return FindCharOption(c) != nullptr; 
        } 

        /** 
         * Search for the option with given long name
         * @param name     long name for search
         * @return         ref on result (throw exception if not found)
         */
        const TOpt& GetLongOption(const TStringBuf& name) const; 

        /** 
         * Search for the option with given long name
         * @param name     long name for search
         * @return         ref on result (throw exception if not found)
         */
        TOpt& GetLongOption(const TStringBuf& name); 

        /** 
         * Search for the option with given short name
         * @param c        short name for search
         * @return         ref on result (throw exception if not found)
         */
        const TOpt& GetCharOption(char c) const; 

        /** 
         * Search for the option with given short name
         * @param c        short name for search
         * @return         ref on result (throw exception if not found)
         */
        TOpt& GetCharOption(char c); 

        /** 
         * Search for the option with given name
         * @param name     name for search
         * @return         ref on result (throw exception if not found)
         */
        /// @{

        const TOpt& GetOption(const TStringBuf& name) const {
            return GetLongOption(name);
        }

        TOpt& GetOption(const TStringBuf& name) {
            return GetLongOption(name);
        }

        const TOpt& GetOption(char c) const {
            return GetCharOption(c);
        }

        TOpt& GetOption(char c) {
            return GetCharOption(c);
        }

        /// @}

        /**
         * @return true if short options exist
         */
        bool HasAnyShortOption() const; 

        /** 
         * @return true if long options exist
         */
        bool HasAnyLongOption() const; 

        /** 
         * Creates new [option description (TOpt)] as a copy of given one
         * @param option   source
         * @return         reference for created option
         */
        TOpt& AddOption(const TOpt& option); 

        /** 
         * Creates new free argument handling
         * @param name   name of free arg to show in help
         * @param target variable address to store parsing result into
         * @param help   help string to show in help
         */
        template <typename T>
        void AddFreeArgBinding(const TString& name, T& target, const TString& help = "") {
            ArgBindings_.emplace_back([&target](TStringBuf value) {
                target = FromString<T>(value);
            });

            FreeArgsMax_ = Max<ui32>(FreeArgsMax_, ArgBindings_.size());
            SetFreeArgTitle(ArgBindings_.size() - 1, name, help);
        }

        /**
         * Creates options list from string as in getopt(3)
         *
         * @param optstring   source
         */
        void AddCharOptions(const TStringBuf& optstring); 

        /** 
         * Creates new [option description (TOpt)] with given short name and given help string
         *
         * @param c        short name
         * @param help     help string
         * @return         reference for created option
         */
        TOpt& AddCharOption(char c, const TString& help = "") { 
            return AddCharOption(c, DEFAULT_HAS_ARG, help); 
        } 

        /** 
         * Creates new [option description (TOpt)] with given short name and given help string
         *
         * @param c        short name
         * @param help     help string
         * @return         reference for created option
         */
        TOpt& AddCharOption(char c, EHasArg hasArg, const TString& help = "") { 
            TOpt option; 
            option.AddShortName(c); 
            option.Help(help); 
            option.HasArg(hasArg); 
            return AddOption(option); 
        } 

        /** 
         * Creates new [option description (TOpt)] with given long name and given help string
         *
         * @param name     long name
         * @param help     help string
         * @return         reference for created option
         */
        TOpt& AddLongOption(const TString& name, const TString& help = "") { 
            return AddLongOption(0, name, help); 
        } 

        /** 
         * Creates new [option description (TOpt)] with given long and short names and given help string
         *
         * @param c        short name
         * @param name     long name
         * @param help     help string
         * @return         reference for created option
         */
        TOpt& AddLongOption(char c, const TString& name, const TString& help = "") { 
            TOpt option; 
            if (c != 0) 
                option.AddShortName(c); 
            option.AddLongName(name); 
            option.Help(help); 
            return AddOption(option); 
        } 

        /** 
         * Creates new [option description (TOpt)] for help printing,
         *   adds appropriate handler for it
         * If "help" option already exist, will add given short name to it.
         *
         * @param c        new short name for help option
         */
        TOpt& AddHelpOption(char c = '?') { 
            if (TOpt* o = FindLongOption("help")) { 
                if (!o->CharIs(c)) 
                    o->AddShortName(c); 
                return *o; 
            } 
            return AddLongOption(c, "help", "print usage") 
                .HasArg(NO_ARGUMENT) 
                .IfPresentDisableCompletion()
                .Handler(&PrintUsageAndExit); 
        }

        /** 
         * Creates new [option description (TOpt)] for svn-revision printing,
         *   adds appropriate handler for it.
         * If "svnversion" option already exist, will add given short name to it.
         *
         * @param c        new short name for "svnversion" option
         */
        TOpt& AddVersionOption(char c = 'V') { 
            if (TOpt* o = FindLongOption("svnrevision")) { 
                if (!o->CharIs(c)) 
                    o->AddShortName(c); 
                return *o; 
            } 
            return AddLongOption(c, "svnrevision", "print svn version") 
                .HasArg(NO_ARGUMENT) 
                .IfPresentDisableCompletion()
                .Handler(&PrintVersionAndExit); 
        }

        /** 
         * Creates new option for generating completion shell scripts.
         *
         * @param command name of command that should be completed (typically corresponds to the executable name).
         */
        TOpt& AddCompletionOption(TString command, TString longName = "completion");

        /**
         * Creates or finds option with given short name
         *
         * @param c        new short name for search/create
         */
        TOpt& CharOption(char c) { 
            const TOpt* opt = FindCharOption(c); 
            if (opt != nullptr) { 
                return const_cast<TOpt&>(*opt); 
            } else { 
                AddCharOption(c); 
                return const_cast<TOpt&>(GetCharOption(c)); 
            } 
        }

        /** 
         * Indicate that some options can't appear together.
         *
         * Note: this is not transitive.
         *
         * Note: don't use this on options with default values. If option with default value wasn't specified,
         * parser will run handlers for default value, thus triggering a false-positive exclusivity check.
         */
        template <typename T1, typename T2>
        void MutuallyExclusive(T1&& opt1, T2&& opt2) {
            MutuallyExclusiveOpt(GetOption(std::forward<T1>(opt1)), GetOption(std::forward<T2>(opt2)));
        }

        /**
         * Like `MutuallyExclusive`, but accepts `TOpt`s instead of option names.
         */
        void MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2);

        /**
         * @return index of option
         *
         * @param opt        pointer of option to search
         */
        size_t IndexOf(const TOpt* opt) const; 

        /** 
         * Replace help string with given
         *
         * @param decr        new help string
         */
        void SetCmdLineDescr(const TString& descr) { 
            CustomCmdLineDescr = descr; 
        } 

        /** 
         * Replace usage string with given
         *
         * @param usage        new usage string
         */
        void SetCustomUsage(const TString& usage) {
            CustomUsage = usage;
        }

        /**
         * Add a section to print after the main usage spec.
         */
        void AddSection(TString title, TString text) {
            Sections.emplace_back(std::move(title), std::move(text));
        }

        /**
         * Add section with examples.
         *
         * @param examples text of this section
         */
        void SetExamples(TString examples) {
            AddSection("Examples", std::move(examples));
        }

        /**
         * Set minimal number of free args
         *
         * @param min        new value
         */
        void SetFreeArgsMin(size_t min) { 
            FreeArgsMin_ = ui32(min); 
        } 


        /** 
         * Get current minimal number of free args
         */
        ui32 GetFreeArgsMin() const {
            return FreeArgsMin_;
        }

        /**
         * Set maximal number of free args
         *
         * @param max        new value
         */
        void SetFreeArgsMax(size_t max) { 
            FreeArgsMax_ = ui32(max); 
            FreeArgsMax_ = Max<ui32>(FreeArgsMax_, ArgBindings_.size());
        } 

        /** 
         * Get current maximal number of free args
         */
        ui32 GetFreeArgsMax() const {
            return FreeArgsMax_;
        }

        /**
         * Get mapping for free args
         */
        const TMap<ui32, TFreeArgSpec>& GetFreeArgSpecs() const {
            return FreeArgSpecs_;
        }

        /**
         * Set exact expected number of free args
         *
         * @param count        new value
         */
        void SetFreeArgsNum(size_t count) { 
            FreeArgsMin_ = ui32(count); 
            FreeArgsMax_ = ui32(count); 
        } 

        /** 
         * Set minimal and maximal number of free args
         *
         * @param min        new value for minimal
         * @param max        new value for maximal
         */
        void SetFreeArgsNum(size_t min, size_t max) { 
            FreeArgsMin_ = ui32(min); 
            FreeArgsMax_ = ui32(max); 
        } 

        /** 
         * Set title and help string of free argument
         *
         * @param pos          index of argument
         * @param title        new value for argument title
         * @param help         new value for help string
         * @param optional     indicates that the flag's help string should be rendered as for optional flag;
         *                     does not affect actual flags parsing
         */
        void SetFreeArgTitle(size_t pos, const TString& title, const TString& help = TString(), bool optional = false);

        /** 
         * Get free argument's spec for further modification.
         */
        TFreeArgSpec& GetFreeArgSpec(size_t pos);

        /**
         * Legacy, don't use. Same as `SetTrailingArgTitle`.
         * Older versions of lastgetopt didn't have destinction between default title and title
         * for the trailing argument.
         */
        void SetFreeArgDefaultTitle(const TString& title, const TString& help = TString()) {
            SetTrailingArgTitle(title, help);
        }

        /**
         * Set default title that will be used for all arguments that have no title.
         */
        void SetDefaultFreeArgTitle(TString title) {
            DefaultFreeArgTitle_ = std::move(title);
        }

        /**
         * Set default title that will be used for all arguments that have no title.
         */
        const TString& GetDefaultFreeArgTitle() const {
            return DefaultFreeArgTitle_;
        }

        /**
         * Set title and help for the trailing argument.
         *
         * This title and help are used to render the last repeated argument when max number of arguments is unlimited.
         */
        /// @{
        void SetTrailingArgTitle(TString title) {
            TrailingArgSpec_.Title(std::move(title));
        }
        void SetTrailingArgTitle(TString title, TString help) {
            TrailingArgSpec_.Title(std::move(title));
            TrailingArgSpec_.Help(std::move(help));
        }
        /// @}

        /** 
         * Get spec for the trailing argument.
         *
         * This spec is used to render the last repeated argument when max number of arguments is unlimited.
         */
        /// @{
        TFreeArgSpec& GetTrailingArgSpec() {
            return TrailingArgSpec_;
        }
        const TFreeArgSpec& GetTrailingArgSpec() const {
            return TrailingArgSpec_;
        }
        /// @}

        /**
         * Set the rule of parsing single dash as prefix of long names
         *
         * @param value     new value of the option
         */
        void SetAllowSingleDashForLong(bool value) { 
            AllowSingleDashForLong_ = value; 
        } 

        /** 
         * Wrap help text at this number of characters. 0 to disable wrapping.
         */
        void SetWrap(ui32 wrap = 80) {
            Wrap_ = wrap;
        }

        /**
         * Print usage string
         *
         * @param program      prefix of result (path to the program)
         * @param os           destination stream
         * @param colors       colorizer
         */
        void PrintUsage(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; 

        /** 
         * Print usage string
         *
         * @param program      prefix of result (path to the program)
         * @param os           destination stream
         */
        void PrintUsage(const TStringBuf& program, IOutputStream& os = Cout) const; 
 
        /** 
         * Get list of options in order of definition.
         */
        TVector<const TOpt*> GetOpts() const {
            auto ret = TVector<const TOpt*>(Reserve(Opts_.size()));
            for (auto& opt : Opts_) {
                ret.push_back(opt.Get());
            }
            return ret;
        }

    private:
        /** 
         * @return argument title of a free argument
         *
         * @param pos     position of the argument
         */
        TStringBuf GetFreeArgTitle(size_t pos) const;

        /** 
         * Print usage helper
         *
         * @param program    prefix of result (path to the program)
         * @param os         destination stream
         * @param colors     colorizer
         */
        void PrintCmdLine(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; 

        /** 
         * Print usage helper
         *
         * @param os         destination stream
         * @param colors     colorizer
         */
        void PrintFreeArgsDesc(IOutputStream& os, const NColorizer::TColors& colors) const; 
    }; 

}