summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjansenin <[email protected]>2023-09-06 12:31:15 +0300
committerjansenin <[email protected]>2023-09-06 13:04:39 +0300
commite369652514d1de8e1b6b361631158a95385f11dd (patch)
treea13bfbad4bf7c1e9b132e0ff9c32a496fc9b2b30
parent04a19cc6b675d2380241eaa4b6e584a07bbd4280 (diff)
add opaque map checks support, cast from typed NodeWrapper to untyped
-rw-r--r--ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp75
-rw-r--r--ydb/library/yaml_config/validator/validator_checks.cpp182
-rw-r--r--ydb/library/yaml_config/validator/validator_checks.h49
3 files changed, 270 insertions, 36 deletions
diff --git a/ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp b/ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp
index ae4fa3ffe73..cd269f706ce 100644
--- a/ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp
+++ b/ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp
@@ -306,4 +306,79 @@ Y_UNIT_TEST_SUITE(Checks) {
{"/", "Check \"must have even a/b/c if flag is true. Else must have odd c/a/b\" failed: Node \"/c/b/a\" is not presented"},
}));
}
+
+ Y_UNIT_TEST(OpaqueMaps) {
+ auto v =
+ TMapBuilder([](auto& b) {
+ b.Opaque()
+ .Int64("depth", [](auto& b) {
+ b.Range(0, 3);
+ });
+ })
+ .AddCheck("field/field/.../field level must equal /depth", [](auto& c) {
+ int remaining_depth = c.Node()["depth"].Int64();
+ int cur_depth = 0;
+ TNodeWrapper node = c.Node();
+ while (remaining_depth > 0) {
+ if (!node.IsMap()) {
+ c.Expect(false, "field \"field\" on level " + ToString<i64>(cur_depth) + " must be a map");
+ return;
+ }
+ node = node.Map()["field"];
+ remaining_depth--;
+ cur_depth++;
+ }
+ if (cur_depth == 0) {
+ c.Expect(!node.Map()["field"].Exists(), "If depth == 0, field \"field\" must not be there");
+ } else {
+ c.Expect(node.Exists(), "field \"field\" on level " + ToString<i64>(cur_depth) + " must exist");
+ return;
+ c.Expect(node.IsScalar(), "field \"field\" on level " + ToString<i64>(cur_depth) + " must be a scalar");
+ }
+ })
+ .CreateValidator();
+
+ const char* yaml =
+ "depth: 0\n"
+ "field: value\n";
+
+ UNIT_ASSERT(HasOnlyThisIssues(v.Validate(yaml), {{
+ "/", "Check \"field/field/.../field level must equal /depth\" failed: If depth == 0, field \"field\" must not be there"
+ }}));
+
+ yaml =
+ "depth: 1\n"
+ "field: value\n";
+
+ UNIT_ASSERT(Valid(v.Validate(yaml)));
+
+ yaml =
+ "depth: 1\n";
+
+ UNIT_ASSERT(HasOnlyThisIssues(v.Validate(yaml), {{
+ "/", "Check \"field/field/.../field level must equal /depth\" failed: field \"field\" on level 1 must exist"
+ }}));
+
+ yaml =
+ "depth: 2\n"
+ "field:\n"
+ " field: value\n";
+
+ UNIT_ASSERT(Valid(v.Validate(yaml)));
+
+ yaml =
+ "depth: 2\n"
+ "field:\n"
+ " field: []\n";
+
+ UNIT_ASSERT(Valid(v.Validate(yaml)));
+
+ yaml =
+ "depth: 2\n"
+ "field:\n"
+ " field:\n"
+ " field: value\n";
+
+ UNIT_ASSERT(Valid(v.Validate(yaml)));
+ }
}
diff --git a/ydb/library/yaml_config/validator/validator_checks.cpp b/ydb/library/yaml_config/validator/validator_checks.cpp
index 0224236d3a9..def0158aa7e 100644
--- a/ydb/library/yaml_config/validator/validator_checks.cpp
+++ b/ydb/library/yaml_config/validator/validator_checks.cpp
@@ -1,5 +1,6 @@
#include "validator_checks.h"
+#include "validator_builder.h"
#include "validator.h"
#include <util/string/cast.h>
@@ -19,6 +20,9 @@ TNodeWrapper::TNodeWrapper(
, PathFromCheckNode_(pathFromCheckNode) {}
TGenericNodeWrapper TNodeWrapper::Generic() {
+ // opaque nodes doesn't have validators and generic node type doesn't exist
+ Y_ASSERT(!IsOpaqueChild());
+
TGenericCheckContext* context = nullptr;
TGenericValidator* validator = nullptr;
@@ -41,7 +45,13 @@ TMapNodeWrapper TNodeWrapper::Map() {
TMapValidator* validator = nullptr;
if (Node_) {
- Y_ASSERT(NodeType_ == ENodeType::Map);
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(NodeType_ == ENodeType::Map);
+ } else {
+ if (Node_.Type() != NFyaml::ENodeType::Mapping) {
+ throw yexception() << "Must be a map";
+ }
+ }
context = static_cast<TMapCheckContext*>(Context_);
validator = static_cast<TMapValidator*>(Validator_);
@@ -59,7 +69,13 @@ TArrayNodeWrapper TNodeWrapper::Array() {
TArrayValidator* validator = nullptr;
if (Node_) {
- Y_ASSERT(NodeType_ == ENodeType::Array);
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(NodeType_ == ENodeType::Array);
+ } else {
+ if (Node_.Type() != NFyaml::ENodeType::Sequence) {
+ throw yexception() << "Must be an array";
+ }
+ }
context = static_cast<TArrayCheckContext*>(Context_);
validator = static_cast<TArrayValidator*>(Validator_);
@@ -77,7 +93,14 @@ TInt64NodeWrapper TNodeWrapper::Int64() {
TInt64Validator* validator = nullptr;
if (Node_) {
- Y_ASSERT(NodeType_ == ENodeType::Int64);
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(NodeType_ == ENodeType::Int64);
+ } else {
+ auto validationResult = TInt64Builder().CreateValidator().Validate(Node_);
+ if (!validationResult.Ok()) {
+ throw yexception() << validationResult;
+ }
+ }
context = static_cast<TInt64CheckContext*>(Context_);
validator = static_cast<TInt64Validator*>(Validator_);
@@ -95,7 +118,14 @@ TStringNodeWrapper TNodeWrapper::String() {
TStringValidator* validator = nullptr;
if (Node_) {
- Y_ASSERT(NodeType_ == ENodeType::String);
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(NodeType_ == ENodeType::String);
+ } else {
+ auto validationResult = TStringBuilder().CreateValidator().Validate(Node_);
+ if (!validationResult.Ok()) {
+ throw yexception() << validationResult;
+ }
+ }
context = static_cast<TStringCheckContext*>(Context_);
validator = static_cast<TStringValidator*>(Validator_);
@@ -113,7 +143,14 @@ TBoolNodeWrapper TNodeWrapper::Bool() {
TBoolValidator* validator = nullptr;
if (Node_) {
- Y_ASSERT(NodeType_ == ENodeType::Bool);
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(NodeType_ == ENodeType::Bool);
+ } else {
+ auto validationResult = TBoolBuilder().CreateValidator().Validate(Node_);
+ if (!validationResult.Ok()) {
+ throw yexception() << validationResult;
+ }
+ }
context = static_cast<TBoolCheckContext*>(Context_);
validator = static_cast<TBoolValidator*>(Validator_);
@@ -126,12 +163,41 @@ TBoolNodeWrapper TNodeWrapper::Bool() {
PathFromCheckNode_);
}
-ENodeType TNodeWrapper::Type() {
+TMaybe<ENodeType> TNodeWrapper::ValidatorType() {
+ ThrowIfNullNode();
+ return NodeType_;
+}
+
+bool TNodeWrapper::IsMap() {
+ ThrowIfNullNode();
+ return Node_.Type() == NFyaml::ENodeType::Mapping;
+}
+
+bool TNodeWrapper::IsArray() {
ThrowIfNullNode();
- Y_ASSERT(NodeType_.Defined());
- return NodeType_.GetRef();
+ return Node_.Type() == NFyaml::ENodeType::Sequence;
}
+bool TNodeWrapper::IsInt64() {
+ ThrowIfNullNode();
+ return TInt64Builder().CreateValidator().Validate(Node_).Ok();
+}
+
+bool TNodeWrapper::IsString() {
+ ThrowIfNullNode();
+ return TStringBuilder().CreateValidator().Validate(Node_).Ok();
+}
+
+bool TNodeWrapper::IsBool() {
+ ThrowIfNullNode();
+ return TBoolBuilder().CreateValidator().Validate(Node_).Ok();
+}
+
+bool TNodeWrapper::IsScalar() {
+ return IsInt64() || IsString() || IsBool();
+}
+
+
TGenericNodeWrapper::TGenericNodeWrapper(
TGenericCheckContext* context,
NFyaml::TNodeRef node,
@@ -146,6 +212,11 @@ TGenericNodeWrapper::TGenericNodeWrapper(
Y_UNUSED(Node_);
}
+TGenericNodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::Generic, PathFromCheckNode_);
+}
+
+
TMapNodeWrapper::TMapNodeWrapper(
TMapCheckContext* context,
NFyaml::TNodeRef node,
@@ -154,22 +225,30 @@ TMapNodeWrapper::TMapNodeWrapper(
: Context_(context)
, Node_(node)
, Validator_(validator)
- , PathFromCheckNode_(pathFromCheckNode) {}
+ , PathFromCheckNode_(pathFromCheckNode) {
+ if (Node_) {
+ Y_ASSERT(node.Type() == NFyaml::ENodeType::Mapping);
+ }
+ }
TNodeWrapper TMapNodeWrapper::operator[](const TString& field) {
NFyaml::TNodeRef node(nullptr);
TValidator* validator = nullptr;
TMaybe<ENodeType> nodeType;
- if (Node_ && Map().Has(field)) {
- node = Map().at(field);
- }
- if (Validator_ && Validator_->Children_.contains(field)) {
- validator = Validator_->Children_.at(field).Get();
- nodeType = validator->NodeType_;
+ if (Node_) {
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(Validator_);
+ if (Validator_->Children_.contains(field)) {
+ validator = Validator_->Children_.at(field).Get();
+ nodeType = validator->NodeType_;
+ }
+ }
+
+ if (Map().Has(field)) {
+ node = Map().at(field);
+ }
}
- // TODO: else must deduce node type here or later(in check for
- // example) for use with opaque maps
return TNodeWrapper(
Context_,
@@ -182,31 +261,44 @@ TNodeWrapper TMapNodeWrapper::operator[](const TString& field) {
TNodeWrapper TMapNodeWrapper::At(const TString& field) {
ThrowIfNullNode();
- if (!Validator_->Children_.contains(field) || !Map().Has(field)) {
+ if (!Map().Has(field)) {
TString nodePath = Context_->CheckNodePath_ + "/" + PathFromCheckNode_;
TString message = "Node \"" + nodePath + "\" is not presented";
- Context_->AddError(message);
throw yexception() << message;
}
- TValidator* validator = Validator_->Children_.at(field).Get();
+ TValidator* validator = nullptr;
+ TMaybe<ENodeType> nodeType;
+
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(Validator_);
+ if (Validator_->Children_.contains(field)) {
+ validator = Validator_->Children_.at(field).Get();
+ nodeType = validator->NodeType_;
+ }
+ }
+
return TNodeWrapper(
Context_,
- Map().at(field),
+ Map()[field],
validator,
- validator->NodeType_,
+ nodeType,
PathFromCheckNode_ + "/" + field);
}
bool TMapNodeWrapper::Has(const TString& field) {
- ThrowIfNullNode();
- return Map().Has(field);
+ return Node_ && Map().Has(field);
}
NFyaml::TMapping TMapNodeWrapper::Map() {
return Node_.Map();
}
+TMapNodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::Map, PathFromCheckNode_);
+}
+
+
TArrayNodeWrapper::TArrayNodeWrapper(
TArrayCheckContext* context,
NFyaml::TNodeRef node,
@@ -215,7 +307,9 @@ TArrayNodeWrapper::TArrayNodeWrapper(
: Context_(context)
, Node_(node)
, Validator_(validator)
- , PathFromCheckNode_(pathFromCheckNode) {}
+ , PathFromCheckNode_(pathFromCheckNode) {
+ Y_ASSERT(node.Type() == NFyaml::ENodeType::Sequence);
+ }
int TArrayNodeWrapper::Length() {
ThrowIfNullNode();
@@ -227,13 +321,18 @@ TNodeWrapper TArrayNodeWrapper::operator[](size_t index) {
TValidator* validator = nullptr;
TMaybe<ENodeType> nodeType;
- if (Node_ && Sequence().size() > index) {
- node = Sequence()[index];
- }
-
- if (Validator_) {
- validator = Validator_->ItemValidatorPtr_.Get();
- nodeType = validator->NodeType_;
+ if (Node_) {
+ if (!IsOpaqueChild()) {
+ Y_ASSERT(Validator_);
+ if (Validator_->ItemValidatorPtr_) {
+ validator = Validator_->ItemValidatorPtr_.Get();
+ nodeType = validator->NodeType_;
+ }
+ }
+
+ if (0 <= index && index < Sequence().size()) {
+ node = Sequence()[index];
+ }
}
return TNodeWrapper(
@@ -248,6 +347,11 @@ NFyaml::TSequence TArrayNodeWrapper::Sequence() {
return Node_.Sequence();
}
+TArrayNodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::Array, PathFromCheckNode_);
+}
+
+
TInt64NodeWrapper::TInt64NodeWrapper(
TInt64CheckContext* context,
NFyaml::TNodeRef node,
@@ -267,6 +371,11 @@ TInt64NodeWrapper::operator i64() {
return Value();
}
+TInt64NodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::Int64, PathFromCheckNode_);
+}
+
+
TStringNodeWrapper::TStringNodeWrapper(
TStringCheckContext* context,
NFyaml::TNodeRef node,
@@ -286,6 +395,11 @@ TStringNodeWrapper::operator TString() {
return Value();
}
+TStringNodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::String, PathFromCheckNode_);
+}
+
+
TBoolNodeWrapper::TBoolNodeWrapper(
TBoolCheckContext* context,
NFyaml::TNodeRef node,
@@ -307,6 +421,10 @@ TBoolNodeWrapper::operator bool() {
return Value();
}
+TBoolNodeWrapper::operator TNodeWrapper() {
+ return TNodeWrapper(Context_, Node_, Validator_, ENodeType::Bool, PathFromCheckNode_);
+}
+
TCheckContext::TCheckContext(
NFyaml::TNodeRef node,
diff --git a/ydb/library/yaml_config/validator/validator_checks.h b/ydb/library/yaml_config/validator/validator_checks.h
index 417991600a5..b68e171404b 100644
--- a/ydb/library/yaml_config/validator/validator_checks.h
+++ b/ydb/library/yaml_config/validator/validator_checks.h
@@ -33,20 +33,42 @@ namespace NDetail {
template <typename TThis>
class TNodeWrapperCommonOps {
public:
+ bool Exists();
+
+protected:
+ // if true, then has no validator, node type and can be anything
+ bool IsOpaqueChild();
void ThrowIfNullNode();
+
+private:
+ TThis& AsDerived();
};
template <typename TThis>
void TNodeWrapperCommonOps<TThis>::ThrowIfNullNode() {
- auto& This = static_cast<TThis&>(*this);
- if (!This.Node_) {
+ if (!AsDerived().Node_) {
throw yexception() <<
"Node \"" +
- This.PathFromCheckNode_ +
+ AsDerived().PathFromCheckNode_ +
"\" is not presented";
}
}
+template <typename TThis>
+bool TNodeWrapperCommonOps<TThis>::IsOpaqueChild() {
+ return !AsDerived().Validator_;
+}
+
+template <typename TThis>
+bool TNodeWrapperCommonOps<TThis>::Exists() {
+ return AsDerived().Node_ != nullptr;
+}
+
+template <typename TThis>
+TThis& TNodeWrapperCommonOps<TThis>::AsDerived() {
+ return static_cast<TThis&>(*this);
+}
+
} // namespace NDetail
class TNodeWrapper : public NDetail::TNodeWrapperCommonOps<TNodeWrapper> {
@@ -67,7 +89,14 @@ public:
TStringNodeWrapper String();
TBoolNodeWrapper Bool();
- ENodeType Type();
+ TMaybe<ENodeType> ValidatorType();
+
+ bool IsMap();
+ bool IsArray();
+ bool IsInt64();
+ bool IsString();
+ bool IsBool();
+ bool IsScalar();
protected:
TCheckContext* Context_;
@@ -87,6 +116,8 @@ public:
NFyaml::TNodeRef node,
TGenericValidator* validator,
const TString& pathFromCheckNode);
+
+ operator TNodeWrapper();
private:
TGenericCheckContext* Context_;
@@ -105,6 +136,8 @@ public:
TMapValidator* validator,
const TString& pathFromCheckNode);
+ operator TNodeWrapper();
+
TNodeWrapper operator[](const TString& field);
TNodeWrapper At(const TString& field);
bool Has(const TString& field);
@@ -132,6 +165,8 @@ public:
TNodeWrapper operator[](size_t index);
+ operator TNodeWrapper();
+
private:
TArrayCheckContext* Context_;
NFyaml::TNodeRef Node_;
@@ -154,6 +189,8 @@ public:
i64 Value();
operator i64();
+ operator TNodeWrapper();
+
private:
TInt64CheckContext* Context_;
NFyaml::TNodeRef Node_;
@@ -174,6 +211,8 @@ public:
TString Value();
operator TString();
+ operator TNodeWrapper();
+
private:
TStringCheckContext* Context_;
NFyaml::TNodeRef Node_;
@@ -194,6 +233,8 @@ public:
bool Value();
operator bool();
+ operator TNodeWrapper();
+
private:
TBoolCheckContext* Context_;
NFyaml::TNodeRef Node_;