diff options
| author | ilikepugs <[email protected]> | 2025-12-23 18:56:01 +0300 |
|---|---|---|
| committer | ilikepugs <[email protected]> | 2025-12-23 19:19:02 +0300 |
| commit | 9d66ab24e3973f879cae08d109a8bfadadcd1d43 (patch) | |
| tree | 2e2d2002a871daca348cb198a1050546a9f91b8c /library/cpp/getopt/small | |
| parent | c06e738ea27bbf0882167c01fb9cda6ab7a70f6e (diff) | |
Introduce tags for free args in opt-parser
### Что случилось?
- Появилась возможность размечать свободные аргументы тегами (хранятся как ui32-значения)
- Теги могут быть статические (`.SetTag(ESomeTags::Tag1)`) и динамические (`.SetTag([] (const TString& a) {return a ? ESomeTags::Tag1 : ESomeTags::Tag2; })`).
- После парсинга можно получить свободные аргументы с определенным тегом (`res.GetFreeArgs(ESomeTags::Tag1)`)
commit_hash:c1a9d821da376538eb7e4251626052a9f03b941c
Diffstat (limited to 'library/cpp/getopt/small')
| -rw-r--r-- | library/cpp/getopt/small/last_getopt_opt.h | 54 | ||||
| -rw-r--r-- | library/cpp/getopt/small/last_getopt_opts.cpp | 8 | ||||
| -rw-r--r-- | library/cpp/getopt/small/last_getopt_opts.h | 7 | ||||
| -rw-r--r-- | library/cpp/getopt/small/last_getopt_parse_result.cpp | 50 | ||||
| -rw-r--r-- | library/cpp/getopt/small/last_getopt_parse_result.h | 26 |
5 files changed, 136 insertions, 9 deletions
diff --git a/library/cpp/getopt/small/last_getopt_opt.h b/library/cpp/getopt/small/last_getopt_opt.h index de3b10badec..7d6ae6b8016 100644 --- a/library/cpp/getopt/small/last_getopt_opt.h +++ b/library/cpp/getopt/small/last_getopt_opt.h @@ -13,6 +13,8 @@ #include <util/string/join.h> #include <optional> +#include <functional> +#include <utility> namespace NLastGetopt { enum EHasArg { @@ -22,6 +24,11 @@ namespace NLastGetopt { DEFAULT_HAS_ARG = REQUIRED_ARGUMENT }; + template<class T> + concept ArgTagConcept = + std::is_enum_v<std::remove_cvref_t<T>> || + std::is_same_v<std::remove_cvref_t<T>, ui32>; + /** * NLastGetopt::TOpt is a storage of data about exactly one program option. * The data is: parse politics and help information. @@ -797,6 +804,9 @@ namespace NLastGetopt { * argument name (title) */ struct TFreeArgSpec { + template <ArgTagConcept E> + using TTagger = std::function<E(const TString&)>; + TFreeArgSpec() = default; TFreeArgSpec(const TString& title, const TString& help = TString(), bool optional = false) : Title_(title) @@ -808,6 +818,7 @@ namespace NLastGetopt { TString Title_; TString Help_; TString CompletionArgHelp_; + TTagger<ui32> Tagger_; bool Optional_ = false; NComp::ICompleterPtr Completer_ = nullptr; @@ -890,5 +901,48 @@ namespace NLastGetopt { Completer_ = std::move(completer); return *this; } + + /** + * Set a tagger that can compute tag dynamically for each argument value. + */ + TFreeArgSpec& SetTag(TTagger<ui32>&& tagger) { + Tagger_ = std::forward<TTagger<ui32>>(tagger); + return *this; + } + + /** + * Set a static tag for all arguments described by this spec. + */ + template <ArgTagConcept E> + TFreeArgSpec& SetTag(E tag) { + Tagger_ = [tag](const TString&) -> ui32 { + return static_cast<ui32>(tag); + }; + return *this; + } + + /** + * Set a tagger that can compute tag dynamically for each argument value. + */ + template <ArgTagConcept E> + TFreeArgSpec& SetTag(TTagger<E>&& tagger) { + Tagger_ = [tagger](const TString& value) -> ui32 { + return static_cast<ui32>(tagger(value)); + }; + return *this; + } + + /** + * Compute tag for argument value at given position. + */ + ui32 GetTag(const TString& value) const { + if (Tagger_) { + ui32 tag = Tagger_(value); + if (tag) { + return tag; + } + } + return 0; + } }; } diff --git a/library/cpp/getopt/small/last_getopt_opts.cpp b/library/cpp/getopt/small/last_getopt_opts.cpp index 984927a0387..b656e607e85 100644 --- a/library/cpp/getopt/small/last_getopt_opts.cpp +++ b/library/cpp/getopt/small/last_getopt_opts.cpp @@ -213,7 +213,7 @@ namespace NLastGetopt { if (FreeArgsMax_ < FreeArgsMin_) { ythrow TConfException() << "FreeArgsMax must be >= FreeArgsMin"; } - if (!FreeArgSpecs_.empty() && FreeArgSpecs_.rbegin()->first >= FreeArgsMax_) { + if (!FreeArgSpecs_.empty() && GetTrailingArgsIndex() > FreeArgsMax_) { ythrow TConfException() << "Described args count is greater than FreeArgsMax. Either increase FreeArgsMax or remove unreachable descriptions"; } } @@ -335,7 +335,7 @@ namespace NLastGetopt { } os << "[OPTIONS]"; - ui32 numDescribedFlags = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; + ui32 numDescribedFlags = GetTrailingArgsIndex(); ui32 numArgsToShow = Max(FreeArgsMin_, FreeArgsMax_ == UNLIMITED_ARGS ? numDescribedFlags : FreeArgsMax_); for (ui32 i = 0, nonOptionalFlagsPrinted = 0; i < numArgsToShow; ++i) { @@ -513,8 +513,8 @@ namespace NLastGetopt { } os << colors.OldColor() << Endl; - const size_t limit = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first; - for (size_t i = 0; i <= limit; ++i) { + const size_t limit = GetTrailingArgsIndex(); + for (size_t i = 0; i < limit; ++i) { if (!FreeArgSpecs_.contains(i)) { continue; } diff --git a/library/cpp/getopt/small/last_getopt_opts.h b/library/cpp/getopt/small/last_getopt_opts.h index cb1e0227633..abfc79425f8 100644 --- a/library/cpp/getopt/small/last_getopt_opts.h +++ b/library/cpp/getopt/small/last_getopt_opts.h @@ -504,6 +504,13 @@ namespace NLastGetopt { } /** + * Get index from where trailing arguments start + */ + ui32 GetTrailingArgsIndex() const { + return FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; + } + + /** * Set exact expected number of free args * * @param count new value diff --git a/library/cpp/getopt/small/last_getopt_parse_result.cpp b/library/cpp/getopt/small/last_getopt_parse_result.cpp index f5a005b4c75..016fe347141 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.cpp +++ b/library/cpp/getopt/small/last_getopt_parse_result.cpp @@ -9,6 +9,44 @@ namespace NLastGetopt { return nullptr; } + void TOptsParseResult::BuildTaggedFreeArgs(const TOpts* options) { + TaggedFreeArgs_.clear(); + + if (!Parser_) { + return; + } + + const size_t freeArgsPos = GetFreeArgsPos(); + for (size_t argPos = freeArgsPos; argPos < Parser_->Argc_; ++argPos) { + size_t index = argPos - freeArgsPos; + + TString value = Parser_->Argv_[argPos]; + ui32 tag = 0; + + if (options) { + const TFreeArgSpec* spec = nullptr; + auto it = options->FreeArgSpecs_.find(index); + if (it != options->FreeArgSpecs_.end()) { + spec = &it->second; + } else if (options->FreeArgsMax_ == TOpts::UNLIMITED_ARGS) { + ui32 trailingArgsIndex = options->GetTrailingArgsIndex(); + if (index >= trailingArgsIndex) { + spec = &options->TrailingArgSpec_; + } + } + + if (spec) { + tag = spec->GetTag(value); + } + } + + TaggedFreeArgs_.push_back(TTaggedArg { + .Value = value, + .Tag = tag + }); + } + } + const TOptParseResult* TOptsParseResult::FindOptParseResult(const TOpt* opt, bool includeDefault) const { const TOptParseResult* r = FindParseResult(Opts_, opt); if (nullptr == r && includeDefault) @@ -116,15 +154,16 @@ namespace NLastGetopt { } TVector<TString> TOptsParseResult::GetFreeArgs() const { - TVector<TString> v; - for (size_t i = GetFreeArgsPos(); i < Parser_->Argc_; ++i) { - v.push_back(Parser_->Argv_[i]); + TVector<TString> args; + args.reserve(TaggedFreeArgs_.size()); + for (const auto& arg : TaggedFreeArgs_) { + args.push_back(arg.Value); } - return v; + return args; } size_t TOptsParseResult::GetFreeArgCount() const { - return Parser_->Argc_ - GetFreeArgsPos(); + return TaggedFreeArgs_.size(); } void FindUserTypos(const TString& arg, const TOpts* options) { @@ -150,6 +189,7 @@ namespace NLastGetopt { } Y_ENSURE(options); + BuildTaggedFreeArgs(options); const auto freeArgs = GetFreeArgs(); for (size_t i = 0; i < freeArgs.size(); ++i) { if (i >= options->ArgBindings_.size()) { diff --git a/library/cpp/getopt/small/last_getopt_parse_result.h b/library/cpp/getopt/small/last_getopt_parse_result.h index 31c2dbe072c..6574667aede 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.h +++ b/library/cpp/getopt/small/last_getopt_parse_result.h @@ -3,7 +3,14 @@ #include "last_getopt_opts.h" #include "last_getopt_parser.h" +#include <util/generic/hash.h> + namespace NLastGetopt { + struct TTaggedArg { + TString Value; + ui32 Tag = 0; + }; + /** * NLastGetopt::TOptParseResult contains all arguments for exactly one TOpt, * that have been fetched during parsing @@ -74,6 +81,7 @@ namespace NLastGetopt { TdVec Opts_; //Parsing result for all options, that have been explicitly defined in argc/argv TdVec OptsDef_; //Parsing result for options, that have been defined by default values only TVector<TString> ProgramSubcommandPath_; + TVector<TTaggedArg> TaggedFreeArgs_; private: TOptParseResult& OptParseResult(); @@ -88,6 +96,8 @@ namespace NLastGetopt { */ static const TOptParseResult* FindParseResult(const TdVec& vec, const TOpt* opt); + void BuildTaggedFreeArgs(const TOpts* options); + protected: /** * Performs parsing of comand line arguments. @@ -185,6 +195,22 @@ namespace NLastGetopt { */ TVector<TString> GetFreeArgs() const; + template <ArgTagConcept E> + TVector<TString> GetFreeArgs(E tag) const { + TVector<TString> args; + ui32 ui32Tag = static_cast<ui32>(tag); + for (const auto& arg : TaggedFreeArgs_) { + if (arg.Tag == ui32Tag) { + args.push_back(arg.Value); + } + } + return args; + } + + const TVector<TTaggedArg>& GetTaggedFreeArgs() const { + return TaggedFreeArgs_; + } + /** * @return true if given option exist in results of parsing * |
