summaryrefslogtreecommitdiffstats
path: root/library/cpp/getopt/small
diff options
context:
space:
mode:
authorilikepugs <[email protected]>2025-12-23 18:56:01 +0300
committerilikepugs <[email protected]>2025-12-23 19:19:02 +0300
commit9d66ab24e3973f879cae08d109a8bfadadcd1d43 (patch)
tree2e2d2002a871daca348cb198a1050546a9f91b8c /library/cpp/getopt/small
parentc06e738ea27bbf0882167c01fb9cda6ab7a70f6e (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.h54
-rw-r--r--library/cpp/getopt/small/last_getopt_opts.cpp8
-rw-r--r--library/cpp/getopt/small/last_getopt_opts.h7
-rw-r--r--library/cpp/getopt/small/last_getopt_parse_result.cpp50
-rw-r--r--library/cpp/getopt/small/last_getopt_parse_result.h26
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
*