mirror of https://gitee.com/bigwinds/arangodb
move parts from ProgramOptions from headers into cpp files
This commit is contained in:
parent
6c065cd889
commit
53fa66e60d
|
@ -44,7 +44,7 @@ class BindParameters {
|
|||
: _builder(nullptr), _parameters(), _processed(false) {}
|
||||
|
||||
/// @brief create the parameters
|
||||
explicit BindParameters(std::shared_ptr<arangodb::velocypack::Builder> builder)
|
||||
explicit BindParameters(std::shared_ptr<arangodb::velocypack::Builder> const& builder)
|
||||
: _builder(builder), _parameters(), _processed(false) {}
|
||||
|
||||
/// @brief destroy the parameters
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "Query.h"
|
||||
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Aql/AqlItemBlock.h"
|
||||
#include "Aql/AqlTransaction.h"
|
||||
#include "Aql/ExecutionBlock.h"
|
||||
|
@ -42,6 +43,7 @@
|
|||
#include "Cluster/ServerState.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "RestServer/AqlFeature.h"
|
||||
#include "RestServer/QueryRegistryFeature.h"
|
||||
#include "StorageEngine/TransactionState.h"
|
||||
#include "Transaction/Methods.h"
|
||||
#include "Transaction/StandaloneContext.h"
|
||||
|
@ -69,23 +71,11 @@ static std::atomic<TRI_voc_tick_t> NextQueryId(1);
|
|||
constexpr uint64_t DontCache = 0;
|
||||
}
|
||||
|
||||
/// @brief global memory limit for AQL queries
|
||||
uint64_t Query::MemoryLimitValue = 0;
|
||||
|
||||
/// @brief global threshold value for slow queries
|
||||
double Query::SlowQueryThresholdValue = 10.0;
|
||||
|
||||
/// @brief whether or not query tracking is disabled globally
|
||||
bool Query::DoDisableQueryTracking = false;
|
||||
|
||||
/// @brief whether a warning in an AQL query should raise an error
|
||||
bool Query::DoFailOnWarning = false;
|
||||
|
||||
/// @brief creates a query
|
||||
Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase,
|
||||
char const* queryString, size_t queryLength,
|
||||
std::shared_ptr<VPackBuilder> bindParameters,
|
||||
std::shared_ptr<VPackBuilder> options, QueryPart part)
|
||||
std::shared_ptr<VPackBuilder> const& bindParameters,
|
||||
std::shared_ptr<VPackBuilder> const& options, QueryPart part)
|
||||
: _id(0),
|
||||
_resourceMonitor(),
|
||||
_resources(&_resourceMonitor),
|
||||
|
@ -102,6 +92,7 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase,
|
|||
_maxWarningCount(10),
|
||||
_warnings(),
|
||||
_startTime(TRI_microtime()),
|
||||
_queryRegistry(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")),
|
||||
_part(part),
|
||||
_contextOwnedByExterior(contextOwnedByExterior),
|
||||
_killed(false),
|
||||
|
@ -152,8 +143,8 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase,
|
|||
|
||||
/// @brief creates a query from VelocyPack
|
||||
Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase,
|
||||
std::shared_ptr<VPackBuilder> const queryStruct,
|
||||
std::shared_ptr<VPackBuilder> options, QueryPart part)
|
||||
std::shared_ptr<VPackBuilder> const& queryStruct,
|
||||
std::shared_ptr<VPackBuilder> const& options, QueryPart part)
|
||||
: _id(0),
|
||||
_resourceMonitor(),
|
||||
_resources(&_resourceMonitor),
|
||||
|
@ -169,6 +160,7 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase,
|
|||
_maxWarningCount(10),
|
||||
_warnings(),
|
||||
_startTime(TRI_microtime()),
|
||||
_queryRegistry(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")),
|
||||
_part(part),
|
||||
_contextOwnedByExterior(contextOwnedByExterior),
|
||||
_killed(false),
|
||||
|
@ -211,21 +203,7 @@ Query::~Query() {
|
|||
|
||||
_executor.reset();
|
||||
|
||||
if (_context != nullptr) {
|
||||
TRI_ASSERT(!_contextOwnedByExterior);
|
||||
|
||||
// unregister transaction and resolver in context
|
||||
ISOLATE;
|
||||
TRI_GET_GLOBALS();
|
||||
auto ctx =
|
||||
static_cast<arangodb::transaction::V8Context*>(v8g->_transactionContext);
|
||||
if (ctx != nullptr) {
|
||||
ctx->unregisterTransaction();
|
||||
}
|
||||
|
||||
V8DealerFeature::DEALER->exitContext(_context);
|
||||
_context = nullptr;
|
||||
}
|
||||
exitContext();
|
||||
|
||||
_ast.reset();
|
||||
|
||||
|
@ -376,7 +354,7 @@ void Query::registerErrorCustom(int code, char const* details) {
|
|||
void Query::registerWarning(int code, char const* details) {
|
||||
TRI_ASSERT(code != TRI_ERROR_NO_ERROR);
|
||||
|
||||
if (DoFailOnWarning) {
|
||||
if (_queryRegistry->failOnWarning()) {
|
||||
// make an error from each warning if requested
|
||||
if (details == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(code);
|
||||
|
@ -1139,7 +1117,6 @@ void Query::exitContext() {
|
|||
V8DealerFeature::DEALER->exitContext(_context);
|
||||
_context = nullptr;
|
||||
}
|
||||
TRI_ASSERT(_context == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1473,8 +1450,18 @@ Graph const* Query::lookupGraphByName(std::string const& name) {
|
|||
|
||||
return g.release();
|
||||
}
|
||||
|
||||
size_t Query::memoryLimit() const {
|
||||
uint64_t globalLimit = _queryRegistry->queryMemoryLimit();
|
||||
uint64_t value = getNumericOption<uint64_t>("memoryLimit", globalLimit);
|
||||
if (value > 0) {
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @brief returns the next query id
|
||||
TRI_voc_tick_t Query::NextId() {
|
||||
return NextQueryId.fetch_add(1, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace velocypack {
|
|||
class Builder;
|
||||
}
|
||||
|
||||
class QueryRegistryFeature;
|
||||
|
||||
namespace aql {
|
||||
|
||||
struct AstNode;
|
||||
|
@ -75,12 +77,12 @@ class Query {
|
|||
|
||||
public:
|
||||
Query(bool, TRI_vocbase_t*, char const*, size_t,
|
||||
std::shared_ptr<arangodb::velocypack::Builder>,
|
||||
std::shared_ptr<arangodb::velocypack::Builder>, QueryPart);
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& bindParameters,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& options, QueryPart);
|
||||
|
||||
Query(bool, TRI_vocbase_t*,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const,
|
||||
std::shared_ptr<arangodb::velocypack::Builder>, QueryPart);
|
||||
Query(bool contextOwnedByExterior, TRI_vocbase_t*,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& queryStruct,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& options, QueryPart);
|
||||
|
||||
~Query();
|
||||
|
||||
|
@ -185,13 +187,7 @@ class Query {
|
|||
}
|
||||
|
||||
/// @brief memory limit for query
|
||||
size_t memoryLimit() const {
|
||||
uint64_t value = getNumericOption<decltype(MemoryLimitValue)>("memoryLimit", MemoryLimitValue);
|
||||
if (value > 0) {
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size_t memoryLimit() const;
|
||||
|
||||
/// @brief maximum number of plans to produce
|
||||
int64_t literalSizeThreshold() const {
|
||||
|
@ -274,37 +270,6 @@ class Query {
|
|||
/// NOTE: returns nullptr if there are no warnings.
|
||||
std::shared_ptr<arangodb::velocypack::Builder> warningsToVelocyPack() const;
|
||||
|
||||
/// @brief fetch the query memory limit
|
||||
static uint64_t MemoryLimit() { return MemoryLimitValue; }
|
||||
|
||||
/// @brief set the query memory limit
|
||||
static void MemoryLimit(uint64_t value) {
|
||||
MemoryLimitValue = value;
|
||||
}
|
||||
|
||||
/// @brief fetch the global query tracking value
|
||||
static bool DisableQueryTracking() { return DoDisableQueryTracking; }
|
||||
|
||||
/// @brief turn off tracking globally
|
||||
static void DisableQueryTracking(bool value) {
|
||||
DoDisableQueryTracking = value;
|
||||
}
|
||||
|
||||
/// @brief whether a warning in an AQL query should raise an error
|
||||
static bool FailOnWarning() { return DoFailOnWarning; }
|
||||
|
||||
static void FailOnWarning(bool value) {
|
||||
DoFailOnWarning = value;
|
||||
}
|
||||
|
||||
/// @brief fetch the global slow query threshold value
|
||||
static double SlowQueryThreshold() { return SlowQueryThresholdValue; }
|
||||
|
||||
/// @brief set global slow query threshold value
|
||||
static void SlowQueryThreshold(double value) {
|
||||
SlowQueryThresholdValue = value;
|
||||
}
|
||||
|
||||
/// @brief get a description of the query's current state
|
||||
std::string getStateString() const;
|
||||
|
||||
|
@ -400,7 +365,7 @@ class Query {
|
|||
/// @brief the currently used V8 context
|
||||
V8Context* _context;
|
||||
|
||||
/// @brief warnings collected during execution
|
||||
/// @brief graphs used in query, identified by name
|
||||
std::unordered_map<std::string, Graph*> _graphs;
|
||||
|
||||
/// @brief the actual query string
|
||||
|
@ -452,6 +417,8 @@ class Query {
|
|||
/// @brief query start time
|
||||
double _startTime;
|
||||
|
||||
QueryRegistryFeature const* _queryRegistry;
|
||||
|
||||
/// @brief the query part
|
||||
QueryPart const _part;
|
||||
|
||||
|
@ -463,18 +430,6 @@ class Query {
|
|||
|
||||
/// @brief whether or not the query is a data modification query
|
||||
bool _isModificationQuery;
|
||||
|
||||
/// @brief global memory limit for AQL queries
|
||||
static uint64_t MemoryLimitValue;
|
||||
|
||||
/// @brief global threshold value for slow queries
|
||||
static double SlowQueryThresholdValue;
|
||||
|
||||
/// @brief whether or not query tracking is disabled globally
|
||||
static bool DoDisableQueryTracking;
|
||||
|
||||
/// @brief whether a warning in an AQL query should raise an error
|
||||
static bool DoFailOnWarning;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
/// @author Jan Steemann
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Aql/QueryList.h"
|
||||
#include "QueryList.h"
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Aql/Query.h"
|
||||
#include "Aql/QueryProfile.h"
|
||||
#include "Basics/ReadLocker.h"
|
||||
|
@ -29,8 +30,10 @@
|
|||
#include "Basics/WriteLocker.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "RestServer/QueryRegistryFeature.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::aql;
|
||||
|
||||
QueryEntryCopy::QueryEntryCopy(TRI_voc_tick_t id,
|
||||
|
@ -41,21 +44,17 @@ QueryEntryCopy::QueryEntryCopy(TRI_voc_tick_t id,
|
|||
: id(id), queryString(std::move(queryString)), bindParameters(bindParameters),
|
||||
started(started), runTime(runTime), state(state) {}
|
||||
|
||||
double const QueryList::DefaultSlowQueryThreshold = 10.0;
|
||||
size_t const QueryList::DefaultMaxSlowQueries = 64;
|
||||
size_t const QueryList::DefaultMaxQueryStringLength = 4096;
|
||||
|
||||
/// @brief create a query list
|
||||
QueryList::QueryList(TRI_vocbase_t*)
|
||||
: _lock(),
|
||||
_current(),
|
||||
_slow(),
|
||||
_slowCount(0),
|
||||
_enabled(!Query::DisableQueryTracking()),
|
||||
_trackSlowQueries(!Query::DisableQueryTracking()),
|
||||
_slowQueryThreshold(Query::SlowQueryThreshold()),
|
||||
_maxSlowQueries(QueryList::DefaultMaxSlowQueries),
|
||||
_maxQueryStringLength(QueryList::DefaultMaxQueryStringLength) {
|
||||
_enabled(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")->queryTracking()),
|
||||
_trackSlowQueries(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")->queryTracking()),
|
||||
_slowQueryThreshold(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")->slowThreshold()),
|
||||
_maxSlowQueries(defaultMaxSlowQueries),
|
||||
_maxQueryStringLength(defaultMaxQueryStringLength) {
|
||||
_current.reserve(64);
|
||||
}
|
||||
|
||||
|
|
|
@ -162,6 +162,12 @@ class QueryList {
|
|||
private:
|
||||
std::string extractQueryString(Query const* query, size_t maxLength) const;
|
||||
|
||||
/// @brief default maximum number of slow queries to keep in list
|
||||
static constexpr size_t defaultMaxSlowQueries = 64;
|
||||
|
||||
/// @brief default max length of a query when returning it
|
||||
static constexpr size_t defaultMaxQueryStringLength = 4096;
|
||||
|
||||
private:
|
||||
/// @brief r/w lock for the list
|
||||
arangodb::basics::ReadWriteLock _lock;
|
||||
|
@ -189,15 +195,6 @@ class QueryList {
|
|||
|
||||
/// @brief max length of query strings to return
|
||||
size_t _maxQueryStringLength;
|
||||
|
||||
/// @brief default threshold for slow queries
|
||||
static double const DefaultSlowQueryThreshold;
|
||||
|
||||
/// @brief default maximum number of slow queries to keep in list
|
||||
static size_t const DefaultMaxSlowQueries;
|
||||
|
||||
/// @brief default max length of a query when returning it
|
||||
static size_t const DefaultMaxQueryStringLength;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "RestServer/DatabaseFeature.h"
|
||||
#include "RestServer/ServerFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -23,12 +23,15 @@
|
|||
#include "InitDatabaseFeature.h"
|
||||
|
||||
#include "Basics/FileUtils.h"
|
||||
#include "Basics/terminal-utils.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "Logger/LoggerFeature.h"
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
#include "RestServer/DatabasePathFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -80,22 +80,7 @@ void QueryRegistryFeature::collectOptions(
|
|||
new UInt64Parameter(&_queryCacheEntries));
|
||||
}
|
||||
|
||||
void QueryRegistryFeature::validateOptions(
|
||||
std::shared_ptr<ProgramOptions> options) {
|
||||
}
|
||||
|
||||
void QueryRegistryFeature::prepare() {
|
||||
// set query memory limit
|
||||
arangodb::aql::Query::MemoryLimit(_queryMemoryLimit);
|
||||
|
||||
// set global query tracking flag
|
||||
arangodb::aql::Query::DisableQueryTracking(!_queryTracking);
|
||||
|
||||
arangodb::aql::Query::FailOnWarning(_failOnWarning);
|
||||
|
||||
// set global threshold value for slow queries
|
||||
arangodb::aql::Query::SlowQueryThreshold(_slowThreshold);
|
||||
|
||||
// configure the query cache
|
||||
std::pair<std::string, size_t> cacheProperties{_queryCacheMode,
|
||||
_queryCacheEntries};
|
||||
|
@ -106,8 +91,7 @@ void QueryRegistryFeature::prepare() {
|
|||
QUERY_REGISTRY = _queryRegistry.get();
|
||||
}
|
||||
|
||||
void QueryRegistryFeature::start() {
|
||||
}
|
||||
void QueryRegistryFeature::start() {}
|
||||
|
||||
void QueryRegistryFeature::unprepare() {
|
||||
// clear the query registery
|
||||
|
|
|
@ -39,11 +39,15 @@ class QueryRegistryFeature final : public application_features::ApplicationFeatu
|
|||
|
||||
public:
|
||||
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
|
||||
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
|
||||
void prepare() override final;
|
||||
void start() override final;
|
||||
void unprepare() override final;
|
||||
|
||||
bool queryTracking() const { return _queryTracking; }
|
||||
bool failOnWarning() const { return _failOnWarning; }
|
||||
uint64_t queryMemoryLimit() const { return _queryMemoryLimit; }
|
||||
double slowThreshold() const { return _slowThreshold; }
|
||||
|
||||
private:
|
||||
bool _queryTracking;
|
||||
bool _failOnWarning;
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "V8Server/V8Context.h"
|
||||
#include "V8Server/V8DealerFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/property_tree/detail/xml_parser_utils.hpp>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "SimpleHttpClient/SimpleHttpClient.h"
|
||||
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "SimpleHttpClient/SimpleHttpClient.h"
|
||||
#include "Ssl/ssl-helper.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::httpclient;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "ProgramOptions/Section.h"
|
||||
#include "Shell/ClientFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "Logger/Logger.h"
|
||||
#include "ProgramOptions/ArgumentParser.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "ProgramOptions/IniFileParser.h"
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
#include "ProgramOptions/Translator.h"
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
@ -71,7 +72,7 @@ void ConfigFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
void ConfigFeature::loadOptions(std::shared_ptr<ProgramOptions> options,
|
||||
char const* binaryPath) {
|
||||
for (auto const& def : _defines) {
|
||||
DefineEnvironment(def);
|
||||
arangodb::options::DefineEnvironment(def);
|
||||
}
|
||||
|
||||
loadConfigFile(options, _progname, binaryPath);
|
||||
|
|
|
@ -188,6 +188,9 @@ add_library(${LIB_ARANGO} STATIC
|
|||
Logger/LoggerBufferFeature.cpp
|
||||
Logger/LoggerFeature.cpp
|
||||
Logger/LoggerStream.cpp
|
||||
ProgramOptions/Option.cpp
|
||||
ProgramOptions/ProgramOptions.cpp
|
||||
ProgramOptions/Section.cpp
|
||||
ProgramOptions/Translator.cpp
|
||||
Random/RandomFeature.cpp
|
||||
Random/RandomGenerator.cpp
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "Basics/FileUtils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Option.h"
|
||||
|
||||
#include "ProgramOptions/Parameters.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::options;
|
||||
|
||||
// create an option, consisting of single string
|
||||
Option::Option(std::string const& value, std::string const& description,
|
||||
Parameter* parameter, bool hidden, bool obsolete)
|
||||
: section(),
|
||||
name(),
|
||||
description(description),
|
||||
shorthand(),
|
||||
parameter(parameter),
|
||||
hidden(hidden),
|
||||
obsolete(obsolete) {
|
||||
auto parts = splitName(value);
|
||||
section = parts.first;
|
||||
name = parts.second;
|
||||
|
||||
size_t const pos = name.find(',');
|
||||
if (pos != std::string::npos) {
|
||||
shorthand = stripShorthand(name.substr(pos + 1));
|
||||
name = name.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void Option::toVPack(VPackBuilder& builder) const {
|
||||
parameter->toVPack(builder);
|
||||
}
|
||||
|
||||
// print help for an option
|
||||
// the special search string "." will show help for all sections, even if hidden
|
||||
void Option::printHelp(std::string const& search, size_t tw, size_t ow, bool) const {
|
||||
if (search == "." || !hidden) {
|
||||
std::cout << " " << pad(nameWithType(), ow) << " ";
|
||||
|
||||
std::string value = description;
|
||||
if (obsolete) {
|
||||
value += " (obsolete option)";
|
||||
} else {
|
||||
std::string description = parameter->description();
|
||||
if (!description.empty()) {
|
||||
value.append(". ");
|
||||
value.append(description);
|
||||
}
|
||||
value += " (default: " + parameter->valueString() + ")";
|
||||
}
|
||||
auto parts = wordwrap(value, tw - ow - 6);
|
||||
size_t const n = parts.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
std::cout << trim(parts[i]) << std::endl;
|
||||
if (i < n - 1) {
|
||||
std::cout << " " << pad("", ow) << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine the width of an option help string
|
||||
size_t Option::optionsWidth() const {
|
||||
if (hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nameWithType().size();
|
||||
}
|
||||
|
||||
// strip the "--" from a string
|
||||
std::string Option::stripPrefix(std::string const& name) {
|
||||
size_t pos = name.find("--");
|
||||
if (pos == 0) {
|
||||
// strip initial "--"
|
||||
return name.substr(2);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// strip the "-" from a string
|
||||
std::string Option::stripShorthand(std::string const& name) {
|
||||
size_t pos = name.find("-");
|
||||
if (pos == 0) {
|
||||
// strip initial "-"
|
||||
return name.substr(1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// split an option name at the ".", if it exists
|
||||
std::pair<std::string, std::string> Option::splitName(std::string name) {
|
||||
std::string section;
|
||||
name = stripPrefix(name);
|
||||
// split at "."
|
||||
size_t pos = name.find(".");
|
||||
if (pos == std::string::npos) {
|
||||
// global option
|
||||
section = "";
|
||||
} else {
|
||||
// section-specific option
|
||||
section = name.substr(0, pos);
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
return std::make_pair(section, name);
|
||||
}
|
||||
|
||||
std::vector<std::string> Option::wordwrap(std::string const& value,
|
||||
size_t size) {
|
||||
std::vector<std::string> result;
|
||||
std::string next = value;
|
||||
|
||||
if (size > 0) {
|
||||
while (next.size() > size) {
|
||||
size_t m = next.find_last_of("., ", size - 1);
|
||||
|
||||
if (m == std::string::npos || m < size / 2) {
|
||||
m = size;
|
||||
} else {
|
||||
m += 1;
|
||||
}
|
||||
|
||||
result.emplace_back(next.substr(0, m));
|
||||
next = next.substr(m);
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(next);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// right-pad a string
|
||||
std::string Option::pad(std::string const& value, size_t length) {
|
||||
size_t const valueLength = value.size();
|
||||
if (valueLength > length) {
|
||||
return value.substr(0, length);
|
||||
}
|
||||
if (valueLength == length) {
|
||||
return value;
|
||||
}
|
||||
return value + std::string(length - valueLength, ' ');
|
||||
}
|
||||
|
||||
std::string Option::trim(std::string const& value) {
|
||||
size_t const pos = value.find_first_not_of(" \t\n\r");
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return value.substr(pos);
|
||||
}
|
|
@ -27,41 +27,21 @@
|
|||
#include "ProgramOptions/Parameters.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace arangodb {
|
||||
namespace options {
|
||||
|
||||
struct Parameter;
|
||||
|
||||
// a single program option container
|
||||
struct Option {
|
||||
// options are default copy-constructible and default movable
|
||||
|
||||
// create an option, consisting of single string
|
||||
Option(std::string const& value, std::string const& description,
|
||||
Parameter* parameter, bool hidden, bool obsolete)
|
||||
: section(),
|
||||
name(),
|
||||
description(description),
|
||||
shorthand(),
|
||||
parameter(parameter),
|
||||
hidden(hidden),
|
||||
obsolete(obsolete) {
|
||||
auto parts = splitName(value);
|
||||
section = parts.first;
|
||||
name = parts.second;
|
||||
Parameter* parameter, bool hidden, bool obsolete);
|
||||
|
||||
size_t const pos = name.find(',');
|
||||
if (pos != std::string::npos) {
|
||||
shorthand = stripShorthand(name.substr(pos + 1));
|
||||
name = name.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void toVPack(VPackBuilder& builder) const {
|
||||
parameter->toVPack(builder);
|
||||
}
|
||||
void toVPack(arangodb::velocypack::Builder& builder) const;
|
||||
|
||||
// get display name for the option
|
||||
std::string displayName() const { return "--" + fullName(); }
|
||||
|
@ -76,127 +56,31 @@ struct Option {
|
|||
|
||||
// print help for an option
|
||||
// the special search string "." will show help for all sections, even if hidden
|
||||
void printHelp(std::string const& search, size_t tw, size_t ow, bool) const {
|
||||
if (search == "." || !hidden) {
|
||||
std::cout << " " << pad(nameWithType(), ow) << " ";
|
||||
|
||||
std::string value = description;
|
||||
if (obsolete) {
|
||||
value += " (obsolete option)";
|
||||
} else {
|
||||
std::string description = parameter->description();
|
||||
if (!description.empty()) {
|
||||
value.append(". ");
|
||||
value.append(description);
|
||||
}
|
||||
value += " (default: " + parameter->valueString() + ")";
|
||||
}
|
||||
auto parts = wordwrap(value, tw - ow - 6);
|
||||
size_t const n = parts.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
std::cout << trim(parts[i]) << std::endl;
|
||||
if (i < n - 1) {
|
||||
std::cout << " " << pad("", ow) << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void printHelp(std::string const& search, size_t tw, size_t ow, bool) const;
|
||||
|
||||
std::string nameWithType() const {
|
||||
return displayName() + " " + parameter->typeDescription();
|
||||
}
|
||||
|
||||
// determine the width of an option help string
|
||||
size_t optionsWidth() const {
|
||||
if (hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nameWithType().size();
|
||||
}
|
||||
size_t optionsWidth() const;
|
||||
|
||||
// strip the "--" from a string
|
||||
static std::string stripPrefix(std::string const& name) {
|
||||
size_t pos = name.find("--");
|
||||
if (pos == 0) {
|
||||
// strip initial "--"
|
||||
return name.substr(2);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
static std::string stripPrefix(std::string const& name);
|
||||
|
||||
// strip the "-" from a string
|
||||
static std::string stripShorthand(std::string const& name) {
|
||||
size_t pos = name.find("-");
|
||||
if (pos == 0) {
|
||||
// strip initial "-"
|
||||
return name.substr(1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
static std::string stripShorthand(std::string const& name);
|
||||
|
||||
// split an option name at the ".", if it exists
|
||||
static std::pair<std::string, std::string> splitName(std::string name) {
|
||||
std::string section;
|
||||
name = stripPrefix(name);
|
||||
// split at "."
|
||||
size_t pos = name.find(".");
|
||||
if (pos == std::string::npos) {
|
||||
// global option
|
||||
section = "";
|
||||
} else {
|
||||
// section-specific option
|
||||
section = name.substr(0, pos);
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
return std::make_pair(section, name);
|
||||
}
|
||||
static std::pair<std::string, std::string> splitName(std::string name);
|
||||
|
||||
static std::vector<std::string> wordwrap(std::string const& value,
|
||||
size_t size) {
|
||||
std::vector<std::string> result;
|
||||
std::string next = value;
|
||||
|
||||
if (size > 0) {
|
||||
while (next.size() > size) {
|
||||
size_t m = next.find_last_of("., ", size - 1);
|
||||
|
||||
if (m == std::string::npos || m < size / 2) {
|
||||
m = size;
|
||||
} else {
|
||||
m += 1;
|
||||
}
|
||||
|
||||
result.emplace_back(next.substr(0, m));
|
||||
next = next.substr(m);
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(next);
|
||||
|
||||
return result;
|
||||
}
|
||||
size_t size);
|
||||
|
||||
// right-pad a string
|
||||
static std::string pad(std::string const& value, size_t length) {
|
||||
size_t const valueLength = value.size();
|
||||
if (valueLength > length) {
|
||||
return value.substr(0, length);
|
||||
}
|
||||
if (valueLength == length) {
|
||||
return value;
|
||||
}
|
||||
return value + std::string(length - valueLength, ' ');
|
||||
}
|
||||
static std::string pad(std::string const& value, size_t length);
|
||||
|
||||
static std::string trim(std::string const& value) {
|
||||
size_t const pos = value.find_first_not_of(" \t\n\r");
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return value.substr(pos);
|
||||
}
|
||||
static std::string trim(std::string const& value);
|
||||
|
||||
std::string section;
|
||||
std::string name;
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "ProgramOptions.h"
|
||||
#include "Basics/levenshtein.h"
|
||||
#include "Basics/shell-colors.h"
|
||||
#include "Basics/terminal-utils.h"
|
||||
#include "ProgramOptions/Option.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
#include "ProgramOptions/Translator.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define ARANGODB_PROGRAM_OPTIONS_PROGNAME "#progname#"
|
||||
|
||||
using namespace arangodb::options;
|
||||
|
||||
ProgramOptions::ProgramOptions(char const* progname, std::string const& usage,
|
||||
std::string const& more,
|
||||
char const* binaryPath)
|
||||
: _progname(progname),
|
||||
_usage(usage),
|
||||
_more(more),
|
||||
_terminalWidth(TRI_ColumnsWidth),
|
||||
_similarity(TRI_Levenshtein),
|
||||
_processingResult(),
|
||||
_sealed(false),
|
||||
_overrideOptions(false),
|
||||
_binaryPath(binaryPath){
|
||||
// find progname wildcard in string
|
||||
size_t const pos = _usage.find(ARANGODB_PROGRAM_OPTIONS_PROGNAME);
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
// and replace it with actual program name
|
||||
_usage = usage.substr(0, pos) + _progname +
|
||||
_usage.substr(pos + strlen(ARANGODB_PROGRAM_OPTIONS_PROGNAME));
|
||||
}
|
||||
|
||||
_translator = EnvironmentTranslator;
|
||||
}
|
||||
|
||||
// sets a value translator
|
||||
void ProgramOptions::setTranslator(
|
||||
std::function<std::string(std::string const&, char const*)> translator) {
|
||||
_translator = translator;
|
||||
}
|
||||
|
||||
// prints usage information
|
||||
void ProgramOptions::printUsage() const { std::cout << _usage << std::endl << std::endl; }
|
||||
|
||||
// prints a help for all options, or the options of a section
|
||||
// the special search string "*" will show help for all sections
|
||||
// the special search string "." will show help for all sections, even if
|
||||
// hidden
|
||||
void ProgramOptions::printHelp(std::string const& search) const {
|
||||
bool const colors = (isatty(STDOUT_FILENO) != 0);
|
||||
printUsage();
|
||||
|
||||
size_t const tw = _terminalWidth();
|
||||
size_t const ow = optionsWidth();
|
||||
|
||||
for (auto const& it : _sections) {
|
||||
if (search == "*" || search == "." || search == it.second.name) {
|
||||
it.second.printHelp(search, tw, ow, colors);
|
||||
}
|
||||
}
|
||||
|
||||
if (search == "*") {
|
||||
printSectionsHelp();
|
||||
}
|
||||
}
|
||||
|
||||
// prints the names for all section help options
|
||||
void ProgramOptions::printSectionsHelp() const {
|
||||
char const* colorStart;
|
||||
char const* colorEnd;
|
||||
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
colorStart = TRI_SHELL_COLOR_BRIGHT;
|
||||
colorEnd = TRI_SHELL_COLOR_RESET;
|
||||
} else {
|
||||
colorStart = colorEnd = "";
|
||||
}
|
||||
|
||||
// print names of sections
|
||||
std::cout << _more;
|
||||
for (auto const& it : _sections) {
|
||||
if (!it.second.name.empty() && it.second.hasOptions()) {
|
||||
std::cout << " " << colorStart << "--help-" << it.second.name
|
||||
<< colorEnd;
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// returns a VPack representation of the option values
|
||||
VPackBuilder ProgramOptions::toVPack(bool onlyTouched,
|
||||
std::unordered_set<std::string> const& exclude) const {
|
||||
VPackBuilder builder;
|
||||
builder.openObject();
|
||||
|
||||
walk(
|
||||
[&builder, &exclude](Section const&, Option const& option) {
|
||||
std::string full(option.fullName());
|
||||
if (exclude.find(full) != exclude.end()) {
|
||||
// excluded option
|
||||
return;
|
||||
}
|
||||
|
||||
// add key
|
||||
builder.add(VPackValue(full));
|
||||
|
||||
// add value
|
||||
option.toVPack(builder);
|
||||
},
|
||||
onlyTouched);
|
||||
|
||||
builder.close();
|
||||
return builder;
|
||||
}
|
||||
|
||||
// translate a shorthand option
|
||||
std::string ProgramOptions::translateShorthand(std::string const& name) const {
|
||||
auto it = _shorthands.find(name);
|
||||
|
||||
if (it == _shorthands.end()) {
|
||||
return name;
|
||||
}
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
void ProgramOptions::walk(std::function<void(Section const&, Option const&)> const& callback,
|
||||
bool onlyTouched) const {
|
||||
for (auto const& it : _sections) {
|
||||
if (it.second.obsolete) {
|
||||
// obsolete section. ignore it
|
||||
continue;
|
||||
}
|
||||
for (auto const& it2 : it.second.options) {
|
||||
if (it2.second.obsolete) {
|
||||
// obsolete option. ignore it
|
||||
continue;
|
||||
}
|
||||
if (onlyTouched && !_processingResult.touched(it2.second.fullName())) {
|
||||
// option not touched. skip over it
|
||||
continue;
|
||||
}
|
||||
callback(it.second, it2.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks whether a specific option exists
|
||||
// if the option does not exist, this will flag an error
|
||||
bool ProgramOptions::require(std::string const& name) {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sets a value for an option
|
||||
bool ProgramOptions::setValue(std::string const& name, std::string const& value) {
|
||||
if (!_overrideOptions && _processingResult.frozen(name)) {
|
||||
// option already frozen. don't override it
|
||||
return true;
|
||||
}
|
||||
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
if ((*it).second.obsolete) {
|
||||
// section is obsolete. ignore it
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
auto& option = (*it2).second;
|
||||
if (option.obsolete) {
|
||||
// option is obsolete. ignore it
|
||||
_processingResult.touch(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string result = option.parameter->set(_translator(value, _binaryPath));
|
||||
|
||||
if (!result.empty()) {
|
||||
// parameter validation failed
|
||||
return fail("error setting value for option '--" + name + "': " + result);
|
||||
}
|
||||
|
||||
_processingResult.touch(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// finalizes a pass, copying touched into frozen
|
||||
void ProgramOptions::endPass() {
|
||||
if (_overrideOptions) {
|
||||
return;
|
||||
}
|
||||
for (auto const& it : _processingResult._touched) {
|
||||
_processingResult.freeze(it);
|
||||
}
|
||||
}
|
||||
|
||||
// check whether or not an option requires a value
|
||||
bool ProgramOptions::requiresValue(std::string const& name) const {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (*it2).second.parameter->requiresValue();
|
||||
}
|
||||
|
||||
// returns an option description
|
||||
std::string ProgramOptions::getDescription(std::string const& name) {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return (*it2).second.description;
|
||||
}
|
||||
|
||||
// handle an unknown option
|
||||
bool ProgramOptions::unknownOption(std::string const& name) {
|
||||
char const* colorStart;
|
||||
char const* colorEnd;
|
||||
|
||||
if (isatty(STDERR_FILENO)) {
|
||||
colorStart = TRI_SHELL_COLOR_BRIGHT;
|
||||
colorEnd = TRI_SHELL_COLOR_RESET;
|
||||
} else {
|
||||
colorStart = colorEnd = "";
|
||||
}
|
||||
|
||||
fail(std::string("unknown option '") + colorStart + "--" + name + colorEnd +
|
||||
"'");
|
||||
|
||||
auto similarOptions = similar(name, 8, 4);
|
||||
if (!similarOptions.empty()) {
|
||||
if (similarOptions.size() == 1) {
|
||||
std::cerr << "Did you mean this?" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Did you mean one of these?" << std::endl;
|
||||
}
|
||||
// determine maximum width
|
||||
size_t maxWidth = 0;
|
||||
for (auto const& it : similarOptions) {
|
||||
maxWidth = (std::max)(maxWidth, it.size());
|
||||
}
|
||||
|
||||
for (auto const& it : similarOptions) {
|
||||
std::cerr << " " << colorStart << Option::pad(it, maxWidth) << colorEnd
|
||||
<< " " << getDescription(it) << std::endl;
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
auto it = _oldOptions.find(name);
|
||||
if (it != _oldOptions.end()) {
|
||||
// a now removed or renamed option was specified...
|
||||
auto& now = (*it).second;
|
||||
if (now.empty()) {
|
||||
std::cerr << "Please note that the specified option '" << colorStart
|
||||
<< "--" << name << colorEnd
|
||||
<< "' has been removed in this ArangoDB version";
|
||||
} else {
|
||||
std::cerr << "Please note that the specified option '" << colorStart
|
||||
<< "--" << name << colorEnd << "' has been renamed to '--"
|
||||
<< colorStart << now << colorEnd
|
||||
<< "' in this ArangoDB version";
|
||||
}
|
||||
|
||||
std::cerr
|
||||
<< std::endl
|
||||
<< "Please be sure to read the manual section about changed options"
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Use " << colorStart << "--help" << colorEnd << " or "
|
||||
<< colorStart << "--help-all" << colorEnd
|
||||
<< " to get an overview of available options" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// report an error (callback from parser)
|
||||
bool ProgramOptions::fail(std::string const& message) {
|
||||
_processingResult.failed(true);
|
||||
std::cerr << "Error while processing " << _context << ":" << std::endl;
|
||||
failNotice(message);
|
||||
std::cerr << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProgramOptions::failNotice(std::string const& message) {
|
||||
_processingResult.failed(true);
|
||||
std::cerr << " " << message << std::endl;
|
||||
}
|
||||
|
||||
// add a positional argument (callback from parser)
|
||||
void ProgramOptions::addPositional(std::string const& value) {
|
||||
_processingResult._positionals.emplace_back(value);
|
||||
}
|
||||
|
||||
// adds an option to the list of options
|
||||
void ProgramOptions::addOption(Option const& option) {
|
||||
checkIfSealed();
|
||||
auto it = _sections.find(option.section);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
throw std::logic_error(
|
||||
std::string("no section defined for program option ") +
|
||||
option.displayName());
|
||||
}
|
||||
|
||||
if (!option.shorthand.empty()) {
|
||||
if (!_shorthands.emplace(option.shorthand, option.fullName()).second) {
|
||||
throw std::logic_error(
|
||||
std::string("shorthand option already defined for option ") +
|
||||
option.displayName());
|
||||
}
|
||||
}
|
||||
|
||||
(*it).second.options.emplace(option.name, option);
|
||||
}
|
||||
|
||||
// determine maximum width of all options labels
|
||||
size_t ProgramOptions::optionsWidth() const {
|
||||
size_t width = 0;
|
||||
for (auto const& it : _sections) {
|
||||
width = (std::max)(width, it.second.optionsWidth());
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
// check if the options are already sealed and throw if yes
|
||||
void ProgramOptions::checkIfSealed() const {
|
||||
if (_sealed) {
|
||||
throw std::logic_error("program options are already sealed");
|
||||
}
|
||||
}
|
||||
|
||||
// get a list of similar options
|
||||
std::vector<std::string> ProgramOptions::similar(std::string const& value, int cutOff,
|
||||
size_t maxResults) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (_similarity != nullptr) {
|
||||
// build a sorted map of similar values first
|
||||
std::multimap<int, std::string> distances;
|
||||
// walk over all options
|
||||
walk(
|
||||
[this, &value, &distances](Section const&, Option const& option) {
|
||||
if (option.fullName() != value) {
|
||||
distances.emplace(_similarity(value, option.fullName()),
|
||||
option.displayName());
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
// now return the ones that have an edit distance not higher than the
|
||||
// cutOff value
|
||||
int last = 0;
|
||||
for (auto const& it : distances) {
|
||||
if (last > 1 && it.first > 2 * last) {
|
||||
break;
|
||||
}
|
||||
if (it.first > cutOff) {
|
||||
continue;
|
||||
}
|
||||
result.emplace_back(it.second);
|
||||
if (result.size() >= maxResults) {
|
||||
break;
|
||||
}
|
||||
last = it.first;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -25,16 +25,10 @@
|
|||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include "Basics/levenshtein.h"
|
||||
#include "Basics/terminal-utils.h"
|
||||
#include "ProgramOptions/Option.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
#include "ProgramOptions/Translator.h"
|
||||
|
||||
#define ARANGODB_PROGRAM_OPTIONS_PROGNAME "#progname#"
|
||||
#include <velocypack/Builder.h>
|
||||
|
||||
namespace arangodb {
|
||||
namespace options {
|
||||
|
@ -102,35 +96,11 @@ class ProgramOptions {
|
|||
|
||||
ProgramOptions(char const* progname, std::string const& usage,
|
||||
std::string const& more,
|
||||
const char *binaryPath,
|
||||
TerminalWidthFuncType const& terminalWidth = TRI_ColumnsWidth,
|
||||
SimilarityFuncType const& similarity = TRI_Levenshtein)
|
||||
: _progname(progname),
|
||||
_usage(usage),
|
||||
_more(more),
|
||||
_terminalWidth(terminalWidth),
|
||||
_similarity(similarity),
|
||||
_processingResult(),
|
||||
_sealed(false),
|
||||
_overrideOptions(false),
|
||||
_binaryPath(binaryPath){
|
||||
// find progname wildcard in string
|
||||
size_t const pos = _usage.find(ARANGODB_PROGRAM_OPTIONS_PROGNAME);
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
// and replace it with actual program name
|
||||
_usage = usage.substr(0, pos) + _progname +
|
||||
_usage.substr(pos + strlen(ARANGODB_PROGRAM_OPTIONS_PROGNAME));
|
||||
}
|
||||
|
||||
_translator = EnvironmentTranslator;
|
||||
}
|
||||
char const* binaryPath);
|
||||
|
||||
// sets a value translator
|
||||
void setTranslator(
|
||||
std::function<std::string(std::string const&, char const*)> translator) {
|
||||
_translator = translator;
|
||||
}
|
||||
std::function<std::string(std::string const&, char const*)> translator);
|
||||
|
||||
// return a const reference to the processing result
|
||||
ProcessingResult const& processingResult() const { return _processingResult; }
|
||||
|
@ -205,201 +175,40 @@ class ProgramOptions {
|
|||
}
|
||||
|
||||
// prints usage information
|
||||
void printUsage() const { std::cout << _usage << std::endl << std::endl; }
|
||||
void printUsage() const;
|
||||
|
||||
// prints a help for all options, or the options of a section
|
||||
// the special search string "*" will show help for all sections
|
||||
// the special search string "." will show help for all sections, even if
|
||||
// hidden
|
||||
void printHelp(std::string const& search) const {
|
||||
bool const colors = (isatty(STDOUT_FILENO) != 0);
|
||||
printUsage();
|
||||
|
||||
size_t const tw = _terminalWidth();
|
||||
size_t const ow = optionsWidth();
|
||||
|
||||
for (auto const& it : _sections) {
|
||||
if (search == "*" || search == "." || search == it.second.name) {
|
||||
it.second.printHelp(search, tw, ow, colors);
|
||||
}
|
||||
}
|
||||
|
||||
if (search == "*") {
|
||||
printSectionsHelp();
|
||||
}
|
||||
}
|
||||
void printHelp(std::string const& search) const;
|
||||
|
||||
// prints the names for all section help options
|
||||
void printSectionsHelp() const {
|
||||
char const* colorStart;
|
||||
char const* colorEnd;
|
||||
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
colorStart = TRI_SHELL_COLOR_BRIGHT;
|
||||
colorEnd = TRI_SHELL_COLOR_RESET;
|
||||
} else {
|
||||
colorStart = colorEnd = "";
|
||||
}
|
||||
|
||||
// print names of sections
|
||||
std::cout << _more;
|
||||
for (auto const& it : _sections) {
|
||||
if (!it.second.name.empty() && it.second.hasOptions()) {
|
||||
std::cout << " " << colorStart << "--help-" << it.second.name
|
||||
<< colorEnd;
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void printSectionsHelp() const;
|
||||
|
||||
// returns a VPack representation of the option values
|
||||
VPackBuilder toVPack(bool onlyTouched,
|
||||
std::unordered_set<std::string> const& exclude) const {
|
||||
VPackBuilder builder;
|
||||
builder.openObject();
|
||||
|
||||
walk(
|
||||
[&builder, &exclude](Section const&, Option const& option) {
|
||||
std::string full(option.fullName());
|
||||
if (exclude.find(full) != exclude.end()) {
|
||||
// excluded option
|
||||
return;
|
||||
}
|
||||
|
||||
// add key
|
||||
builder.add(VPackValue(full));
|
||||
|
||||
// add value
|
||||
option.toVPack(builder);
|
||||
},
|
||||
onlyTouched);
|
||||
|
||||
builder.close();
|
||||
return builder;
|
||||
}
|
||||
arangodb::velocypack::Builder toVPack(bool onlyTouched,
|
||||
std::unordered_set<std::string> const& exclude) const;
|
||||
|
||||
// translate a shorthand option
|
||||
std::string translateShorthand(std::string const& name) const {
|
||||
auto it = _shorthands.find(name);
|
||||
|
||||
if (it == _shorthands.end()) {
|
||||
return name;
|
||||
}
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
std::string translateShorthand(std::string const& name) const;
|
||||
|
||||
void walk(std::function<void(Section const&, Option const&)> const& callback,
|
||||
bool onlyTouched) const {
|
||||
for (auto const& it : _sections) {
|
||||
if (it.second.obsolete) {
|
||||
// obsolete section. ignore it
|
||||
continue;
|
||||
}
|
||||
for (auto const& it2 : it.second.options) {
|
||||
if (it2.second.obsolete) {
|
||||
// obsolete option. ignore it
|
||||
continue;
|
||||
}
|
||||
if (onlyTouched && !_processingResult.touched(it2.second.fullName())) {
|
||||
// option not touched. skip over it
|
||||
continue;
|
||||
}
|
||||
callback(it.second, it2.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool onlyTouched) const;
|
||||
|
||||
// checks whether a specific option exists
|
||||
// if the option does not exist, this will flag an error
|
||||
bool require(std::string const& name) {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool require(std::string const& name);
|
||||
|
||||
// sets a value for an option
|
||||
bool setValue(std::string const& name, std::string const& value) {
|
||||
if (!_overrideOptions && _processingResult.frozen(name)) {
|
||||
// option already frozen. don't override it
|
||||
return true;
|
||||
}
|
||||
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
if ((*it).second.obsolete) {
|
||||
// section is obsolete. ignore it
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return unknownOption(name);
|
||||
}
|
||||
|
||||
auto& option = (*it2).second;
|
||||
if (option.obsolete) {
|
||||
// option is obsolete. ignore it
|
||||
_processingResult.touch(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string result = option.parameter->set(_translator(value, _binaryPath));
|
||||
|
||||
if (!result.empty()) {
|
||||
// parameter validation failed
|
||||
return fail("error setting value for option '--" + name + "': " + result);
|
||||
}
|
||||
|
||||
_processingResult.touch(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setValue(std::string const& name, std::string const& value);
|
||||
|
||||
// finalizes a pass, copying touched into frozen
|
||||
void endPass() {
|
||||
if (_overrideOptions) {
|
||||
return;
|
||||
}
|
||||
for (auto const& it : _processingResult._touched) {
|
||||
_processingResult.freeze(it);
|
||||
}
|
||||
}
|
||||
void endPass();
|
||||
|
||||
// check whether or not an option requires a value
|
||||
bool requiresValue(std::string const& name) const {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (*it2).second.parameter->requiresValue();
|
||||
}
|
||||
|
||||
bool requiresValue(std::string const& name) const;
|
||||
|
||||
// returns a pointer to an option value, specified by option name
|
||||
// returns a nullptr if the option is unknown
|
||||
template <typename T>
|
||||
|
@ -423,184 +232,32 @@ class ProgramOptions {
|
|||
}
|
||||
|
||||
// returns an option description
|
||||
std::string getDescription(std::string const& name) {
|
||||
auto parts = Option::splitName(name);
|
||||
auto it = _sections.find(parts.first);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto it2 = (*it).second.options.find(parts.second);
|
||||
|
||||
if (it2 == (*it).second.options.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return (*it2).second.description;
|
||||
}
|
||||
std::string getDescription(std::string const& name);
|
||||
|
||||
// handle an unknown option
|
||||
bool unknownOption(std::string const& name) {
|
||||
char const* colorStart;
|
||||
char const* colorEnd;
|
||||
|
||||
if (isatty(STDERR_FILENO)) {
|
||||
colorStart = TRI_SHELL_COLOR_BRIGHT;
|
||||
colorEnd = TRI_SHELL_COLOR_RESET;
|
||||
} else {
|
||||
colorStart = colorEnd = "";
|
||||
}
|
||||
|
||||
fail(std::string("unknown option '") + colorStart + "--" + name + colorEnd +
|
||||
"'");
|
||||
|
||||
auto similarOptions = similar(name, 8, 4);
|
||||
if (!similarOptions.empty()) {
|
||||
if (similarOptions.size() == 1) {
|
||||
std::cerr << "Did you mean this?" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Did you mean one of these?" << std::endl;
|
||||
}
|
||||
// determine maximum width
|
||||
size_t maxWidth = 0;
|
||||
for (auto const& it : similarOptions) {
|
||||
maxWidth = (std::max)(maxWidth, it.size());
|
||||
}
|
||||
|
||||
for (auto const& it : similarOptions) {
|
||||
std::cerr << " " << colorStart << Option::pad(it, maxWidth) << colorEnd
|
||||
<< " " << getDescription(it) << std::endl;
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
auto it = _oldOptions.find(name);
|
||||
if (it != _oldOptions.end()) {
|
||||
// a now removed or renamed option was specified...
|
||||
auto& now = (*it).second;
|
||||
if (now.empty()) {
|
||||
std::cerr << "Please note that the specified option '" << colorStart
|
||||
<< "--" << name << colorEnd
|
||||
<< "' has been removed in this ArangoDB version";
|
||||
} else {
|
||||
std::cerr << "Please note that the specified option '" << colorStart
|
||||
<< "--" << name << colorEnd << "' has been renamed to '--"
|
||||
<< colorStart << now << colorEnd
|
||||
<< "' in this ArangoDB version";
|
||||
}
|
||||
|
||||
std::cerr
|
||||
<< std::endl
|
||||
<< "Please be sure to read the manual section about changed options"
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Use " << colorStart << "--help" << colorEnd << " or "
|
||||
<< colorStart << "--help-all" << colorEnd
|
||||
<< " to get an overview of available options" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
bool unknownOption(std::string const& name);
|
||||
|
||||
// report an error (callback from parser)
|
||||
bool fail(std::string const& message) {
|
||||
_processingResult.failed(true);
|
||||
std::cerr << "Error while processing " << _context << ":" << std::endl;
|
||||
failNotice(message);
|
||||
std::cerr << std::endl;
|
||||
return false;
|
||||
}
|
||||
bool fail(std::string const& message);
|
||||
|
||||
void failNotice(std::string const& message) {
|
||||
_processingResult.failed(true);
|
||||
std::cerr << " " << message << std::endl;
|
||||
}
|
||||
void failNotice(std::string const& message);
|
||||
|
||||
// add a positional argument (callback from parser)
|
||||
void addPositional(std::string const& value) {
|
||||
_processingResult._positionals.emplace_back(value);
|
||||
}
|
||||
void addPositional(std::string const& value);
|
||||
|
||||
private:
|
||||
// adds an option to the list of options
|
||||
void addOption(Option const& option) {
|
||||
checkIfSealed();
|
||||
auto it = _sections.find(option.section);
|
||||
|
||||
if (it == _sections.end()) {
|
||||
throw std::logic_error(
|
||||
std::string("no section defined for program option ") +
|
||||
option.displayName());
|
||||
}
|
||||
|
||||
if (!option.shorthand.empty()) {
|
||||
if (!_shorthands.emplace(option.shorthand, option.fullName()).second) {
|
||||
throw std::logic_error(
|
||||
std::string("shorthand option already defined for option ") +
|
||||
option.displayName());
|
||||
}
|
||||
}
|
||||
|
||||
(*it).second.options.emplace(option.name, option);
|
||||
}
|
||||
void addOption(Option const& option);
|
||||
|
||||
// determine maximum width of all options labels
|
||||
size_t optionsWidth() const {
|
||||
size_t width = 0;
|
||||
for (auto const& it : _sections) {
|
||||
width = (std::max)(width, it.second.optionsWidth());
|
||||
}
|
||||
return width;
|
||||
}
|
||||
size_t optionsWidth() const;
|
||||
|
||||
// check if the options are already sealed and throw if yes
|
||||
void checkIfSealed() const {
|
||||
if (_sealed) {
|
||||
throw std::logic_error("program options are already sealed");
|
||||
}
|
||||
}
|
||||
void checkIfSealed() const;
|
||||
|
||||
// get a list of similar options
|
||||
std::vector<std::string> similar(std::string const& value, int cutOff,
|
||||
size_t maxResults) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (_similarity != nullptr) {
|
||||
// build a sorted map of similar values first
|
||||
std::multimap<int, std::string> distances;
|
||||
// walk over all options
|
||||
walk(
|
||||
[this, &value, &distances](Section const&, Option const& option) {
|
||||
if (option.fullName() != value) {
|
||||
distances.emplace(_similarity(value, option.fullName()),
|
||||
option.displayName());
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
// now return the ones that have an edit distance not higher than the
|
||||
// cutOff value
|
||||
int last = 0;
|
||||
for (auto const& it : distances) {
|
||||
if (last > 1 && it.first > 2 * last) {
|
||||
break;
|
||||
}
|
||||
if (it.first > cutOff) {
|
||||
continue;
|
||||
}
|
||||
result.emplace_back(it.second);
|
||||
if (result.size() >= maxResults) {
|
||||
break;
|
||||
}
|
||||
last = it.first;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
size_t maxResults);
|
||||
|
||||
private:
|
||||
// name of binary (i.e. argv[0])
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Section.h"
|
||||
#include "Basics/shell-colors.h"
|
||||
#include "ProgramOptions/Option.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::options;
|
||||
|
||||
// adds a program option to the section
|
||||
void Section::addOption(Option const& option) { options.emplace(option.name, option); }
|
||||
|
||||
// whether or not the section has (displayable) options
|
||||
bool Section::hasOptions() const {
|
||||
if (!hidden) {
|
||||
for (auto const& it : options) {
|
||||
if (!it.second.hidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// print help for a section
|
||||
// the special search string "." will show help for all sections, even if hidden
|
||||
void Section::printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const {
|
||||
if (search != "." && (hidden || !hasOptions())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (colors) {
|
||||
std::cout << "Section '" << TRI_SHELL_COLOR_BRIGHT << displayName() << TRI_SHELL_COLOR_RESET << "' (" << description << ")"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Section '" << displayName() << "' (" << description << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// propagate print command to options
|
||||
for (auto const& it : options) {
|
||||
it.second.printHelp(search, tw, ow, colors);
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// determine display width for a section
|
||||
size_t Section::optionsWidth() const {
|
||||
size_t width = 0;
|
||||
|
||||
if (!hidden) {
|
||||
for (auto const& it : options) {
|
||||
width = (std::max)(width, it.second.optionsWidth());
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
|
@ -24,8 +24,6 @@
|
|||
#define ARANGODB_PROGRAM_OPTIONS_SECTION_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/shell-colors.h"
|
||||
|
||||
#include "ProgramOptions/Option.h"
|
||||
|
||||
namespace arangodb {
|
||||
|
@ -44,58 +42,20 @@ struct Section {
|
|||
obsolete(obsolete) {}
|
||||
|
||||
// adds a program option to the section
|
||||
void addOption(Option const& option) { options.emplace(option.name, option); }
|
||||
void addOption(Option const& option);
|
||||
|
||||
// get display name for the section
|
||||
std::string displayName() const { return alias.empty() ? name : alias; }
|
||||
|
||||
// whether or not the section has (displayable) options
|
||||
bool hasOptions() const {
|
||||
if (!hidden) {
|
||||
for (auto const& it : options) {
|
||||
if (!it.second.hidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool hasOptions() const;
|
||||
|
||||
// print help for a section
|
||||
// the special search string "." will show help for all sections, even if hidden
|
||||
void printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const {
|
||||
if (search != "." && (hidden || !hasOptions())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (colors) {
|
||||
std::cout << "Section '" << TRI_SHELL_COLOR_BRIGHT << displayName() << TRI_SHELL_COLOR_RESET << "' (" << description << ")"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Section '" << displayName() << "' (" << description << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// propagate print command to options
|
||||
for (auto const& it : options) {
|
||||
it.second.printHelp(search, tw, ow, colors);
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const;
|
||||
|
||||
// determine display width for a section
|
||||
size_t optionsWidth() const {
|
||||
size_t width = 0;
|
||||
|
||||
if (!hidden) {
|
||||
for (auto const& it : options) {
|
||||
width = (std::max)(width, it.second.optionsWidth());
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
size_t optionsWidth() const;
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
|
|
Loading…
Reference in New Issue