diff options
author | jansenin <jansenin@yandex-team.com> | 2023-08-30 15:24:06 +0300 |
---|---|---|
committer | jansenin <jansenin@yandex-team.com> | 2023-08-30 15:58:07 +0300 |
commit | 2a6fe42ec1f143aa44c90f8129ae22783f3577bf (patch) | |
tree | e2836be7f8e32e9529401f065afb01acc7edc8a2 | |
parent | 578f59cd45578cc5d3a61e3b1dc1cab146ecab69 (diff) | |
download | ydb-2a6fe42ec1f143aa44c90f8129ae22783f3577bf.tar.gz |
add simple checks
14 files changed, 1395 insertions, 143 deletions
diff --git a/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt index e12028770f..cbeaf2ade8 100644 --- a/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt +++ b/ydb/library/yaml_config/validator/CMakeLists.darwin-x86_64.txt @@ -17,6 +17,7 @@ target_link_libraries(library-yaml_config-validator PUBLIC target_sources(library-yaml_config-validator PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_checks.cpp ) generate_enum_serilization(library-yaml_config-validator ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h diff --git a/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt b/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt index ed6cd10ed4..7fd0a3fa98 100644 --- a/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt +++ b/ydb/library/yaml_config/validator/CMakeLists.linux-aarch64.txt @@ -18,6 +18,7 @@ target_link_libraries(library-yaml_config-validator PUBLIC target_sources(library-yaml_config-validator PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_checks.cpp ) generate_enum_serilization(library-yaml_config-validator ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h diff --git a/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt index ed6cd10ed4..7fd0a3fa98 100644 --- a/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt +++ b/ydb/library/yaml_config/validator/CMakeLists.linux-x86_64.txt @@ -18,6 +18,7 @@ target_link_libraries(library-yaml_config-validator PUBLIC target_sources(library-yaml_config-validator PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_checks.cpp ) generate_enum_serilization(library-yaml_config-validator ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h diff --git a/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt b/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt index e12028770f..cbeaf2ade8 100644 --- a/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt +++ b/ydb/library/yaml_config/validator/CMakeLists.windows-x86_64.txt @@ -17,6 +17,7 @@ target_link_libraries(library-yaml_config-validator PUBLIC target_sources(library-yaml_config-validator PRIVATE ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.cpp ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator.cpp + ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_checks.cpp ) generate_enum_serilization(library-yaml_config-validator ${CMAKE_SOURCE_DIR}/ydb/library/yaml_config/validator/validator_builder.h 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 new file mode 100644 index 0000000000..35033f4df3 --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_checks/validator_checks_ut.cpp @@ -0,0 +1,307 @@ +#include <util/stream/str.h> + +#include <library/cpp/testing/unittest/registar.h> +#include <util/system/types.h> + +#include <ydb/library/yaml_config/validator/validator_builder.h> + +using namespace NYamlConfig::NValidator; +using TIssue = TValidationResult::TIssue; + +bool HasOnlyThisIssues(TValidationResult result, TVector<TIssue> issues) { + if (result.Issues.size() != issues.size()) { + Cerr << "Issue counts are differend. List of actul issues:" << Endl; + Cerr << result; + Cerr << "------------- List of Expected Issues: " << Endl; + Cerr << TValidationResult(issues); + Cerr << "------------- End of issue List" << Endl; + return false; + } + Sort(result.Issues); + Sort(issues); + for (size_t i = 0; i < issues.size(); ++i) { + if (result.Issues[i] != issues[i]) { + Cerr << "Issues are differend. List of actul issues:" << Endl; + Cerr << result; + Cerr << "------------- List of Expected Issues: " << Endl; + Cerr << TValidationResult(issues); + Cerr << "------------- End of issue List" << Endl; + return false; + } + } + return true; +} + +bool Valid(TValidationResult result) { + if (result.Ok()) return true; + + Cerr << "List of issues:" << Endl; + Cerr << result; + Cerr << "------------- End of issue list: " << Endl; + return false; +} + +Y_UNIT_TEST_SUITE(Checks) { + Y_UNIT_TEST(BasicIntChecks) { + TInt64Builder b; + b.AddCheck("Value must be divisible by 2", [](auto& c) { + c.Expect(c.Node() % 2 == 0); + }).AddCheck("Value must be divisible by 3", [](auto& c) { + c.Expect(c.Node().Value() % 3 == 0); + }); + auto v = b.CreateValidator(); + + Y_ENSURE(HasOnlyThisIssues(v.Validate("1"), { + {"/", "Value must be divisible by 3"}, + {"/", "Value must be divisible by 2"} + })); + Y_ENSURE(HasOnlyThisIssues(v.Validate("2"), {{"/", "Value must be divisible by 3"}})); + Y_ENSURE(HasOnlyThisIssues(v.Validate("3"), {{"/", "Value must be divisible by 2"}})); + Y_ENSURE(Valid(v.Validate("6"))); + } + + Y_UNIT_TEST(BasicStringChecks) { + NYamlConfig::NValidator::TStringBuilder b; + b.AddCheck("Value length must be divisible by 2", [](auto& c) { + c.Expect(c.Node().Value().length() % 2 == 0); + }).AddCheck("Value length must be divisible by 3", [](auto& c) { + c.Expect(c.Node().Value().length() % 3 == 0); + }); + auto v = b.CreateValidator(); + + Y_ENSURE(HasOnlyThisIssues(v.Validate("1"), { + {"/", "Value length must be divisible by 3"}, + {"/", "Value length must be divisible by 2"} + })); + Y_ENSURE(HasOnlyThisIssues(v.Validate("22"), {{"/", "Value length must be divisible by 3"}})); + Y_ENSURE(HasOnlyThisIssues(v.Validate("333"), {{"/", "Value length must be divisible by 2"}})); + Y_ENSURE(Valid(v.Validate("666666"))); + } + + Y_UNIT_TEST(IntArrayValidation) { + TArrayBuilder b; + b.Item(TInt64Builder().Range(0, 10)) + .AddCheck("Array length must be greater than 5", [](auto& c) { + c.Expect(c.Node().Length() >= 5); + }).AddCheck("Xor of all array elements must be 0", [](auto& c) { + i64 totalXor = 0; + for (int i = 0; i < c.Node().Length(); ++i) { + totalXor ^= c.Node()[i].Int64().Value(); // or just c[i]? + } + c.Expect(totalXor == 0); + }); + auto v = b.CreateValidator(); + + Y_ENSURE(HasOnlyThisIssues(v.Validate("[]"), {{"/", "Array length must be greater than 5"}})); + + Y_ENSURE(HasOnlyThisIssues(v.Validate("[1]"), { + {"/", "Array length must be greater than 5"}, + {"/", "Xor of all array elements must be 0"} + })); + Y_ENSURE(HasOnlyThisIssues(v.Validate("[-1]"), { + {"/0", "Value must be greater or equal to min value(i.e >= 0)"} + })); + + Y_ENSURE(Valid(v.Validate("[4,5,6,4,5,6]"))); + Y_ENSURE(Valid(v.Validate("[1,2,3,4,5,6,7]"))); + } + + Y_UNIT_TEST(MapValidation) { + // field1 -> must have field2 + // field2 -> must have field1 + + // field3 -> must have int_arr of 3 even non-negative elements with total sum <= 100 + // field4 -> must not have int_arr + + auto optionalInt = [](TInt64Builder& b) { + b.Optional(); + }; + auto evenInt = [](TInt64CheckContext& c) { + c.Expect(c.Node().Value() % 2 == 0); + }; + auto arrLengthIs3 = [](TArrayCheckContext& c) { + c.Expect(c.Node().Length() == 3); + }; + auto sumIsLessOrEqualTo100 = [](TArrayCheckContext& c) { + i64 sum = 0; + for (int i = 0; i < c.Node().Length(); ++i) { + sum += c.Node()[i].Int64().Value(); + } + c.Expect(sum <= 100); + }; + auto field1AndField2MustBeBothPresentOrAbsent = [](TMapCheckContext& c) { + c.Expect(c.Node().Has("field1") == c.Node().Has("field2")); + }; + auto arrayPresenceCheck = [](TMapCheckContext& c) { + if (c.Node().Has("field3")) { + c.Expect(c.Node().Has("int_arr"), "If you have field3, than you must have int_arr"); + } + if (c.Node().Has("field4")) { + c.Expect(!c.Node().Has("int_arr"), "If you have field4, than you must not have int_arr"); + } + }; + + auto v = + TMapBuilder() + .Int64("field1", optionalInt) + .Int64("field2", optionalInt) + .Int64("field3", optionalInt) + .Int64("field4", optionalInt) + .Array("int_arr", [=](auto& b) { + b.Optional() + .Int64Item([=](auto& b) { + b.Min(0) + .AddCheck("Value must be even", evenInt); + }) + .AddCheck("Array length must be 3", arrLengthIs3) + .AddCheck("Sum of all array elements must be less or equal to 100", sumIsLessOrEqualTo100); + }) + .AddCheck( + "field1 and field2 either both must be present or both must be absent", + field1AndField2MustBeBothPresentOrAbsent) + .AddCheck("int_arr precense", arrayPresenceCheck) + .CreateValidator(); + + { + TString yaml = + "{}"; + + Y_ENSURE(Valid(v.Validate(yaml))); + } + + { + TString yaml = + "{\n" + "field1: 0,\n" + "field2: 0,\n" + "field3: 0,\n" + // "field4: 0,\n" + "int_arr: [2,4,6]\n" + "}"; + Y_ENSURE(Valid(v.Validate(yaml))); + } + + { + TString yaml = + "{\n" + "field1: 0,\n" + "field2: 0,\n" + "field3: 0,\n" + // "field4: 0,\n" + "int_arr: [-1,4,601,2]\n" + "}"; + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/int_arr/0", "Value must be greater or equal to min value(i.e >= 0)"}, + {"/int_arr/2", "Value must be even"} + })); + } + + { + TString yaml = + "{\n" + // "field1: 0,\n" + "field2: 0,\n" + "field3: 0,\n" + "field4: 0,\n" + "int_arr: [2,4,600,4]\n" + "}"; + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/int_arr", "Array length must be 3"}, + {"/int_arr", "Sum of all array elements must be less or equal to 100"} + })); + } + + { + TString yaml = + "{\n" + // "field1: 0,\n" + "field2: 0,\n" + "field3: 0,\n" + "field4: 0,\n" + "int_arr: [2,4,4]\n" + "}"; + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/", "Check \"int_arr precense\" failed: If you have field4, than you must not have int_arr"}, + {"/", "field1 and field2 either both must be present or both must be absent"}, + })); + } + } + + //TODO: make Some check tests for generic node(and + //before that make ability to somehow work with this nodes) + + Y_UNIT_TEST(ErrorInCheck) { + auto v = + TMapBuilder() + .Bool("flag") + .Map("a", [](auto& b) { + b.Optional() + .Map("b", [](auto& b) { + b.Int64("c"); + }); + }) + .Map("c", [](auto& b) { + b.Optional() + .Map("b", [](auto& b) { + b.Int64("a"); + }); + }) + .AddCheck("must have even a/b/c if flag is true. Else must have odd c/a/b", [](auto& c) { + if (c.Node()["flag"].Bool()) { + c.Expect(c.Node()["a"].Map()["b"].Map()["c"].Int64().Value() % 2 == 0); + } else { + c.Expect(c.Node()["c"].Map()["b"].Map()["a"].Int64().Value() % 2 == 1); + } + }) + .CreateValidator(); + + const char* yaml = + "{" + " flag: true" + "}"; + + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/", "Check \"must have even a/b/c if flag is true. Else must have odd c/a/b\" failed: Node \"/a/b/c\" is not presented"}, + })); + + yaml = + "{" + " flag: true," + " a: {" + " b: {" + " c: 0" + " }" + " }" + "}"; + + Y_ENSURE(Valid(v.Validate(yaml))); + + yaml = + "{" + " flag: true," + " a: {" + " b: {" + " c: 1" + " }" + " }" + "}"; + + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/", "must have even a/b/c if flag is true. Else must have odd c/a/b"}, + })); + + yaml = + "{" + " flag: faLse," + " a: {" + " b: {" + " c: 0" + " }" + " }" + "}"; + + Y_ENSURE(HasOnlyThisIssues(v.Validate(yaml), { + {"/", "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"}, + })); + } +} diff --git a/ydb/library/yaml_config/validator/ut/validator_checks/ya.make b/ydb/library/yaml_config/validator/ut/validator_checks/ya.make new file mode 100644 index 0000000000..ee3c2f122a --- /dev/null +++ b/ydb/library/yaml_config/validator/ut/validator_checks/ya.make @@ -0,0 +1,11 @@ +UNITTEST() + +SRCS( + validator_checks_ut.cpp +) + +PEERDIR( + ydb/library/yaml_config/validator +) + +END() diff --git a/ydb/library/yaml_config/validator/ut/ya.make b/ydb/library/yaml_config/validator/ut/ya.make index 3a4573f2fb..ee6ee78e22 100644 --- a/ydb/library/yaml_config/validator/ut/ya.make +++ b/ydb/library/yaml_config/validator/ut/ya.make @@ -1,4 +1,5 @@ RECURSE_FOR_TESTS( validator validator_builder + validator_checks ) diff --git a/ydb/library/yaml_config/validator/validator.cpp b/ydb/library/yaml_config/validator/validator.cpp index 87f229daf3..8f7e8f0094 100644 --- a/ydb/library/yaml_config/validator/validator.cpp +++ b/ydb/library/yaml_config/validator/validator.cpp @@ -1,5 +1,7 @@ #include "validator.h" +#include "validator_checks.h" + #include <util/string/cast.h> #include <util/system/types.h> @@ -83,17 +85,20 @@ TValidationResult TValidator::Validate(const TString& documentStr) { return Validate(document.Root()); } -TValidator::TValidator() {} +TValidator::TValidator(ENodeType nodeType) + : NodeType_(nodeType) {} TValidator::TValidator(TValidator&& validator) - : Required_(validator.Required_) {} + : Required_(validator.Required_) + , NodeType_(validator.NodeType_) {} TValidator::~TValidator() {} -TGenericValidator::TGenericValidator() {} +TGenericValidator::TGenericValidator() + : TBase(ENodeType::Generic) {} TGenericValidator::TGenericValidator(TGenericValidator&& validator) - : TValidator(std::move(validator)) + : TBase(std::move(validator)) , ValidatorPtrs_(std::move(validator.ValidatorPtrs_)) {} TValidationResult TGenericValidator::Validate(const NFyaml::TNodeRef& node) { @@ -134,29 +139,39 @@ TValidationResult TGenericValidator::Validate(const NFyaml::TNodeRef& node) { addNewLine = true; } - return TVector<TValidationResult::TIssue>{{ + TValidationResult result = TVector<TValidationResult::TIssue>{{ node.Path(), message }}; + + // put checks code into separete function + // think about all combinations of checker name and errors + if (result.Ok()) { + performChecks(result, node); + } + + return result; } void TGenericValidator::AddValidator(TSimpleSharedPtr<TValidator> validatorPtr) { ValidatorPtrs_.emplace_back(std::move(validatorPtr)); } -TMapValidator::TMapValidator() {} +TMapValidator::TMapValidator() + : TBase(ENodeType::Map) {} TMapValidator::TMapValidator(TMapValidator&& validator) - : TValidator(std::move(validator)) + : TBase(std::move(validator)) , Children_(std::move(validator.Children_)) , Opaque_(validator.Opaque_) {} TMapValidator::TMapValidator(THashMap<TString, TSimpleSharedPtr<TValidator>>&& children, bool Opaque) - : Children_(std::move(children)) + : TBase(ENodeType::Map) + , Children_(std::move(children)) , Opaque_(Opaque) {} TValidationResult TMapValidator::Validate(const TNodeRef& node) { - if (node.Type() != ENodeType::Mapping) { + if (node.Type() != NFyaml::ENodeType::Mapping) { return TValidationResult({{ node.Path(), "Node must be Map" @@ -198,21 +213,28 @@ TValidationResult TMapValidator::Validate(const TNodeRef& node) { } } + if (validationResult.Ok()) { + performChecks(validationResult, node); + } + return validationResult; } -TArrayValidator::TArrayValidator() {} +TArrayValidator::TArrayValidator() + : TBase(ENodeType::Array) {} TArrayValidator::TArrayValidator(TArrayValidator&& validator) - : TValidator(std::move(validator)) + : TBase(std::move(validator)) , ItemValidatorPtr_(std::move(validator.ItemValidatorPtr_)) , Unique_(validator.Unique_) {} TArrayValidator::TArrayValidator(TSimpleSharedPtr<TValidator> itemValidatorPtr, bool Unique) - : ItemValidatorPtr_(std::move(itemValidatorPtr)), Unique_(Unique) {} + : TBase(ENodeType::Array) + , ItemValidatorPtr_(std::move(itemValidatorPtr)) + , Unique_(Unique) {} TValidationResult TArrayValidator::Validate(const TNodeRef& node) { - if (node.Type() != ENodeType::Sequence) { + if (node.Type() != NFyaml::ENodeType::Sequence) { return TValidationResult({{ node.Path(), "Node must be Array" @@ -226,22 +248,29 @@ TValidationResult TArrayValidator::Validate(const TNodeRef& node) { // TODO: check uniqueness Y_UNUSED(Unique_); + + if (validationResult.Ok()) { + performChecks(validationResult, node); + } + return validationResult; } -TInt64Validator::TInt64Validator() {} +TInt64Validator::TInt64Validator() + : TBase(ENodeType::Int64) {} TInt64Validator::TInt64Validator(TInt64Validator&& validator) - : TValidator(std::move(validator)) + : TBase(std::move(validator)) , Min_(validator.Min_) , Max_(validator.Max_) {} TInt64Validator::TInt64Validator(i64 min, i64 max) - : Min_(min) + : TBase(ENodeType::Int64) + , Min_(min) , Max_(max) {} TValidationResult TInt64Validator::Validate(const TNodeRef& node) { - if (node.Type() != ENodeType::Scalar) { + if (node.Type() != NFyaml::ENodeType::Scalar) { return TValidationResult({{ node.Path(), "Node must be Scalar(Int64)" @@ -271,6 +300,10 @@ TValidationResult TInt64Validator::Validate(const TNodeRef& node) { }); } + if (validationResult.Ok()) { + performChecks(validationResult, node); + } + return validationResult; } @@ -282,28 +315,37 @@ void TInt64Validator::SetMax(i64 max) { Max_ = max; } -TStringValidator::TStringValidator() {} +TStringValidator::TStringValidator() + : TBase(ENodeType::String) {} TStringValidator::TStringValidator(TStringValidator&& validator) - : TValidator(std::move(validator)) {} + : TBase(std::move(validator)) {} TValidationResult TStringValidator::Validate(const TNodeRef& node) { - if (node.Type() != ENodeType::Scalar) { + if (node.Type() != NFyaml::ENodeType::Scalar) { return TValidationResult({{ node.Path(), "Node must be Scalar(String)" }}); } - return {}; + + TValidationResult validationResult; + + if (validationResult.Ok()) { + performChecks(validationResult, node); + } + + return validationResult; } -TBoolValidator::TBoolValidator() {} +TBoolValidator::TBoolValidator() + : TBase(ENodeType::Bool) {} TBoolValidator::TBoolValidator(TBoolValidator&& validator) - : TValidator(std::move(validator)) {} + : TBase(std::move(validator)) {} TValidationResult TBoolValidator::Validate(const TNodeRef& node) { - if (node.Type() != ENodeType::Scalar) { + if (node.Type() != NFyaml::ENodeType::Scalar) { return TValidationResult({{ node.Path(), "Node must be Scalar(Bool)" @@ -318,7 +360,12 @@ TValidationResult TBoolValidator::Validate(const TNodeRef& node) { "Value must be either true or false" }}); } - return {}; + + TValidationResult validationResult; + + performChecks(validationResult, node); + + return validationResult; } } // namespace NYamlConfig::NValidator diff --git a/ydb/library/yaml_config/validator/validator.h b/ydb/library/yaml_config/validator/validator.h index a186c32d42..63ff8c4fed 100644 --- a/ydb/library/yaml_config/validator/validator.h +++ b/ydb/library/yaml_config/validator/validator.h @@ -12,6 +12,10 @@ namespace NYamlConfig::NValidator { +enum class ENodeType { + Generic, Map, Array, Int64, String, Bool +}; + class TValidator; class TGenericValidator; class TMapValidator; @@ -20,6 +24,22 @@ class TInt64Validator; class TStringValidator; class TBoolValidator; +class TGenericNodeWrapper; +class TMapNodeWrapper; +class TArrayNodeWrapper; +class TInt64NodeWrapper; +class TStringNodeWrapper; +class TBoolNodeWrapper; + +class TGenericCheckContext; +class TMapCheckContext; +class TArrayCheckContext; +class TInt64CheckContext; +class TStringCheckContext; +class TBoolCheckContext; + +class TCheckContext; + class TGenericBuilder; namespace NDetail { @@ -28,15 +48,20 @@ class TBuilder; TSimpleSharedPtr<TValidator> CreateValidatorPtr(const TSimpleSharedPtr<NDetail::TBuilder>& builder); +template <typename TThis, typename TContext> +class TValidatorCommonOps; + } class TValidationResult { + friend class TCheckContext; friend class TGenericValidator; friend class TMapValidator; friend class TArrayValidator; friend class TInt64Validator; friend class TStringValidator; friend class TBoolValidator; + template <typename TThis, typename TContext> friend class NDetail::TValidatorCommonOps; public: struct TIssue { @@ -76,23 +101,98 @@ class TValidator { friend TSimpleSharedPtr<TValidator> NYamlConfig::NValidator::NDetail::CreateValidatorPtr(const TSimpleSharedPtr<NDetail::TBuilder>& builder); friend class TMapValidator; // for .Required_ friend class TGenericBuilder; // for .Required_ + friend class TMapNodeWrapper; // NodeType + friend class TArrayNodeWrapper; // NodeType public: - TValidator(); - virtual TValidationResult Validate(const NFyaml::TNodeRef& node) = 0; TValidationResult Validate(const TString& document); virtual ~TValidator(); protected: + bool Required_ = true; + + TValidator(ENodeType nodeType); TValidator(TValidator&& validator); private: - bool Required_ = true; + ENodeType NodeType_; }; -class TGenericValidator : public TValidator { +namespace NDetail { + +template <typename TThis, typename TContext> +class TValidatorCommonOps : public TValidator { +protected: + THashMap<TString, std::function<void(TContext&)>> Checkers_; + + TValidatorCommonOps<TThis, TContext>(ENodeType nodeType); + TValidatorCommonOps<TThis, TContext>(TValidatorCommonOps<TThis, TContext>&& builder); + + void performChecks(TValidationResult& validationResult, const NFyaml::TNodeRef& node); +}; + +template <typename TThis, typename TContext> +TValidatorCommonOps<TThis, TContext>::TValidatorCommonOps(ENodeType nodeType) + : TValidator(nodeType) {} + +template <typename TThis, typename TContext> +TValidatorCommonOps<TThis, TContext>::TValidatorCommonOps(TValidatorCommonOps<TThis, TContext>&& builder) + : TValidator(std::move(builder)) + , Checkers_(std::move(builder.Checkers_)) {} + +template <typename TThis, typename TContext> +void TValidatorCommonOps<TThis, TContext>::performChecks(TValidationResult& validationResult, const NFyaml::TNodeRef& node) { + TThis* This = static_cast<TThis*>(this); + for (auto& [checkName, checker] : Checkers_) { + // TODO: change node.Path() to stored path in validator (or maybe node.Path is better?) + TString nodePath = node.Path(); + if (nodePath == "/") { + nodePath = ""; + } + + TContext context(node, nodePath, This); + try { + checker(context); + } catch (yexception& ye) { + context.AddError(ye.what()); + context.someExpectFailed = true; + } + + if (context.someExpectFailed) { + if (context.Errors_.empty()) { + validationResult.AddIssue({node.Path(), checkName}); + } else if (context.Errors_.size() == 1) { + validationResult.AddIssue({node.Path(), "Check \"" + checkName + "\" failed: " + context.Errors_[0]}); + } else { + TString errorMessages; + + //TODO: add multiline errors support(and maybe extract big + //error creation into separete function) + bool newLine = false; + for (auto& error : context.Errors_) { + if (newLine) { + errorMessages += "\n"; + } + errorMessages += error; + newLine = true; + } + + validationResult.AddIssue({node.Path(), "Check \"" + checkName + "\" failed: \n" + errorMessages}); + } + } + } +} + +} + +class TGenericValidator : public NDetail::TValidatorCommonOps<TGenericValidator, TGenericCheckContext> { + friend class TGenericNodeWrapper; + friend class TGenericBuilder; + + using TBase = NDetail::TValidatorCommonOps<TGenericValidator, TGenericCheckContext>; + public: TGenericValidator(); @@ -101,51 +201,64 @@ public: TValidationResult Validate(const NFyaml::TNodeRef& node) override; using TValidator::Validate; - void AddValidator(TSimpleSharedPtr<TValidator> validatorPtr); - private: TVector<TSimpleSharedPtr<TValidator>> ValidatorPtrs_; + + void AddValidator(TSimpleSharedPtr<TValidator> validatorPtr); }; -class TMapValidator : public TValidator { +class TMapValidator : public NDetail::TValidatorCommonOps<TMapValidator, TMapCheckContext> { + friend class TMapNodeWrapper; + friend class TMapBuilder; + + using TBase = NDetail::TValidatorCommonOps<TMapValidator, TMapCheckContext>; + public: TMapValidator(); TMapValidator(TMapValidator&& validator); - explicit TMapValidator(THashMap<TString, TSimpleSharedPtr<TValidator>>&& children, bool Opaque); - TValidationResult Validate(const NFyaml::TNodeRef& node) override; using TValidator::Validate; private: THashMap<TString, TSimpleSharedPtr<TValidator>> Children_; bool Opaque_; + + TMapValidator(THashMap<TString, TSimpleSharedPtr<TValidator>>&& children, bool Opaque); }; -class TArrayValidator : public TValidator { +class TArrayValidator : public NDetail::TValidatorCommonOps<TArrayValidator, TArrayCheckContext> { + friend class TArrayNodeWrapper; + friend class TArrayBuilder; + + using TBase = NDetail::TValidatorCommonOps<TArrayValidator, TArrayCheckContext>; + public: TArrayValidator(); TArrayValidator(TArrayValidator&& validator); - TArrayValidator(TSimpleSharedPtr<TValidator> itemValidatorPtr, bool Unique); - TValidationResult Validate(const NFyaml::TNodeRef& node) override; using TValidator::Validate; private: TSimpleSharedPtr<TValidator> ItemValidatorPtr_; bool Unique_; + + TArrayValidator(TSimpleSharedPtr<TValidator> itemValidatorPtr, bool Unique); }; -class TInt64Validator : public TValidator { +class TInt64Validator : public NDetail::TValidatorCommonOps<TInt64Validator, TInt64CheckContext> { + friend class TInt64NodeWrapper; + friend class TInt64Builder; + + using TBase = NDetail::TValidatorCommonOps<TInt64Validator, TInt64CheckContext>; + public: TInt64Validator(); TInt64Validator(TInt64Validator&& validator); - - TInt64Validator(i64 min, i64 max); TValidationResult Validate(const NFyaml::TNodeRef& node) override; using TValidator::Validate; @@ -154,11 +267,18 @@ private: i64 Min_ = Min<i64>(); i64 Max_ = Max<i64>(); + TInt64Validator(i64 min, i64 max); + void SetMin(i64 min); void SetMax(i64 max); }; -class TStringValidator : public TValidator { +class TStringValidator : public NDetail::TValidatorCommonOps<TStringValidator, TStringCheckContext> { + friend class TStringNodeWrapper; + friend class TStringBuilder; + + using TBase = NDetail::TValidatorCommonOps<TStringValidator, TStringCheckContext>; + public: TStringValidator(); @@ -168,7 +288,12 @@ public: using TValidator::Validate; }; -class TBoolValidator : public TValidator { +class TBoolValidator : public NDetail::TValidatorCommonOps<TBoolValidator, TBoolCheckContext> { + friend class TBoolNodeWrapper; + friend class TBoolBuilder; + + using TBase = NDetail::TValidatorCommonOps<TBoolValidator, TBoolCheckContext>; + public: TBoolValidator(); diff --git a/ydb/library/yaml_config/validator/validator_builder.cpp b/ydb/library/yaml_config/validator/validator_builder.cpp index df62cc82dd..8813a60045 100644 --- a/ydb/library/yaml_config/validator/validator_builder.cpp +++ b/ydb/library/yaml_config/validator/validator_builder.cpp @@ -11,52 +11,40 @@ namespace NYamlConfig::NValidator { namespace NDetail { TSimpleSharedPtr<TValidator> CreateValidatorPtr(const TSimpleSharedPtr<TBuilder>& builder) { - switch (builder->BuilderType_) { - case EBuilderType::Generic: { - TGenericValidator v = static_cast<TGenericBuilder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TGenericValidator(std::move(v))); + switch (builder->NodeType_) { + case ENodeType::Generic: { + return MakeSimpleShared<TGenericValidator>(static_cast<TGenericBuilder*>(builder.Get())->CreateValidator()); } - case EBuilderType::Map: { - TMapValidator v = static_cast<TMapBuilder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TMapValidator(std::move(v))); + case ENodeType::Map: { + return MakeSimpleShared<TMapValidator>(static_cast<TMapBuilder*>(builder.Get())->CreateValidator()); } - case EBuilderType::Array: { - TArrayValidator v = static_cast<TArrayBuilder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TArrayValidator(std::move(v))); + case ENodeType::Array: { + return MakeSimpleShared<TArrayValidator>(static_cast<TArrayBuilder*>(builder.Get())->CreateValidator()); } - case EBuilderType::Int64: { - TInt64Validator v = static_cast<TInt64Builder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TInt64Validator(std::move(v))); + case ENodeType::Int64: { + return MakeSimpleShared<TInt64Validator>(static_cast<TInt64Builder*>(builder.Get())->CreateValidator()); } - case EBuilderType::String: { - TStringValidator v = static_cast<TStringBuilder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TStringValidator(std::move(v))); + case ENodeType::String: { + return MakeSimpleShared<TStringValidator>(static_cast<TStringBuilder*>(builder.Get())->CreateValidator()); } - case EBuilderType::Bool: { - TBoolValidator v = static_cast<TBoolBuilder*>(builder.Get())->CreateValidator(); - v.Required_ = builder->Required_; - return TSimpleSharedPtr<TValidator>(new TBoolValidator(std::move(v))); + case ENodeType::Bool: { + return MakeSimpleShared<TBoolValidator>(static_cast<TBoolBuilder*>(builder.Get())->CreateValidator()); } } } -TBuilder::TBuilder(EBuilderType builderType) - : BuilderType_(builderType) {} +TBuilder::TBuilder(ENodeType nodeType) + : NodeType_(nodeType) {} TBuilder& TBuilder::operator=(const TBuilder& builder) { - Y_ASSERT(BuilderType_ == builder.BuilderType_); + Y_ASSERT(NodeType_ == builder.NodeType_); Required_ = builder.Required_; Description_ = builder.Description_; return *this; } TBuilder& TBuilder::operator=(TBuilder&& builder) { - Y_ASSERT(BuilderType_ == builder.BuilderType_); + Y_ASSERT(NodeType_ == builder.NodeType_); Required_ = builder.Required_; Description_ = std::move(builder.Description_); return *this; @@ -64,23 +52,23 @@ TBuilder& TBuilder::operator=(TBuilder&& builder) { } // namespace NDetail -TGenericBuilder::NodeTypeAndBuilder::NodeTypeAndBuilder() {} +TGenericBuilder::TTypedBuilder::TTypedBuilder() {} -TGenericBuilder::NodeTypeAndBuilder::NodeTypeAndBuilder(EBuilderType type, TSimpleSharedPtr<TBuilder> builder) +TGenericBuilder::TTypedBuilder::TTypedBuilder(ENodeType type, TSimpleSharedPtr<TBuilder> builder) : Type(type) , Builder(std::move(builder)) {} // TGenericBuilder TGenericBuilder::TGenericBuilder() - : TCommonBuilderOps<TGenericBuilder>(EBuilderType::Generic) {}; + : TBase(ENodeType::Generic) {}; TGenericBuilder::TGenericBuilder(const TGenericBuilder& builder) - : TCommonBuilderOps<TGenericBuilder>(builder) + : TBase(builder) , PossibleBuilderPtrs_(builder.PossibleBuilderPtrs_) {} TGenericBuilder::TGenericBuilder(TGenericBuilder&& builder) - : TCommonBuilderOps<TGenericBuilder>(std::move(builder)) + : TBase(std::move(builder)) , PossibleBuilderPtrs_(std::move(builder.PossibleBuilderPtrs_)) {} TGenericBuilder& TGenericBuilder::operator=(const TGenericBuilder& builder) { @@ -131,21 +119,22 @@ TGenericValidator TGenericBuilder::CreateValidator() { result.AddValidator(NDetail::CreateValidatorPtr(builderPtr)); } + result.Checkers_ = Checkers_; result.Required_ = Required_; return result; } TMapBuilder::TMapBuilder() - : TCommonBuilderOps<TMapBuilder>(EBuilderType::Map) {} + : TBase(ENodeType::Map) {} TMapBuilder::TMapBuilder(const TMapBuilder& builder) - : TCommonBuilderOps<TMapBuilder>(builder) + : TBase(builder) , Children_(builder.Children_) , Opaque_(builder.Opaque_) {} TMapBuilder::TMapBuilder(TMapBuilder&& builder) - : TCommonBuilderOps<TMapBuilder>(std::move(builder)) + : TBase(std::move(builder)) , Children_(std::move(builder.Children_)) , Opaque_(builder.Opaque_) {} @@ -254,21 +243,26 @@ TMapValidator TMapBuilder::CreateValidator() { children[name] = NDetail::CreateValidatorPtr(builderPtr); } - return TMapValidator(std::move(children), Opaque_); + auto result = TMapValidator(std::move(children), Opaque_); + + result.Checkers_ = Checkers_; + result.Required_ = Required_; + + return result; } // TArrayBuilder TArrayBuilder::TArrayBuilder() - : TCommonBuilderOps<TArrayBuilder>(EBuilderType::Array) {} + : TBase(ENodeType::Array) {} TArrayBuilder::TArrayBuilder(const TArrayBuilder& builder) - : TCommonBuilderOps<TArrayBuilder>(builder) + : TBase(builder) , ItemPtr_(builder.ItemPtr_) , Unique_(builder.Unique_) {} TArrayBuilder::TArrayBuilder(TArrayBuilder&& builder) - : TCommonBuilderOps<TArrayBuilder>(std::move(builder)) + : TBase(std::move(builder)) , ItemPtr_(std::move(builder.ItemPtr_)) , Unique_(builder.Unique_) {} @@ -329,21 +323,24 @@ NDetail::TBuilder& TArrayBuilder::GetItem() { } TArrayValidator TArrayBuilder::CreateValidator() { - return TArrayValidator(NDetail::CreateValidatorPtr(ItemPtr_), Unique_); + auto result = TArrayValidator(NDetail::CreateValidatorPtr(ItemPtr_), Unique_); + result.Checkers_ = Checkers_; + result.Required_ = Required_; + return result; } // TInt64Builder TInt64Builder::TInt64Builder() - : TCommonBuilderOps<TInt64Builder>(EBuilderType::Int64) {} + : TBase(ENodeType::Int64) {} TInt64Builder::TInt64Builder(const TInt64Builder& builder) - : TCommonBuilderOps<TInt64Builder>(builder) + : TBase(builder) , Min_(builder.Min_) , Max_(builder.Max_) {} TInt64Builder::TInt64Builder(TInt64Builder&& builder) - : TCommonBuilderOps<TInt64Builder>(std::move(builder)) + : TBase(std::move(builder)) , Min_(builder.Min_) , Max_(builder.Max_) {} @@ -383,19 +380,22 @@ TInt64Builder& TInt64Builder::Range(i64 min, i64 max) { } TInt64Validator TInt64Builder::CreateValidator() { - return TInt64Validator(Min_, Max_); + auto result = TInt64Validator(Min_, Max_); + result.Checkers_ = Checkers_; + result.Required_ = Required_; + return result; } // TStringBuilder TStringBuilder::TStringBuilder() - : TCommonBuilderOps<TStringBuilder>(EBuilderType::Int64) {} + : TBase(ENodeType::String) {} TStringBuilder::TStringBuilder(const TStringBuilder& builder) - : TCommonBuilderOps<TStringBuilder>(builder) {} + : TBase(builder) {} TStringBuilder::TStringBuilder(TStringBuilder&& builder) - : TCommonBuilderOps<TStringBuilder>(std::move(builder)) {} + : TBase(std::move(builder)) {} TStringBuilder& TStringBuilder::operator=(const TStringBuilder& builder) { TBuilder::operator=(builder); @@ -413,19 +413,22 @@ TStringBuilder::TStringBuilder(std::function<void(TStringBuilder&)> configurator } TStringValidator TStringBuilder::CreateValidator() { - return TStringValidator(); + auto result = TStringValidator(); + result.Checkers_ = Checkers_; + result.Required_ = Required_; + return result; } // TBoolBuilder TBoolBuilder::TBoolBuilder() - : TCommonBuilderOps<TBoolBuilder>(EBuilderType::Bool) {} + : TBase(ENodeType::Bool) {} TBoolBuilder::TBoolBuilder(const TBoolBuilder& builder) - : TCommonBuilderOps<TBoolBuilder>(builder) {} + : TBase(builder) {} TBoolBuilder::TBoolBuilder(TBoolBuilder&& builder) - : TCommonBuilderOps<TBoolBuilder>(std::move(builder)) {} + : TBase(std::move(builder)) {} TBoolBuilder& TBoolBuilder::operator=(const TBoolBuilder& builder) { TBuilder::operator=(builder); @@ -443,7 +446,10 @@ TBoolBuilder::TBoolBuilder(std::function<void(TBoolBuilder&)> configurator) } TBoolValidator TBoolBuilder::CreateValidator() { - return TBoolValidator(); + auto result = TBoolValidator(); + result.Checkers_ = Checkers_; + result.Required_ = Required_; + return result; } } // namespace NYamlConfig::NValidator diff --git a/ydb/library/yaml_config/validator/validator_builder.h b/ydb/library/yaml_config/validator/validator_builder.h index eee7b94912..bce7c25b98 100644 --- a/ydb/library/yaml_config/validator/validator_builder.h +++ b/ydb/library/yaml_config/validator/validator_builder.h @@ -1,6 +1,7 @@ #pragma once #include "validator.h" +#include "validator_checks.h" #include <util/generic/ptr.h> #include <util/generic/vector.h> @@ -15,10 +16,6 @@ namespace NYamlConfig::NValidator { -enum class EBuilderType { - Generic, Map, Array, Int64, String, Bool -}; - class BuilderException : public yexception {}; class TGenericBuilder; @@ -38,7 +35,7 @@ class TBuilder { friend TSimpleSharedPtr<TValidator> NDetail::CreateValidatorPtr(const TSimpleSharedPtr<TBuilder>& builder); public: - TBuilder(EBuilderType builderType); + TBuilder(ENodeType nodeType); TBuilder(const TBuilder& builder) = default; TBuilder(TBuilder&& builder) = default; @@ -49,30 +46,37 @@ public: virtual ~TBuilder() = default; protected: - const EBuilderType BuilderType_; + const ENodeType NodeType_; bool Required_ = true; TString Description_; }; -template <typename ThisType> +template <typename TThis, typename TCheckContext> class TCommonBuilderOps : public TBuilder { public: - TCommonBuilderOps<ThisType>(EBuilderType builderType); - TCommonBuilderOps<ThisType>(const TBuilder& builder); + TCommonBuilderOps<TThis, TCheckContext>(ENodeType nodeType); + TCommonBuilderOps<TThis, TCheckContext>(const TCommonBuilderOps<TThis, TCheckContext>& builder); - ThisType& Optional(); - ThisType& Required(); - ThisType& Description(const TString& description); + TThis& Optional(); + TThis& Required(); + TThis& Description(const TString& description); - ThisType& Configure(std::function<void(ThisType&)> configurator = [](auto&){}); + TThis& Configure(std::function<void(TThis&)> configurator = [](auto&){}); + + TThis& AddCheck(TString name, std::function<void(TCheckContext&)> checker); + +protected: + THashMap<TString, std::function<void(TCheckContext&)>> Checkers_; private: - ThisType& AsDerived(); + TThis& AsDerived(); }; } // namespace NDetail -class TGenericBuilder : public NDetail::TCommonBuilderOps<TGenericBuilder> { +class TGenericBuilder : public NDetail::TCommonBuilderOps<TGenericBuilder, TGenericCheckContext> { + using TBase = NDetail::TCommonBuilderOps<TGenericBuilder, TGenericCheckContext>; + public: TGenericBuilder(); @@ -84,8 +88,8 @@ public: TGenericBuilder(std::function<void(TGenericBuilder&)> configurator); - template <typename Builder> - TGenericBuilder& CanBe(Builder builder); + template <typename TBuilder> + TGenericBuilder& CanBe(TBuilder builder); TGenericBuilder& CanBeMap(std::function<void(TMapBuilder&)> configurator = [](auto&){}); TGenericBuilder& CanBeArray(std::function<void(TArrayBuilder&)> configurator = [](auto&){}); TGenericBuilder& CanBeInt64(std::function<void(TInt64Builder&)> configurator = [](auto&){}); @@ -95,18 +99,20 @@ public: TGenericValidator CreateValidator(); private: - struct NodeTypeAndBuilder { - EBuilderType Type; + struct TTypedBuilder { + ENodeType Type; TSimpleSharedPtr<TBuilder> Builder; - NodeTypeAndBuilder(); - NodeTypeAndBuilder(EBuilderType type, TSimpleSharedPtr<TBuilder> builder); + TTypedBuilder(); + TTypedBuilder(ENodeType type, TSimpleSharedPtr<TBuilder> builder); }; TVector<TSimpleSharedPtr<TBuilder>> PossibleBuilderPtrs_; }; -class TMapBuilder : public NDetail::TCommonBuilderOps<TMapBuilder> { +class TMapBuilder : public NDetail::TCommonBuilderOps<TMapBuilder, TMapCheckContext> { + using TBase = NDetail::TCommonBuilderOps<TMapBuilder, TMapCheckContext>; + public: TMapBuilder(); @@ -118,8 +124,8 @@ public: TMapBuilder(std::function<void(TMapBuilder&)> configurator); - template <typename Builder> - TMapBuilder& Field(const TString& field, Builder builder); + template <typename TBuilder> + TMapBuilder& Field(const TString& field, TBuilder builder); TMapBuilder& GenericField(const TString& field, std::function<void(TGenericBuilder&)> configurator = [](auto&){}); TMapBuilder& Map(const TString& field, std::function<void(TMapBuilder&)> configurator = [](auto&){}); @@ -149,7 +155,9 @@ private: void ThrowIfAlreadyHasField(const TString& field); }; -class TArrayBuilder : public NDetail::TCommonBuilderOps<TArrayBuilder> { +class TArrayBuilder : public NDetail::TCommonBuilderOps<TArrayBuilder, TArrayCheckContext> { + using TBase = NDetail::TCommonBuilderOps<TArrayBuilder, TArrayCheckContext>; + public: TArrayBuilder(); @@ -163,8 +171,8 @@ public: TArrayBuilder& Unique(); - template <typename Builder> - TArrayBuilder& Item(Builder builder); + template <typename TBuilder> + TArrayBuilder& Item(TBuilder builder); TArrayBuilder& MapItem(std::function<void(TMapBuilder&)> configurator = [](auto&){}); TArrayBuilder& ArrayItem(std::function<void(TArrayBuilder&)> configurator = [](auto&){}); TArrayBuilder& Int64Item(std::function<void(TInt64Builder&)> configurator = [](auto&){}); @@ -180,7 +188,9 @@ private: bool Unique_ = false; }; -class TInt64Builder : public NDetail::TCommonBuilderOps<TInt64Builder> { +class TInt64Builder : public NDetail::TCommonBuilderOps<TInt64Builder, TInt64CheckContext> { + using TBase = NDetail::TCommonBuilderOps<TInt64Builder, TInt64CheckContext>; + public: TInt64Builder(); @@ -203,7 +213,9 @@ private: i64 Max_ = ::Max<i64>(); }; -class TStringBuilder : public NDetail::TCommonBuilderOps<TStringBuilder> { +class TStringBuilder : public NDetail::TCommonBuilderOps<TStringBuilder, TStringCheckContext> { + using TBase = NDetail::TCommonBuilderOps<TStringBuilder, TStringCheckContext>; + public: TStringBuilder(); @@ -218,7 +230,9 @@ public: TStringValidator CreateValidator(); }; -class TBoolBuilder : public NDetail::TCommonBuilderOps<TBoolBuilder> { +class TBoolBuilder : public NDetail::TCommonBuilderOps<TBoolBuilder, TBoolCheckContext> { + using TBase = NDetail::TCommonBuilderOps<TBoolBuilder, TBoolCheckContext>; + public: TBoolBuilder(); @@ -233,43 +247,52 @@ public: TBoolValidator CreateValidator(); }; -template <typename ThisType> -NDetail::TCommonBuilderOps<ThisType>::TCommonBuilderOps(EBuilderType builderType) - : TBuilder(builderType) {} +template <typename TThis, typename TCheckContext> +NDetail::TCommonBuilderOps<TThis, TCheckContext>::TCommonBuilderOps(ENodeType nodeType) + : TBuilder(nodeType) {} -template <typename ThisType> -NDetail::TCommonBuilderOps<ThisType>::TCommonBuilderOps(const TBuilder& builder) - : TBuilder(builder) {} +template <typename TThis, typename TCheckContext> +NDetail::TCommonBuilderOps<TThis, TCheckContext>::TCommonBuilderOps(const TCommonBuilderOps<TThis, TCheckContext>& builder) + : TBuilder(builder), Checkers_(builder.Checkers_) {} -template <typename ThisType> -ThisType& NDetail::TCommonBuilderOps<ThisType>::AsDerived() { - return static_cast<ThisType&>(*this); +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::AsDerived() { + return static_cast<TThis&>(*this); } -template <typename ThisType> -ThisType& NDetail::TCommonBuilderOps<ThisType>::Optional() { +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::Optional() { Required_ = false; return AsDerived(); } -template <typename ThisType> -ThisType& NDetail::TCommonBuilderOps<ThisType>::Required() { +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::Required() { Required_ = true; return AsDerived(); } -template <typename ThisType> -ThisType& NDetail::TCommonBuilderOps<ThisType>::Description(const TString& description) { +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::Description(const TString& description) { Description_ = description; return AsDerived(); } -template <typename ThisType> -ThisType& NDetail::TCommonBuilderOps<ThisType>::Configure(std::function<void(ThisType&)> configurator) { +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::Configure(std::function<void(TThis&)> configurator) { configurator(AsDerived()); return AsDerived(); } +template <typename TThis, typename TCheckContext> +TThis& NDetail::TCommonBuilderOps<TThis, TCheckContext>::AddCheck(TString name, std::function<void(TCheckContext&)> checker) { + if (Checkers_.contains(name)) { + ythrow yexception() << "Already has check named \"" << name << "\""; + } + Checkers_[name] = checker; + return AsDerived(); +} + template <typename Builder> TGenericBuilder& TGenericBuilder::CanBe(Builder builder) { PossibleBuilderPtrs_.emplace_back(new Builder(std::move(builder))); diff --git a/ydb/library/yaml_config/validator/validator_checks.cpp b/ydb/library/yaml_config/validator/validator_checks.cpp new file mode 100644 index 0000000000..965bf97545 --- /dev/null +++ b/ydb/library/yaml_config/validator/validator_checks.cpp @@ -0,0 +1,390 @@ +#include "validator_checks.h" + +#include <util/string/cast.h> + +namespace NYamlConfig::NValidator { + +TNodeWrapper::TNodeWrapper( + TCheckContext* context, + NFyaml::TNodeRef node, + TValidator* validator, + TMaybe<ENodeType> nodeType, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , NodeType_(nodeType) + , PathFromCheckNode_(pathFromCheckNode) {} + +TGenericNodeWrapper TNodeWrapper::Generic() { + TGenericCheckContext* context = nullptr; + TGenericValidator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::Generic); + + context = static_cast<TGenericCheckContext*>(Context_); + validator = static_cast<TGenericValidator*>(Validator_); + } + + return TGenericNodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +TMapNodeWrapper TNodeWrapper::Map() { + TMapCheckContext* context = nullptr; + TMapValidator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::Map); + + context = static_cast<TMapCheckContext*>(Context_); + validator = static_cast<TMapValidator*>(Validator_); + } + + return TMapNodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +TArrayNodeWrapper TNodeWrapper::Array() { + TArrayCheckContext* context = nullptr; + TArrayValidator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::Array); + + context = static_cast<TArrayCheckContext*>(Context_); + validator = static_cast<TArrayValidator*>(Validator_); + } + + return TArrayNodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +TInt64NodeWrapper TNodeWrapper::Int64() { + TInt64CheckContext* context = nullptr; + TInt64Validator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::Int64); + + context = static_cast<TInt64CheckContext*>(Context_); + validator = static_cast<TInt64Validator*>(Validator_); + } + + return TInt64NodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +TStringNodeWrapper TNodeWrapper::String() { + TStringCheckContext* context = nullptr; + TStringValidator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::String); + + context = static_cast<TStringCheckContext*>(Context_); + validator = static_cast<TStringValidator*>(Validator_); + } + + return TStringNodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +TBoolNodeWrapper TNodeWrapper::Bool() { + TBoolCheckContext* context = nullptr; + TBoolValidator* validator = nullptr; + + if (Node_) { + Y_ASSERT(NodeType_ == ENodeType::Bool); + + context = static_cast<TBoolCheckContext*>(Context_); + validator = static_cast<TBoolValidator*>(Validator_); + } + + return TBoolNodeWrapper( + context, + Node_, + validator, + PathFromCheckNode_); +} + +ENodeType TNodeWrapper::Type() { + ThrowIfNullNode(); + Y_ASSERT(NodeType_.Defined()); + return NodeType_.GetRef(); +} + +TGenericNodeWrapper::TGenericNodeWrapper( + TGenericCheckContext* context, + NFyaml::TNodeRef node, + TGenericValidator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) { + Y_UNUSED(Validator_); + Y_UNUSED(Context_); + Y_UNUSED(Node_); + } + +TMapNodeWrapper::TMapNodeWrapper( + TMapCheckContext* context, + NFyaml::TNodeRef node, + TMapValidator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) {} + +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_; + } + // TODO: else must deduce node type here or later(in check for + // example) for use with opaque maps + + return TNodeWrapper( + Context_, + node, + validator, + nodeType, + PathFromCheckNode_ + "/" + field); +} + +TNodeWrapper TMapNodeWrapper::At(const TString& field) { + ThrowIfNullNode(); + + if (!Validator_->Children_.contains(field) || !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(); + return TNodeWrapper( + Context_, + Map().at(field), + validator, + validator->NodeType_, + PathFromCheckNode_ + "/" + field); +} + +bool TMapNodeWrapper::Has(const TString& field) { + ThrowIfNullNode(); + return Map().Has(field); +} + +NFyaml::TMapping TMapNodeWrapper::Map() { + return Node_.Map(); +} + +TArrayNodeWrapper::TArrayNodeWrapper( + TArrayCheckContext* context, + NFyaml::TNodeRef node, + TArrayValidator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) {} + +int TArrayNodeWrapper::Length() { + ThrowIfNullNode(); + return Sequence().size(); +} + +TNodeWrapper TArrayNodeWrapper::operator[](size_t index) { + NFyaml::TNodeRef node(nullptr); + TValidator* validator = nullptr; + TMaybe<ENodeType> nodeType; + + if (Node_ && Sequence().size() > index) { + node = Sequence()[index]; + } + + if (Validator_) { + validator = Validator_->ItemValidatorPtr_.Get(); + nodeType = validator->NodeType_; + } + + return TNodeWrapper( + Context_, + node, + validator, + nodeType, + PathFromCheckNode_ + "/" + ToString(index)); +} + +NFyaml::TSequence TArrayNodeWrapper::Sequence() { + return Node_.Sequence(); +} + +TInt64NodeWrapper::TInt64NodeWrapper( + TInt64CheckContext* context, + NFyaml::TNodeRef node, + TInt64Validator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) {} + +i64 TInt64NodeWrapper::Value() { + ThrowIfNullNode(); + return FromString<i64>(Node_.Scalar()); +} + +TInt64NodeWrapper::operator i64() { + return Value(); +} + +TStringNodeWrapper::TStringNodeWrapper( + TStringCheckContext* context, + NFyaml::TNodeRef node, + TStringValidator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) {} + +TString TStringNodeWrapper::Value() { + ThrowIfNullNode(); + return Node_.Scalar(); +} + +TStringNodeWrapper::operator TString() { + return Value(); +} + +TBoolNodeWrapper::TBoolNodeWrapper( + TBoolCheckContext* context, + NFyaml::TNodeRef node, + TBoolValidator* validator, + const TString& pathFromCheckNode) + : Context_(context) + , Node_(node) + , Validator_(validator) + , PathFromCheckNode_(pathFromCheckNode) {} + +bool TBoolNodeWrapper::Value() { + ThrowIfNullNode(); + TString scalar = Node_.Scalar(); + scalar.to_lower(); + return scalar == "true"; +} + +TBoolNodeWrapper::operator bool() { + return Value(); +} + + +TCheckContext::TCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath) + : Node_(node) + , CheckNodePath_(checkNodePath) {} + +void TCheckContext::Expect(bool condition, TString error) { + if (!condition) { + AddError(error); + someExpectFailed = true; + } +} + +void TCheckContext::Expect(bool condition) { + someExpectFailed |= !condition; +} + +void TCheckContext::AddError(TString error) { + Errors_.emplace_back(std::move(error)); +} + +TGenericCheckContext::TGenericCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TGenericValidator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TGenericNodeWrapper TGenericCheckContext::Node() { + return TGenericNodeWrapper(this, Node_, Validator_, CheckNodePath_); +} + +TMapCheckContext::TMapCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TMapValidator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TMapNodeWrapper TMapCheckContext::Node() { + return TMapNodeWrapper(this, Node_.Map(), Validator_, CheckNodePath_); +} + +TArrayCheckContext::TArrayCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TArrayValidator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TArrayNodeWrapper TArrayCheckContext::Node() { + return TArrayNodeWrapper(this, Node_.Sequence(), Validator_, CheckNodePath_); +} + +TInt64CheckContext::TInt64CheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TInt64Validator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TInt64NodeWrapper TInt64CheckContext::Node() { + return TInt64NodeWrapper(this, Node_, Validator_, CheckNodePath_); +} + +TStringCheckContext::TStringCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TStringValidator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TStringNodeWrapper TStringCheckContext::Node() { + return TStringNodeWrapper(this, Node_, Validator_, CheckNodePath_); +} + +TBoolCheckContext::TBoolCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath, + TBoolValidator* validator) + : TCheckContext(node, checkNodePath), Validator_(validator) {} + +TBoolNodeWrapper TBoolCheckContext::Node() { + return TBoolNodeWrapper(this, Node_, Validator_, CheckNodePath_); +} + +}
\ No newline at end of file diff --git a/ydb/library/yaml_config/validator/validator_checks.h b/ydb/library/yaml_config/validator/validator_checks.h new file mode 100644 index 0000000000..09bd313c1e --- /dev/null +++ b/ydb/library/yaml_config/validator/validator_checks.h @@ -0,0 +1,336 @@ +#pragma once + +#include "validator.h" + +#include <util/generic/maybe.h> + +// context is attached to some path + +// node wrapper should have context, because of ctx["field1"]["field2"]. +// If field1 exists and field2 doesn't, then NodeWrapper would be responsible +// for error notification. And in general it may be good for node wrapper to +// know in what context it exists + +// TODO: make something like this +// (MapContext ctx) { +// ctx.ExpectEqual(ctx["field"], ctx["Another_field"]["2"], "must be equal"); +// // ExpectEqual(const TNodeWrapper&, const TNodeWrapper&) -> checks types and values (only for scalars) +// // if (ctx["field"]) +// ctx["field"] // error if field is not presented? +// if (ctx.Has("field1")) { +// c.ExpectToExist(ctx["field2"]["field3"]); +// //Access to methods by this path(when node is not presented) - error +// } else { +// ctx.ExpectGreater(ctx["field3"].Int64()/*.Value*/, 10); +// } +// } + +namespace NYamlConfig::NValidator { + +class TNodeWrapper; +class TGenericNodeWrapper; +class TMapNodeWrapper; +class TArrayNodeWrapper; +class TInt64NodeWrapper; +class TStringNodeWrapper; +class TBoolNodeWrapper; + +class TCheckContext; +class TGenericCheckContext; +class TMapCheckContext; +class TArrayCheckContext; +class TInt64CheckContext; +class TStringCheckContext; +class TBoolCheckContext; + +namespace NDetail { + +template <typename TThis, typename TContext> +class TValidatorCommonOps; + +template <typename TThis> +class TNodeWrapperCommonOps { +public: + void ThrowIfNullNode(); +}; + +template <typename TThis> +void TNodeWrapperCommonOps<TThis>::ThrowIfNullNode() { + auto& This = static_cast<TThis&>(*this); + if (!This.Node_) { + throw yexception() << + "Node \"" + + This.PathFromCheckNode_ + + "\" is not presented"; + } +} + +} + +class TNodeWrapper : public NDetail::TNodeWrapperCommonOps<TNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TNodeWrapper( + TCheckContext* context, + NFyaml::TNodeRef node, + TValidator* validator, + TMaybe<ENodeType> nodeType, + const TString& pathFromCheckNode); + + TGenericNodeWrapper Generic(); + TMapNodeWrapper Map(); + TArrayNodeWrapper Array(); + TInt64NodeWrapper Int64(); + TStringNodeWrapper String(); + TBoolNodeWrapper Bool(); + + ENodeType Type(); + +protected: + TCheckContext* Context_; + NFyaml::TNodeRef Node_; + TValidator* Validator_; + TMaybe<ENodeType> NodeType_; + TString PathFromCheckNode_; +}; + +class TGenericNodeWrapper : public NDetail::TNodeWrapperCommonOps<TGenericNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +// TODO: make some functionality for generic wrapper +public: + TGenericNodeWrapper( + TGenericCheckContext* context, + NFyaml::TNodeRef node, + TGenericValidator* validator, + const TString& pathFromCheckNode); + +private: + TGenericCheckContext* Context_; + NFyaml::TNodeRef Node_; + TGenericValidator* Validator_; + TString PathFromCheckNode_; +}; + +class TMapNodeWrapper : public NDetail::TNodeWrapperCommonOps<TMapNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TMapNodeWrapper( + TMapCheckContext* context, + NFyaml::TNodeRef node, + TMapValidator* validator, + const TString& pathFromCheckNode); + + TNodeWrapper operator[](const TString& field); + TNodeWrapper At(const TString& field); + bool Has(const TString& field); + +private: + TMapCheckContext* Context_; + NFyaml::TNodeRef Node_; + TMapValidator* Validator_; + TString PathFromCheckNode_; + + NFyaml::TMapping Map(); +}; + +class TArrayNodeWrapper : public NDetail::TNodeWrapperCommonOps<TArrayNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TArrayNodeWrapper( + TArrayCheckContext* context, + NFyaml::TNodeRef node, + TArrayValidator* validator, + const TString& pathFromCheckNode); + + int Length(); + + TNodeWrapper operator[](size_t index); + +private: + TArrayCheckContext* Context_; + NFyaml::TNodeRef Node_; + TArrayValidator* Validator_; + TString PathFromCheckNode_; + + NFyaml::TSequence Sequence(); +}; + +class TInt64NodeWrapper : public NDetail::TNodeWrapperCommonOps<TInt64NodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TInt64NodeWrapper( + TInt64CheckContext* context, + NFyaml::TNodeRef node, + TInt64Validator* validator, + const TString& pathFromCheckNode); + + i64 Value(); + operator i64(); + +private: + TInt64CheckContext* Context_; + NFyaml::TNodeRef Node_; + TInt64Validator* Validator_; + TString PathFromCheckNode_; +}; + +class TStringNodeWrapper : public NDetail::TNodeWrapperCommonOps<TStringNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TStringNodeWrapper( + TStringCheckContext* context, + NFyaml::TNodeRef node, + TStringValidator* validator, + const TString& pathFromCheckNode); + + TString Value(); + operator TString(); + +private: + TStringCheckContext* Context_; + NFyaml::TNodeRef Node_; + TStringValidator* Validator_; + TString PathFromCheckNode_; +}; + +class TBoolNodeWrapper : public NDetail::TNodeWrapperCommonOps<TBoolNodeWrapper> { + template <typename> friend class NDetail::TNodeWrapperCommonOps; + +public: + TBoolNodeWrapper( + TBoolCheckContext* context, + NFyaml::TNodeRef node, + TBoolValidator* validator, + const TString& pathFromCheckNode); + + bool Value(); + operator bool(); + +private: + TBoolCheckContext* Context_; + NFyaml::TNodeRef Node_; + TBoolValidator* Validator_; + TString PathFromCheckNode_; +}; + + +class TCheckContext { + friend class TMapNodeWrapper; // for access to node path + template <typename TThis, typename TContext> + friend class NDetail::TValidatorCommonOps; + template <typename> + friend class NDetail::TNodeWrapperCommonOps; + +public: + TCheckContext( + NFyaml::TNodeRef node, + const TString& checkNodePath); + + void Expect(bool condition, TString error); + void Expect(bool condition); + void AddError(TString error); + + virtual ~TCheckContext() = default; + +protected: + TVector<TString> Errors_; + NFyaml::TNodeRef Node_; + TString CheckNodePath_; + bool someExpectFailed = false; +}; + +class TGenericCheckContext : public TCheckContext { + friend class TGenericValidator; + friend class TGenericNodeWrapper; + +public: + TGenericCheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TGenericValidator* validator); + + TGenericNodeWrapper Node(); + +private: + TGenericValidator* Validator_; +}; + +class TMapCheckContext : public TCheckContext { + friend class TMapValidator; + friend class TMapNodeWrapper; + +public: + TMapCheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TMapValidator* validator); + + TMapNodeWrapper Node(); + +private: + TMapValidator* Validator_; +}; + +class TArrayCheckContext : public TCheckContext { + friend class TArrayValidator; + friend class TArrayNodeWrapper; + +public: + TArrayCheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TArrayValidator* validator); + + TArrayNodeWrapper Node(); + +private: + TArrayValidator* Validator_; +}; + +class TInt64CheckContext : public TCheckContext { + friend class TInt64Validator; + friend class TInt64NodeWrapper; + +public: + TInt64CheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TInt64Validator* validator); + + TInt64NodeWrapper Node(); + +private: + TInt64Validator* Validator_; +}; + +class TStringCheckContext : public TCheckContext { + friend class TStringNodeWrapper; + +public: + TStringCheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TStringValidator* validator); + + TStringNodeWrapper Node(); + +private: + TStringValidator* Validator_; +}; + +class TBoolCheckContext : public TCheckContext { + friend class TBoolNodeWrapper; + +public: + TBoolCheckContext(NFyaml::TNodeRef node, + const TString& checkNodePath, + TBoolValidator* validator); + + TBoolNodeWrapper Node(); + +private: + TBoolValidator* Validator_; +}; + +} // namespace NYamlConfig::NValidator diff --git a/ydb/library/yaml_config/validator/ya.make b/ydb/library/yaml_config/validator/ya.make index d6bf8e3449..d2ce3c08ce 100644 --- a/ydb/library/yaml_config/validator/ya.make +++ b/ydb/library/yaml_config/validator/ya.make @@ -5,6 +5,8 @@ SRCS( validator_builder.cpp validator.h validator.cpp + validator_checks.h + validator_checks.cpp ) PEERDIR( |