diff options
| author | jansenin <[email protected]> | 2023-09-06 12:31:15 +0300 |
|---|---|---|
| committer | jansenin <[email protected]> | 2023-09-06 13:04:39 +0300 |
| commit | e369652514d1de8e1b6b361631158a95385f11dd (patch) | |
| tree | a13bfbad4bf7c1e9b132e0ff9c32a496fc9b2b30 | |
| parent | 04a19cc6b675d2380241eaa4b6e584a07bbd4280 (diff) | |
add opaque map checks support, cast from typed NodeWrapper to untyped
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_; |
