aboutsummaryrefslogblamecommitdiffstats
path: root/library/cpp/getopt/small/last_getopt_opts.h
blob: 718dbfcb893abda4bf5bfd20358a8a8d7e7b8db0 (plain) (tree)
1
2
3
4
5
6
7
8
9


                            
                                      
 

                             




                                         
 
       

















                                                                                                    

                                      
 
           
                                                                 
                                                                             
                                                              
 

                                                                                                                                                         
 

                                              
 
                        
                                     
 

                                                         
 

                                                                                                                
 
                                                               
                                                                
 
                                                                                                       
           
                                                       
                                                          
 
           

                                                                                    




                                                                          
 
           




                                                                             
                              
 
           


                                                               
                                                                 
 
           


                                                               
                                                 
 
           


                                                               
                                                     
 
           


                                                               
                                     
 
           


























                                                               

                                             
 
           


                                                                  

                                                       
 
           


                                                                   

                                                
 
           


                                                                       
                                                                
 
           


                                                                       
                                                    
 
           


                                                                       
                                                
 
           


                                                                       
                                    
 
           

























                                                                       
                                       
 
           
                                             
                                      
 
           


                                                                         
                                            
 
           














                                                                                          


                                                           
                                                         
 
           




                                                                                              

                                                               
 
           




                                                                                              





                                                                               
 
           




                                                                                             

                                                                            
 
           





                                                                                                        






                                                                                    
 
           




                                                                           






                                                          
                                             
                                             
         
           






                                                   




                                                                                 






                                                                       
                                             
                                               
         
           









                                                                                                                  






                                                           
         
           




















                                                                                                              
                                              
 
           


                                             

                                                    
 
           


                                               



                                                   


















                                                                     

                                         
 






                                                  
           
                                          
                                      
           
                                         
                                                                        
         
 
           





                                                  





                                                                 


                                                 


                                           
 
           



                                                      


                                                     
 
           






































































                                                                                                                       

                                                    
 
           











                                                                              
                                                                                                               
 
           



                                                                     

                                                                                   







                                                                   
 
            
           



                                                     
 
           




                                                                   
                                                                                                                 
 
           



                                               
                                                                                           
 
 
#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;
        bool CheckUserTypos_ = false;

    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);
        }

        /**
         * Set check user typos or not
         * @param check   bool flag for chosing
         */
        void SetCheckUserTypos(bool check = true) {
            CheckUserTypos_ = check;
        }

        /**
         * 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;
    };

}