aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/config
diff options
context:
space:
mode:
authormonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
committermonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
commit06e5c21a835c0e923506c4ff27929f34e00761c2 (patch)
tree75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /library/cpp/config
parent03f024c4412e3aa613bb543cf1660176320ba8f4 (diff)
downloadydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz
fix ya.make
Diffstat (limited to 'library/cpp/config')
-rw-r--r--library/cpp/config/config.cpp425
-rw-r--r--library/cpp/config/config.h146
-rw-r--r--library/cpp/config/domscheme.cpp1
-rw-r--r--library/cpp/config/domscheme.h155
-rw-r--r--library/cpp/config/fwd.h5
-rw-r--r--library/cpp/config/ini.cpp62
-rw-r--r--library/cpp/config/ini.h7
-rw-r--r--library/cpp/config/markup.cpp258
-rw-r--r--library/cpp/config/markup.h7
-rw-r--r--library/cpp/config/markupfsm.h.rl681
-rw-r--r--library/cpp/config/sax.cpp67
-rw-r--r--library/cpp/config/sax.h83
-rw-r--r--library/cpp/config/support/pp.lua63
-rw-r--r--library/cpp/config/value.cpp292
-rw-r--r--library/cpp/config/value.h121
15 files changed, 1773 insertions, 0 deletions
diff --git a/library/cpp/config/config.cpp b/library/cpp/config/config.cpp
new file mode 100644
index 0000000000..e8271af661
--- /dev/null
+++ b/library/cpp/config/config.cpp
@@ -0,0 +1,425 @@
+#include "config.h"
+#include "markup.h"
+#include "ini.h"
+
+#include <library/cpp/archive/yarchive.h>
+
+#include <library/cpp/json/json_reader.h>
+#include <library/cpp/json/json_writer.h>
+
+#include <library/cpp/lua/eval.h>
+
+#include <util/string/cast.h>
+#include <util/string/strip.h>
+#include <util/string/type.h>
+
+#include <util/stream/output.h>
+#include <util/stream/file.h>
+#include <util/memory/blob.h>
+
+#include <util/generic/singleton.h>
+#include <util/stream/str.h>
+
+using namespace NConfig;
+using namespace NJson;
+
+namespace {
+ const unsigned char CODE[] = {
+#include "code.inc"
+ };
+
+ struct TCode: public TArchiveReader {
+ inline TCode()
+ : TArchiveReader(TBlob::NoCopy(CODE, sizeof(CODE)))
+ {
+ }
+
+ static inline TCode& Instance() {
+ return *Singleton<TCode>();
+ }
+ };
+
+ class TPreprocessor: public IInputStream {
+ public:
+ inline TPreprocessor(const TGlobals& g, IInputStream* in)
+ : I_(nullptr, 0)
+ , S_(in)
+ {
+ E_.SetVars(g);
+ }
+
+ size_t DoRead(void* ptr, size_t len) override {
+ while (true) {
+ const size_t read = I_.Read(ptr, len);
+
+ if (read) {
+ return read;
+ }
+
+ do {
+ if (!S_.ReadLine(C_)) {
+ return 0;
+ }
+ } while (IsComment(C_));
+
+ C_ = E_.Preprocess(C_);
+ C_.append('\n');
+ I_.Reset(C_.data(), C_.size());
+ }
+ }
+
+ static inline bool IsComment(TStringBuf s) {
+ s = StripString(s);
+
+ return !s.empty() && s[0] == '#';
+ }
+
+ private:
+ TMemoryInput I_;
+ TBufferedInput S_;
+ TLuaEval E_;
+ TString C_;
+ };
+}
+
+void TConfig::DumpJson(IOutputStream& out) const {
+ TString tmp;
+
+ {
+ TStringOutput out2(tmp);
+
+ ToJson(out2);
+ }
+
+ {
+ TJsonValue v;
+ TStringInput in(tmp);
+
+ ReadJsonTree(&in, &v);
+ WriteJson(&out, &v, true, true);
+ }
+}
+
+TConfig TConfig::FromJson(IInputStream& in, const TGlobals& g) {
+ class TJSONReader: public TJsonCallbacks {
+ public:
+ inline TJSONReader()
+ : Cur(nullptr)
+ {
+ }
+
+ inline bool OnBoolean(bool b) override {
+ *Next() = ConstructValue(b);
+
+ return true;
+ }
+
+ inline bool OnInteger(long long i) override {
+ *Next() = ConstructValue((i64)i);
+
+ return true;
+ }
+
+ inline bool OnUInteger(unsigned long long i) override {
+ *Next() = ConstructValue((ui64)i);
+
+ return true;
+ }
+
+ inline bool OnDouble(double d) override {
+ *Next() = ConstructValue(d);
+
+ return true;
+ }
+
+ inline bool OnString(const TStringBuf& s) override {
+ *Next() = ConstructValue(ToString(s));
+
+ return true;
+ }
+
+ inline bool OnOpenMap() override {
+ Push(ConstructValue(TDict()));
+
+ return true;
+ }
+
+ inline bool OnCloseMap() override {
+ Pop();
+
+ return true;
+ }
+
+ inline bool OnOpenArray() override {
+ Push(ConstructValue(TArray()));
+
+ return true;
+ }
+
+ inline bool OnCloseArray() override {
+ Pop();
+
+ return true;
+ }
+
+ inline bool OnMapKey(const TStringBuf& k) override {
+ if (S.empty()) {
+ ythrow yexception() << "shit happen";
+ }
+
+ Cur = &S.back().GetNonConstant<TDict>()[ToString(k)];
+
+ return true;
+ }
+
+ inline void Push(const TConfig& el) {
+ *Next() = el;
+ S.push_back(el);
+ }
+
+ inline void Pop() {
+ if (S.empty()) {
+ ythrow yexception() << "shit happen";
+ }
+
+ S.pop_back();
+ }
+
+ inline TConfig* Next() {
+ if (S.empty()) {
+ return &Root;
+ }
+
+ TConfig& top = S.back();
+
+ if (top.IsA<TArray>()) {
+ TArray& arr = top.GetNonConstant<TArray>();
+
+ arr.emplace_back();
+
+ return &arr.back();
+ }
+
+ if (top.IsA<TDict>()) {
+ if (Cur) {
+ TConfig* ret = Cur;
+
+ Cur = nullptr;
+
+ return ret;
+ }
+ }
+
+ ythrow yexception() << "shit happen";
+ }
+
+ inline void OnError(size_t off, TStringBuf reason) override {
+ Y_UNUSED(off);
+ if (!FirstErrorReason) {
+ FirstErrorReason = reason;
+ }
+ }
+
+ TConfig Root;
+ TConfig* Cur;
+ TVector<TConfig> S;
+ TString FirstErrorReason;
+ };
+
+ TJSONReader r;
+ TString data = in.ReadAll();
+ TMemoryInput mi(data.data(), data.size());
+ TPreprocessor p(g, &mi);
+
+ if (!NJson::ReadJson(&p, false, true, &r)) {
+ if (!!r.FirstErrorReason) {
+ ythrow TConfigParseError() << "Error parsing json: " << r.FirstErrorReason;
+ } else {
+ ythrow TConfigParseError() << "Could not parse json " << data.Quote();
+ }
+ }
+
+ return r.Root;
+}
+
+namespace {
+ struct TData {
+ const char* Prologue;
+ const char* Epilogue;
+ };
+
+ const TData DATA[] = {
+ {"", "\nassert(not (instance == nil))\nreturn instance\n"},
+ {"", "\nassert(not (main == nil))\nreturn main\n"},
+ {"return ", "\n"},
+ {"", "\n"},
+ };
+}
+
+TConfig TConfig::FromLua(IInputStream& in, const TGlobals& g) {
+ const TString& data = in.ReadAll();
+ TString json;
+
+ for (size_t i = 0; i < Y_ARRAY_SIZE(DATA); ++i) {
+ TStringStream ss;
+
+ ss << TStringBuf("local function configgen()")
+ << DATA[i].Prologue << data << DATA[i].Epilogue
+ << TStringBuf("end\n\nreturn require('json').encode(configgen())\n");
+
+ try {
+ json = TLuaEval().SetVars(g).EvalRaw(ss.Str());
+
+ break;
+ } catch (...) {
+ if (i == (Y_ARRAY_SIZE(DATA) - 1)) {
+ throw;
+ }
+ }
+ }
+
+ TMemoryInput mi(json.data(), json.size());
+
+ return FromJson(mi);
+}
+
+TConfig TConfig::FromMarkup(IInputStream& in, const TGlobals& g) {
+ TPreprocessor pin(g, &in);
+
+ return ParseRawMarkup(pin);
+}
+
+TConfig TConfig::FromIni(IInputStream& in, const TGlobals& g) {
+ TPreprocessor pin(g, &in);
+
+ return ParseIni(pin);
+}
+
+void TConfig::DumpLua(IOutputStream& out) const {
+ TLuaEval e;
+ TStringStream ss;
+
+ ToJson(ss);
+
+ e.SetVariable("jsonval", ss.Str());
+
+ out << "return " << e.EvalRaw(TCode::Instance().ObjectByKey("/pp.lua")->ReadAll() + "\nreturn prettify(require('json').decode(jsonval))\n");
+}
+
+TConfig TConfig::FromStream(IInputStream& in, const TGlobals& g) {
+ const TString& tmp = in.ReadAll();
+ TString luaParsingError = "";
+
+ try {
+ TMemoryInput mi(tmp.data(), tmp.size());
+
+ return FromLua(mi, g);
+ } catch (const yexception& e) {
+ luaParsingError = e.AsStrBuf();
+ } catch (...) {
+ luaParsingError = "unknown error";
+ }
+
+ TMemoryInput mi(tmp.data(), tmp.size());
+
+ try {
+ return FromJson(mi, g);
+ } catch (const yexception& e) {
+ const TStringBuf& jsonParsingError = e.AsStrBuf();
+ ythrow yexception() << "Could not parse config:\nParsing as lua: " << luaParsingError << "\nParsing as json: " << jsonParsingError;
+ }
+}
+
+TConfig TConfig::ReadJson(TStringBuf in, const TGlobals& g) {
+ TMemoryInput mi(in.data(), in.size());
+
+ return FromJson(mi, g);
+}
+
+TConfig TConfig::ReadLua(TStringBuf in, const TGlobals& g) {
+ TMemoryInput mi(in.data(), in.size());
+
+ return FromLua(mi, g);
+}
+
+TConfig TConfig::ReadMarkup(TStringBuf in, const TGlobals& g) {
+ TMemoryInput mi(in.data(), in.size());
+
+ return FromMarkup(mi, g);
+}
+
+TConfig TConfig::ReadIni(TStringBuf in, const TGlobals& g) {
+ TMemoryInput mi(in.data(), in.size());
+
+ return FromIni(mi, g);
+}
+
+void TConfig::Load(IInputStream* input) {
+ TString string;
+ ::Load(input, string);
+ TStringInput stream(string);
+ *this = FromJson(stream);
+}
+
+void TConfig::Save(IOutputStream* output) const {
+ TString result;
+ TStringOutput stream(result);
+ DumpJson(stream);
+ ::Save(output, result);
+}
+
+bool TConfig::Has(const TStringBuf& key) const {
+ return !operator[](key).IsNull();
+}
+
+const TConfig& TConfig::operator[](const TStringBuf& key) const {
+ return Get<TDict>().Find(key);
+}
+
+const TConfig& TConfig::At(const TStringBuf& key) const {
+ return Get<TDict>().At(key);
+}
+
+const TConfig& TConfig::operator[](size_t index) const {
+ return Get<TArray>().Index(index);
+}
+
+size_t TConfig::GetArraySize() const {
+ return Get<TArray>().size();
+}
+
+const TConfig& TDict::Find(const TStringBuf& key) const {
+ const_iterator it = find(key);
+
+ if (it == end()) {
+ return Default<TConfig>();
+ }
+
+ return it->second;
+}
+
+const TConfig& TDict::At(const TStringBuf& key) const {
+ const_iterator it = find(key);
+
+ Y_ENSURE_BT(it != end(), "missing key '" << key << "'");
+
+ return it->second;
+}
+
+const TConfig& TArray::Index(size_t index) const {
+ if (index < size()) {
+ return (*this)[index];
+ }
+
+ return Default<TConfig>();
+}
+
+const TConfig& TArray::At(size_t index) const {
+ Y_ENSURE_BT(index < size(), "index " << index << " is out of bounds");
+
+ return (*this)[index];
+}
+
+THolder<IInputStream> NConfig::CreatePreprocessor(const TGlobals& g, IInputStream& in) {
+ return MakeHolder<TPreprocessor>(g, &in);
+}
diff --git a/library/cpp/config/config.h b/library/cpp/config/config.h
new file mode 100644
index 0000000000..16d2d7edf9
--- /dev/null
+++ b/library/cpp/config/config.h
@@ -0,0 +1,146 @@
+#pragma once
+
+#include "fwd.h"
+#include "value.h"
+
+#include <library/cpp/json/json_value.h>
+
+#include <util/generic/hash.h>
+#include <util/generic/ptr.h>
+#include <util/generic/deque.h>
+#include <util/system/type_name.h>
+#include <util/generic/vector.h>
+#include <util/generic/yexception.h>
+#include <util/generic/bt_exception.h>
+#include <util/ysaveload.h>
+
+class IInputStream;
+class IOutputStream;
+
+namespace NConfig {
+ typedef THashMap<TString, NJson::TJsonValue> TGlobals;
+
+ class TConfigError: public TWithBackTrace<yexception> {
+ };
+
+ class TConfigParseError: public TConfigError {
+ };
+
+ class TTypeMismatch: public TConfigError {
+ };
+
+ struct TArray;
+ struct TDict;
+
+ class TConfig {
+ public:
+ inline TConfig()
+ : V_(Null())
+ {
+ }
+
+ inline TConfig(IValue* v)
+ : V_(v)
+ {
+ }
+
+ TConfig(const TConfig& config) = default;
+ TConfig& operator=(const TConfig& config) = default;
+
+ template <class T>
+ inline bool IsA() const {
+ return V_->IsA(typeid(T));
+ }
+
+ inline bool IsNumeric() const {
+ return IsA<double>() || IsA<i64>() || IsA<ui64>();
+ }
+
+ template <class T>
+ inline const T& Get() const {
+ return GetNonConstant<T>();
+ }
+
+ template <class T>
+ inline T& GetNonConstant() const {
+ if (this->IsA<T>()) {
+ return *(T*)V_->Ptr();
+ }
+
+ if constexpr (std::is_same_v<T, ::NConfig::TArray>) {
+ NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "array");
+ } else if constexpr (std::is_same_v<T, ::NConfig::TDict>) {
+ NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "dict");
+ } else if constexpr (std::is_same_v<T, TString>) {
+ NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "string");
+ } else {
+ NCfgPrivate::ReportTypeMismatch(V_->TypeName(), ::TypeName<T>());
+ }
+ }
+
+ template <class T>
+ inline T As() const {
+ return ValueAs<T>(V_.Get());
+ }
+
+ template <class T>
+ inline T As(T def) const {
+ return IsNull() ? def : As<T>();
+ }
+
+ inline bool IsNull() const noexcept {
+ return V_.Get() == Null();
+ }
+
+ const TConfig& Or(const TConfig& r) const {
+ return IsNull() ? r : *this;
+ }
+
+ //assume value is dict
+ bool Has(const TStringBuf& key) const;
+ const TConfig& operator[](const TStringBuf& key) const;
+ const TConfig& At(const TStringBuf& key) const;
+
+ //assume value is array
+ const TConfig& operator[](size_t index) const;
+ size_t GetArraySize() const;
+
+ static TConfig FromIni(IInputStream& in, const TGlobals& g = TGlobals());
+ static TConfig FromJson(IInputStream& in, const TGlobals& g = TGlobals());
+ static TConfig FromLua(IInputStream& in, const TGlobals& g = TGlobals());
+ //load yconf format. unsafe, but natural mapping
+ static TConfig FromMarkup(IInputStream& in, const TGlobals& g = TGlobals());
+
+ static TConfig FromStream(IInputStream& in, const TGlobals& g = TGlobals());
+
+ inline void ToJson(IOutputStream& out) const {
+ V_->ToJson(out);
+ }
+
+ void DumpJson(IOutputStream& out) const;
+ void DumpLua(IOutputStream& out) const;
+
+ static TConfig ReadJson(TStringBuf in, const TGlobals& g = TGlobals());
+ static TConfig ReadLua(TStringBuf in, const TGlobals& g = TGlobals());
+ static TConfig ReadMarkup(TStringBuf in, const TGlobals& g = TGlobals());
+ static TConfig ReadIni(TStringBuf in, const TGlobals& g = TGlobals());
+
+ void Load(IInputStream* stream);
+ void Save(IOutputStream* stream) const;
+
+ private:
+ TIntrusivePtr<IValue> V_;
+ };
+
+ struct TArray: public TDeque<TConfig> {
+ const TConfig& Index(size_t index) const;
+ const TConfig& At(size_t index) const;
+ };
+
+ struct TDict: public THashMap<TString, TConfig> {
+ const TConfig& Find(const TStringBuf& key) const;
+ const TConfig& At(const TStringBuf& key) const;
+ };
+
+ THolder<IInputStream> CreatePreprocessor(const TGlobals& g, IInputStream& in);
+}
diff --git a/library/cpp/config/domscheme.cpp b/library/cpp/config/domscheme.cpp
new file mode 100644
index 0000000000..95f3dc8278
--- /dev/null
+++ b/library/cpp/config/domscheme.cpp
@@ -0,0 +1 @@
+#include "domscheme.h"
diff --git a/library/cpp/config/domscheme.h b/library/cpp/config/domscheme.h
new file mode 100644
index 0000000000..613850be01
--- /dev/null
+++ b/library/cpp/config/domscheme.h
@@ -0,0 +1,155 @@
+#pragma once
+
+#include "config.h"
+
+#include <util/generic/algorithm.h>
+#include <util/generic/typetraits.h>
+#include <util/stream/str.h>
+
+struct TConfigTraits {
+ using TValue = NConfig::TConfig;
+ using TValueRef = const TValue*;
+ using TConstValueRef = TValueRef;
+ using TStringType = TString;
+
+ // anyvalue defaults
+ template <class T>
+ static inline TValue Value(const T& t) {
+ return TValue(NConfig::ConstructValue(t));
+ }
+
+ template <class T>
+ static inline TValue Value(std::initializer_list<T> list) {
+ NConfig::TArray result;
+ for (const auto& t : list) {
+ result.push_back(TValue(NConfig::ConstructValue(t)));
+ }
+ return TValue(NConfig::ConstructValue(std::move(result)));
+ }
+
+ static inline TConstValueRef Ref(const TValue& v) {
+ return &v;
+ }
+
+ // common ops
+ static inline bool IsNull(TConstValueRef v) {
+ return v->IsNull();
+ }
+
+ static inline TString ToJson(TConstValueRef v) {
+ TStringStream str;
+ v->ToJson(str);
+ return str.Str();
+ }
+
+ // struct ops
+ static inline TConstValueRef GetField(TConstValueRef v, const TStringBuf& name) {
+ return &(*v)[name];
+ }
+
+ // array ops
+ static bool IsArray(TConstValueRef v) {
+ return v->IsA<NConfig::TArray>();
+ }
+
+ using TArrayIterator = size_t;
+
+ static inline TConstValueRef ArrayElement(TConstValueRef v, TArrayIterator n) {
+ return &(*v)[n];
+ }
+
+ static inline size_t ArraySize(TConstValueRef v) {
+ return v->GetArraySize();
+ }
+
+ static inline TArrayIterator ArrayBegin(TConstValueRef) {
+ return 0;
+ }
+
+ static inline TArrayIterator ArrayEnd(TConstValueRef v) {
+ return ArraySize(v);
+ }
+
+ // dict ops
+ static bool IsDict(TConstValueRef v) {
+ return v->IsA<NConfig::TDict>();
+ }
+
+ static inline TConstValueRef DictElement(TConstValueRef v, TStringBuf key) {
+ return &(*v)[key];
+ }
+
+ static inline size_t DictSize(TConstValueRef v) {
+ return v->Get<NConfig::TDict>().size();
+ }
+
+ using TDictIterator = NConfig::TDict::const_iterator;
+
+ static inline TDictIterator DictBegin(TConstValueRef v) {
+ return v->Get<NConfig::TDict>().begin();
+ }
+
+ static inline TDictIterator DictEnd(TConstValueRef v) {
+ return v->Get<NConfig::TDict>().end();
+ }
+
+ static inline TStringBuf DictIteratorKey(TConstValueRef /*dict*/, const TDictIterator& it) {
+ return it->first;
+ }
+
+ static inline TConstValueRef DictIteratorValue(TConstValueRef /*dict*/, const TDictIterator& it) {
+ return &it->second;
+ }
+
+ // generic get
+ template <typename T>
+ static inline void Get(TConstValueRef v, T def, T& t) {
+ t = v->As<T>(def);
+ }
+
+ static inline bool Get(TConstValueRef v, double def, double& t) {
+ if (v->IsNumeric()) {
+ t = v->As<double>(def);
+ return true;
+ }
+ t = def;
+ return false;
+ }
+
+ template <typename T>
+ static inline void Get(TConstValueRef v, T& t) {
+ t = v->As<T>();
+ }
+
+ template <typename T>
+ static inline bool IsValidPrimitive(const T&, TConstValueRef v) {
+ if (v->IsNull()) {
+ return true;
+ }
+
+ try {
+ v->As<T>();
+
+ return true;
+ } catch (const NConfig::TTypeMismatch&) {
+ } catch (const TBadCastException&) {
+ }
+
+ return false;
+ }
+
+ template <class T>
+ static inline void Set(TValueRef v, T&& t) {
+ v->GetNonConstant<std::remove_const_t<std::remove_reference_t<T>>>() = t;
+ }
+
+ // validation ops
+ static inline TVector<TString> GetKeys(TConstValueRef v) {
+ TVector<TString> res;
+ for (const auto& it : v->Get<NConfig::TDict>()) {
+ res.push_back(it.first);
+ }
+ Sort(res.begin(), res.end());
+ return res;
+ }
+};
diff --git a/library/cpp/config/fwd.h b/library/cpp/config/fwd.h
new file mode 100644
index 0000000000..07caafe661
--- /dev/null
+++ b/library/cpp/config/fwd.h
@@ -0,0 +1,5 @@
+#pragma once
+
+namespace NConfig {
+ class TConfig;
+}
diff --git a/library/cpp/config/ini.cpp b/library/cpp/config/ini.cpp
new file mode 100644
index 0000000000..6c1897dd27
--- /dev/null
+++ b/library/cpp/config/ini.cpp
@@ -0,0 +1,62 @@
+#include "ini.h"
+
+#include <util/string/strip.h>
+#include <util/stream/input.h>
+
+using namespace NConfig;
+
+namespace {
+ inline TStringBuf StripComment(TStringBuf line) {
+ return line.Before('#').Before(';');
+ }
+}
+
+TConfig NConfig::ParseIni(IInputStream& in) {
+ TConfig ret = ConstructValue(TDict());
+
+ {
+ TConfig* cur = &ret;
+ TString line;
+
+ while (in.ReadLine(line)) {
+ TStringBuf tmp = StripComment(line);
+ TStringBuf stmp = StripString(tmp);
+
+ if (stmp.empty()) {
+ continue;
+ }
+
+ if (stmp[0] == '[') {
+ //start section
+ if (*(stmp.end() - 1) != ']') {
+ ythrow TConfigParseError() << "malformed section " << stmp;
+ }
+
+ stmp = TStringBuf(stmp.data() + 1, stmp.end() - 1);
+ cur = &ret;
+
+ while (!!stmp) {
+ TStringBuf section;
+
+ stmp.Split('.', section, stmp);
+
+ cur = &cur->GetNonConstant<TDict>()[section];
+ if (!cur->IsA<TDict>()) {
+ *cur = ConstructValue(TDict());
+ }
+ }
+ } else {
+ //value
+ TStringBuf key, value;
+
+ tmp.Split('=', key, value);
+
+ auto& dict = cur->GetNonConstant<TDict>();
+ auto strippedValue = TString(StripString(value));
+ dict[StripString(key)] = ConstructValue(strippedValue);
+ }
+ }
+ }
+
+ return ret;
+}
diff --git a/library/cpp/config/ini.h b/library/cpp/config/ini.h
new file mode 100644
index 0000000000..e5c9567224
--- /dev/null
+++ b/library/cpp/config/ini.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "config.h"
+
+namespace NConfig {
+ TConfig ParseIni(IInputStream& in);
+}
diff --git a/library/cpp/config/markup.cpp b/library/cpp/config/markup.cpp
new file mode 100644
index 0000000000..df072c6529
--- /dev/null
+++ b/library/cpp/config/markup.cpp
@@ -0,0 +1,258 @@
+#include "markup.h"
+
+#include <util/stream/output.h>
+#include <util/stream/mem.h>
+#include <util/string/strip.h>
+#include <util/string/cast.h>
+
+using namespace NConfig;
+
+#define DBG(x)
+
+namespace {
+#define MACHINE_DATA
+#include "markupfsm.h"
+#undef MACHINE_DATA
+
+ class IXmlCB {
+ public:
+ inline void DoTagOpen(const TStringBuf& key) {
+ DBG(Cerr << "topen" << key << Endl);
+
+ S_.push_back(key);
+ OnTagOpen(key);
+ }
+
+ inline void DoTagClose(const TStringBuf& key) {
+ DBG(Cerr << "tclose" << key << Endl);
+
+ if (S_.empty()) {
+ ythrow yexception() << "unbalanced tag";
+ }
+
+ if (S_.back() != key) {
+ ythrow yexception() << "unbalanced tag";
+ }
+
+ S_.pop_back();
+ OnTagClose();
+ }
+
+ inline void DoText(const TStringBuf& text) {
+ DBG(Cerr << "ttext" << text << Endl);
+
+ if (!!text) {
+ OnText(text);
+ }
+ }
+
+ inline void DoAttrKey(const TStringBuf& key) {
+ DBG(Cerr << "tattrkey" << key << Endl);
+
+ A_ = key;
+ }
+
+ inline void DoAttrValue(const TStringBuf& key) {
+ DBG(Cerr << "tattrvalue" << key << Endl);
+
+ if (!A_) {
+ ythrow yexception() << "dangling attribute";
+ }
+
+ OnAttr(A_, key);
+ A_ = TStringBuf();
+ }
+
+ virtual void OnTagOpen(const TStringBuf& key) = 0;
+ virtual void OnTagClose() = 0;
+ virtual void OnText(const TStringBuf& text) = 0;
+ virtual void OnAttr(const TStringBuf& key, const TStringBuf& value) = 0;
+ virtual ~IXmlCB() = default;
+
+ private:
+ TVector<TStringBuf> S_;
+ TStringBuf A_;
+ };
+
+ inline void Parse(TStringBuf s, IXmlCB* cb) {
+ const char* p = s.data();
+ const char* pe = s.end();
+ const char* eof = pe;
+ const char* l = p;
+
+ int cs;
+ TString cur;
+
+#define MACHINE_INIT
+#include "markupfsm.h"
+#undef MACHINE_INIT
+
+#define MACHINE_EXEC
+#include "markupfsm.h"
+#undef MACHINE_EXEC
+
+ if (cs < ParseXml_first_final) {
+ ythrow TConfigParseError() << "can not parse markup data at offset " << (p - s.data());
+ }
+ }
+
+ inline IValue* SmartValue(const TStringBuf& v) {
+ try {
+ return ConstructValue(FromString<ui64>(v));
+ } catch (...) {
+ }
+
+ try {
+ return ConstructValue(FromString<i64>(v));
+ } catch (...) {
+ }
+
+ try {
+ return ConstructValue(FromString<double>(v));
+ } catch (...) {
+ }
+
+ try {
+ return ConstructValue(FromString<bool>(v));
+ } catch (...) {
+ }
+
+ return ConstructValue(ToString(v));
+ }
+
+ inline TConfig Parse(TStringBuf s0) {
+ struct TXmlParser: public IXmlCB {
+ inline TXmlParser()
+ : Root(ConstructValue(TDict()))
+ {
+ S.push_back(&Root);
+ }
+
+ void OnTagOpen(const TStringBuf& key) override {
+ *Push(key) = ConstructValue(TDict());
+ }
+
+ void OnTagClose() override {
+ S.pop_back();
+ }
+
+ static inline bool IsWS(char ch) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case ':':
+ return true;
+ }
+
+ return false;
+ }
+
+ void OnText(const TStringBuf& text) override {
+ TMemoryInput mi(text.data(), text.size());
+ TString line;
+
+ while (mi.ReadLine(line)) {
+ DBG(Cerr << line << Endl);
+
+ TStringBuf s = StripString(TStringBuf(line));
+
+ DBG(Cerr << s << Endl);
+
+ if (!s) {
+ continue;
+ }
+
+ const char* b = s.data();
+ const char* c = b;
+ const char* e = s.end();
+
+ while (c < e && !IsWS(*c)) {
+ ++c;
+ }
+
+ const TStringBuf key(b, c);
+
+ while (c < e && IsWS(*c)) {
+ ++c;
+ }
+
+ const TStringBuf value(c, e);
+
+ if (!key) {
+ continue;
+ }
+
+ DBG(Cerr << key << " " << value << Endl);
+
+ SetAttr(key, value);
+ }
+ }
+
+ void OnAttr(const TStringBuf& key, const TStringBuf& value) override {
+ SetAttr(key, value);
+ }
+
+ inline void SetAttr(const TStringBuf& key, const TStringBuf& value) {
+ Dict()[ToString(key)] = SmartValue(value);
+ }
+
+ inline TConfig* Top() {
+ return S.back();
+ }
+
+ inline TConfig* Push(const TStringBuf& key) {
+ TDict& d = Dict();
+ const TString k = ToString(key);
+
+ if (d.find(k) == d.end()) {
+ S.push_back(&d[k]);
+ } else {
+ TConfig tmp = d[k];
+
+ if (tmp.IsA<TArray>()) {
+ TArray& arr = d[k].GetNonConstant<TArray>();
+
+ arr.push_back(TConfig());
+
+ S.push_back(&arr.back());
+ } else {
+ d[k] = ConstructValue(TArray());
+
+ TArray& arr = d[k].GetNonConstant<TArray>();
+
+ arr.push_back(tmp);
+ arr.push_back(TConfig());
+
+ S.push_back(&arr.back());
+ }
+ }
+
+ return Top();
+ }
+
+ inline TDict& Dict() {
+ try {
+ return Top()->GetNonConstant<TDict>();
+ } catch (...) {
+ }
+
+ return Top()->Get<TArray>().back().GetNonConstant<TDict>();
+ }
+
+ TConfig Root;
+ TVector<TConfig*> S;
+ };
+
+ TXmlParser cb;
+
+ Parse(s0, &cb);
+
+ return cb.Root;
+ }
+}
+
+TConfig NConfig::ParseRawMarkup(IInputStream& in) {
+ return Parse(in.ReadAll());
+}
diff --git a/library/cpp/config/markup.h b/library/cpp/config/markup.h
new file mode 100644
index 0000000000..88e1207b30
--- /dev/null
+++ b/library/cpp/config/markup.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "config.h"
+
+namespace NConfig {
+ TConfig ParseRawMarkup(IInputStream& in);
+}
diff --git a/library/cpp/config/markupfsm.h.rl6 b/library/cpp/config/markupfsm.h.rl6
new file mode 100644
index 0000000000..b709faaebf
--- /dev/null
+++ b/library/cpp/config/markupfsm.h.rl6
@@ -0,0 +1,81 @@
+%%{
+ machine ParseXml;
+
+ action startText {
+ l = p;
+ }
+
+ action endText {
+ cb->DoText(TStringBuf(l + 1, p));
+ }
+
+ action startSTag {
+ l = p;
+ }
+
+ action endSTag {
+ cb->DoTagOpen(TStringBuf(l, p));
+ }
+
+ action startETag {
+ l = p;
+ }
+
+ action endETag {
+ cb->DoTagClose(TStringBuf(l, p));
+ }
+
+ action startKey {
+ l = p;
+ }
+
+ action endKey {
+ cb->DoAttrKey(TStringBuf(l, p));
+ }
+
+ action startValue {
+ l = p;
+ }
+
+ action endValue {
+ cb->DoAttrValue(TStringBuf(l, p));
+ }
+
+ spacesym = [ \r\n\t];
+ gsym = (spacesym | '<' | '>' | '/');
+ sym = any -- gsym;
+ asym = sym -- ('=' | '"');
+ tag = sym+;
+ xattrkey = asym+;
+ xattrval = asym*;
+ xspace = spacesym*;
+ mspace = spacesym+;
+ attr = (xattrkey >startKey %endKey) '=' '"' (xattrval >startValue %endValue) '"';
+ attrs = (mspace attr)*;
+ text = ('>' (any -- '<')*) >startText %endText;
+ stag = '<' xspace (tag >startSTag %endSTag) attrs xspace;
+ etag = '<' xspace '/' xspace (tag >startETag %endETag) xspace;
+
+ main := spacesym* ((stag | etag) text)*;
+}%%
+
+#if defined(MACHINE_DATA)
+#undef MACHINE_DATA
+%%{
+ write data;
+}%%
+#endif
+
+#if defined(MACHINE_INIT)
+#undef MACHINE_INIT
+%%{
+ write init;
+}%%
+#endif
+
+#if defined(MACHINE_EXEC)
+#undef MACHINE_EXEC
+%%{
+ write exec;
+}%%
+#endif
diff --git a/library/cpp/config/sax.cpp b/library/cpp/config/sax.cpp
new file mode 100644
index 0000000000..7837fe97b5
--- /dev/null
+++ b/library/cpp/config/sax.cpp
@@ -0,0 +1,67 @@
+#include "sax.h"
+
+using namespace NConfig;
+
+namespace {
+ class TSax: public IConfig, public IConfig::IValue {
+ public:
+ inline TSax(const TConfig& cfg)
+ : C_(cfg)
+ {
+ }
+
+ void DoForEach(IFunc* func) override {
+ if (C_.IsA<TArray>()) {
+ const TArray& a = C_.Get<TArray>();
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ TSax slave(a[i]);
+
+ func->Consume(ToString(i), &slave);
+ }
+ } else if (C_.IsA<TDict>()) {
+ const TDict& d = C_.Get<TDict>();
+
+ for (const auto& it : d) {
+ TSax slave(it.second);
+
+ func->Consume(it.first, &slave);
+ }
+ }
+ }
+
+ TString AsString() override {
+ if (C_.IsA<TArray>()) {
+ TSax slave(C_.Get<TArray>()[0]);
+ return slave.AsString();
+ }
+ return C_.As<TString>();
+ }
+
+ bool AsBool() override {
+ return C_.As<bool>();
+ }
+
+ IConfig* AsSubConfig() override {
+ return this;
+ }
+ bool IsContainer() const override {
+ return C_.IsA<TArray>() || C_.IsA<TDict>();
+ }
+ void DumpJson(IOutputStream& stream) const override {
+ C_.DumpJson(stream);
+ }
+ void DumpLua(IOutputStream& stream) const override {
+ C_.DumpLua(stream);
+ }
+
+ private:
+ TConfig C_;
+ };
+}
+
+namespace NConfig {
+ THolder<IConfig> ConfigParser(IInputStream& in, const TGlobals& globals) {
+ return MakeHolder<TSax>(TConfig::FromStream(in, globals));
+ }
+}
diff --git a/library/cpp/config/sax.h b/library/cpp/config/sax.h
new file mode 100644
index 0000000000..8ce252c9a7
--- /dev/null
+++ b/library/cpp/config/sax.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "config.h"
+
+#include <util/string/cast.h>
+#include <util/generic/maybe.h>
+#include <util/generic/string.h>
+#include <util/generic/vector.h>
+#include <util/generic/yexception.h>
+
+class IInputStream;
+
+namespace NConfig {
+ class IConfig {
+ public:
+ class IValue {
+ public:
+ virtual TString AsString() = 0;
+ virtual bool AsBool() = 0;
+ virtual IConfig* AsSubConfig() = 0;
+
+ virtual bool IsContainer() const = 0;
+ };
+
+ class IFunc {
+ public:
+ inline void Consume(const TString& key, IValue* value) {
+ DoConsume(key, value);
+ }
+
+ virtual ~IFunc() = default;
+
+ private:
+ virtual void DoConsume(const TString& key, IValue* value) {
+ (void)key;
+ (void)value;
+ }
+ };
+
+ virtual ~IConfig() = default;
+
+ inline void ForEach(IFunc* func) {
+ DoForEach(func);
+ }
+ virtual void DumpJson(IOutputStream& stream) const = 0;
+ virtual void DumpLua(IOutputStream& stream) const = 0;
+
+ private:
+ virtual void DoForEach(IFunc* func) = 0;
+ };
+
+ template <class T>
+ static inline bool ParseFromString(const TString& s, TMaybe<T>& t) {
+ t.ConstructInPlace(FromString<T>(s));
+
+ return true;
+ }
+
+ template <class T>
+ static inline bool ParseFromString(const TString& s, THolder<T>& t) {
+ t = MakeHolder<T>(FromString<T>(s));
+
+ return true;
+ }
+
+ template <class T>
+ static inline bool ParseFromString(const TString& s, T& t) {
+ t = FromString<T>(s);
+
+ return true;
+ }
+
+ THolder<IConfig> ConfigParser(IInputStream& in, const TGlobals& globals = TGlobals());
+}
+
+#define START_PARSE \
+ void DoConsume(const TString& key, NConfig::IConfig::IValue* value) override { \
+ (void)key; \
+ (void)value;
+#define END_PARSE \
+ ythrow NConfig::TConfigParseError() << "unsupported key(" << key.Quote() << ")"; \
+ }
+#define ON_KEY(k, v) if (key == k && NConfig::ParseFromString(value->AsString(), v))
diff --git a/library/cpp/config/support/pp.lua b/library/cpp/config/support/pp.lua
new file mode 100644
index 0000000000..d6a85653a2
--- /dev/null
+++ b/library/cpp/config/support/pp.lua
@@ -0,0 +1,63 @@
+local function off(i)
+ local ss = ''
+ local j = 0
+
+ while j < i do
+ ss = ss .. ' '
+ j = j + 1
+ end
+
+ return ss
+end
+
+local function fmtkey(key)
+ if type(key) == 'string' and key:match('^[_%a][_%w]*$') then
+ return key
+ end
+
+ return '[' .. string.format('%q', key) .. ']'
+end
+
+local function pp(v, i)
+ if type(v) == "string" then
+ return string.format('%q', v)
+ end
+
+ if type(v) == "number" then
+ return tostring(v)
+ end
+
+ if type(v) == "nil" then
+ return 'nil'
+ end
+
+ if type(v) == "boolean" then
+ return tostring(v)
+ end
+
+ if type(v) == "table" then
+ local ret = "{\n"
+ local curoff = 1
+
+ for x, y in pairs(v) do
+ if type(x) == 'number' and x == curoff then
+ ret = ret .. off(i + 1) .. pp(y, i + 1) .. ';\n'
+ curoff = curoff + 1
+ else
+ ret = ret .. off(i + 1) .. fmtkey(x) .. " = " .. pp(y, i + 1) .. ';\n'
+ end
+ end
+
+ return ret .. off(i) .. "}"
+ end
+
+ if v == nil then
+ return 'nil'
+ end
+
+ return ""
+end
+
+local function prettify(v)
+ return pp(v, 0)
+end
diff --git a/library/cpp/config/value.cpp b/library/cpp/config/value.cpp
new file mode 100644
index 0000000000..776cd2b66c
--- /dev/null
+++ b/library/cpp/config/value.cpp
@@ -0,0 +1,292 @@
+#include "value.h"
+#include "config.h"
+
+#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h>
+
+#include <util/generic/algorithm.h>
+#include <util/system/type_name.h>
+#include <util/generic/singleton.h>
+#include <util/string/cast.h>
+#include <util/string/strip.h>
+#include <util/string/type.h>
+
+using namespace NConfig;
+
+namespace {
+ template <class T>
+ class TValue: public IValue {
+ public:
+ inline TValue(const T& t)
+ : T_(t)
+ {
+ }
+
+ bool IsA(const std::type_info& info) const override {
+ return info == typeid(T);
+ }
+
+ TString TypeName() const override {
+ return ::TypeName<T>();
+ }
+
+ void* Ptr() const override {
+ return (void*)&T_;
+ }
+
+ void ToJson(IOutputStream& out) const override {
+ out << AsString();
+ }
+
+ bool AsBool() const override {
+ return (bool)AsDouble();
+ }
+
+ protected:
+ T T_;
+ };
+
+ class TNullValue: public TValue<TNull> {
+ public:
+ inline TNullValue()
+ : TValue<TNull>(TNull())
+ {
+ Ref();
+ }
+
+ double AsDouble() const override {
+ return 0;
+ }
+
+ ui64 AsUInt() const override {
+ return 0;
+ }
+
+ i64 AsInt() const override {
+ return 0;
+ }
+
+ TString AsString() const override {
+ return TString();
+ }
+
+ void ToJson(IOutputStream& out) const override {
+ out << "null";
+ }
+
+ TString TypeName() const override {
+ return "null";
+ }
+ };
+
+ template <class T>
+ class TNumericValue: public TValue<T> {
+ public:
+ inline TNumericValue(const T& t)
+ : TValue<T>(t)
+ {
+ }
+
+ double AsDouble() const override {
+ return this->T_;
+ }
+
+ ui64 AsUInt() const override {
+ return this->T_;
+ }
+
+ i64 AsInt() const override {
+ return this->T_;
+ }
+ };
+
+ class TBoolValue: public TNumericValue<bool> {
+ public:
+ inline TBoolValue(bool v)
+ : TNumericValue<bool>(v)
+ {
+ }
+
+ TString AsString() const override {
+ return T_ ? "true" : "false";
+ }
+ };
+
+ template <class T>
+ class TArithmeticValue: public TNumericValue<T> {
+ public:
+ inline TArithmeticValue(T v)
+ : TNumericValue<T>(v)
+ {
+ }
+
+ TString AsString() const override {
+ return ToString(this->T_);
+ }
+ };
+
+ class TStringValue: public TValue<TString> {
+ public:
+ inline TStringValue(const TString& v)
+ : TValue<TString>(v)
+ {
+ }
+
+ template <class T>
+ inline T AsT() const {
+ const TStringBuf s = StripString(TStringBuf(T_));
+
+ if (IsTrue(s)) {
+ return true;
+ }
+
+ if (IsFalse(s)) {
+ return false;
+ }
+
+ return FromString<T>(s);
+ }
+
+ double AsDouble() const override {
+ return AsT<double>();
+ }
+
+ ui64 AsUInt() const override {
+ return AsT<ui64>();
+ }
+
+ i64 AsInt() const override {
+ return AsT<i64>();
+ }
+
+ TString AsString() const override {
+ return T_;
+ }
+
+ void ToJson(IOutputStream& out) const override {
+ NEscJ::EscapeJ<true, true>(T_, out);
+ }
+
+ TString TypeName() const override {
+ return "string";
+ }
+ };
+
+ template <class T>
+ class TContainer: public TValue<T> {
+ public:
+ inline TContainer(const T& t)
+ : TValue<T>(t)
+ {
+ }
+
+ double AsDouble() const override {
+ NCfgPrivate::ReportTypeMismatch(this->TypeName(), "double");
+ }
+
+ ui64 AsUInt() const override {
+ NCfgPrivate::ReportTypeMismatch(this->TypeName(), "uint");
+ }
+
+ i64 AsInt() const override {
+ NCfgPrivate::ReportTypeMismatch(this->TypeName(), "int");
+ }
+
+ bool AsBool() const override {
+ NCfgPrivate::ReportTypeMismatch(this->TypeName(), "bool");
+ }
+
+ TString AsString() const override {
+ NCfgPrivate::ReportTypeMismatch(this->TypeName(), "string");
+ }
+ };
+
+ class TArrayValue: public TContainer<TArray> {
+ public:
+ inline TArrayValue(const TArray& v)
+ : TContainer<TArray>(v)
+ {
+ }
+
+ void ToJson(IOutputStream& s) const override {
+ s << "[";
+
+ for (TArray::const_iterator it = T_.begin(); it != T_.end(); ++it) {
+ if (it != T_.begin()) {
+ s << ",";
+ }
+
+ it->ToJson(s);
+ }
+
+ s << "]";
+ }
+
+ TString TypeName() const override {
+ return "array";
+ }
+ };
+
+ class TDictValue: public TContainer<TDict> {
+ public:
+ inline TDictValue(const TDict& v)
+ : TContainer<TDict>(v)
+ {
+ }
+
+ void ToJson(IOutputStream& s) const override {
+ s << "{";
+
+ TVector<TStringBuf> buf;
+ buf.reserve(T_.size());
+ for (const auto& t : T_) {
+ buf.push_back(t.first);
+ }
+ Sort(buf.begin(), buf.end());
+ for (TVector<TStringBuf>::const_iterator kit = buf.begin(); kit != buf.end(); ++kit) {
+ TStringBuf key = *kit;
+ TDict::const_iterator it = T_.find(key);
+
+ if (kit != buf.begin()) {
+ s << ",";
+ }
+
+ NEscJ::EscapeJ<true, true>(key, s);
+
+ s << ":";
+
+ it->second.ToJson(s);
+ }
+
+ s << "}";
+ }
+
+ TString TypeName() const override {
+ return "dict";
+ }
+ };
+}
+
+#define DECLARE(type1, type2) \
+ IValue* ConstructValueImpl(const type2& t) { \
+ return new type1(t); \
+ }
+
+namespace NConfig {
+ namespace NCfgPrivate {
+ DECLARE(TBoolValue, bool)
+ DECLARE(TArithmeticValue<double>, double)
+ DECLARE(TArithmeticValue<i64>, i64)
+ DECLARE(TArithmeticValue<ui64>, ui64)
+ DECLARE(TStringValue, TString)
+ DECLARE(TArrayValue, TArray)
+ DECLARE(TDictValue, TDict)
+ }
+
+ IValue* Null() {
+ return Singleton<TNullValue>();
+ }
+
+ [[noreturn]] void NCfgPrivate::ReportTypeMismatch(TStringBuf realType, TStringBuf expectedType) {
+ ythrow TTypeMismatch() << "type mismatch (real: " << realType << ", expected: " << expectedType << ')';
+ }
+}
diff --git a/library/cpp/config/value.h b/library/cpp/config/value.h
new file mode 100644
index 0000000000..bfd1e2f8c3
--- /dev/null
+++ b/library/cpp/config/value.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <typeinfo>
+
+#include <util/generic/ptr.h>
+#include <util/generic/cast.h>
+#include <util/generic/string.h>
+#include <util/generic/typetraits.h>
+
+class IOutputStream;
+
+namespace NConfig {
+ class IValue: public TAtomicRefCount<IValue> {
+ public:
+ virtual ~IValue() = default;
+
+ virtual bool IsA(const std::type_info& info) const = 0;
+ virtual TString TypeName() const = 0;
+ virtual void* Ptr() const = 0;
+
+ virtual ui64 AsUInt() const = 0;
+ virtual i64 AsInt() const = 0;
+ virtual double AsDouble() const = 0;
+ virtual bool AsBool() const = 0;
+ virtual TString AsString() const = 0;
+
+ virtual void ToJson(IOutputStream& out) const = 0;
+ };
+
+ namespace NCfgPrivate {
+ struct TDummy {
+ };
+
+ template <class T>
+ inline IValue* ConstructValueImpl(const T& t, ...) {
+ extern IValue* ConstructValueImpl(const T& t);
+
+ return ConstructValueImpl(t);
+ }
+
+ template <class T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
+ inline IValue* ConstructValueImpl(const T& t, TDummy) {
+ extern IValue* ConstructValueImpl(const double& t);
+
+ return ConstructValueImpl(t);
+ }
+
+ template <class T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
+ inline IValue* ConstructValueImpl(const T& t, TDummy) {
+ typedef std::conditional_t<std::is_signed<T>::value, i64, ui64> Type;
+ extern IValue* ConstructValueImpl(const Type& t);
+
+ return ConstructValueImpl(t);
+ }
+
+ template <class T, std::enable_if_t<std::is_convertible<T, TString>::value>* = nullptr>
+ inline IValue* ConstructValueImpl(const T& t, TDummy) {
+ extern IValue* ConstructValueImpl(const TString& t);
+
+ return ConstructValueImpl(t);
+ }
+
+ inline IValue* ConstructValueImpl(const bool& t, TDummy) {
+ extern IValue* ConstructValueImpl(const bool& t);
+
+ return ConstructValueImpl(t);
+ }
+ };
+
+ template <class T>
+ inline IValue* ConstructValue(const T& t) {
+ return NCfgPrivate::ConstructValueImpl(t, NCfgPrivate::TDummy());
+ }
+
+ IValue* Null();
+
+ namespace NCfgPrivate {
+ template <bool Unsigned>
+ struct TSelector {
+ static inline ui64 Cvt(const IValue* v) {
+ return v->AsUInt();
+ }
+ };
+
+ template <>
+ struct TSelector<false> {
+ static inline i64 Cvt(const IValue* v) {
+ return v->AsInt();
+ }
+ };
+
+ [[noreturn]] void ReportTypeMismatch(TStringBuf realType, TStringBuf expectedType);
+ }
+
+ template <class T>
+ inline T ValueAs(const IValue* val) {
+ typedef NCfgPrivate::TSelector<std::is_unsigned<T>::value> TCvt;
+
+ return SafeIntegerCast<T>(TCvt::Cvt(val));
+ }
+
+ template <>
+ inline double ValueAs(const IValue* val) {
+ return val->AsDouble();
+ }
+
+ template <>
+ inline float ValueAs(const IValue* val) {
+ return (float)val->AsDouble();
+ }
+
+ template <>
+ inline bool ValueAs(const IValue* val) {
+ return val->AsBool();
+ }
+
+ template <>
+ inline TString ValueAs(const IValue* val) {
+ return val->AsString();
+ }
+}