diff options
-rw-r--r-- | yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp | 31 | ||||
-rw-r--r-- | yt/yt/core/ytree/yson_struct_update-inl.h | 69 | ||||
-rw-r--r-- | yt/yt/core/ytree/yson_struct_update.h | 34 |
3 files changed, 128 insertions, 6 deletions
diff --git a/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp b/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp index 0fb160fdcd..fd82265dd7 100644 --- a/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp +++ b/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp @@ -176,5 +176,36 @@ TEST(TUpdateYsonStructTest, Nested) EXPECT_EQ(updatedCommand, "sort"); } +TEST(TUpdateYsonStructTest, Validate) +{ + auto oldSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=pool;}"))); + auto longPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=new_pool;}"))); + auto shortPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=p;}"))); + + std::string updatedPool; + + auto configurator = TConfigurator<TSpecWithPool>(); + configurator.Field("pool", &TSpecWithPool::Pool) + .Validator(BIND([&] (const std::string& newPool) { + THROW_ERROR_EXCEPTION_IF( + newPool.size() > 4, + "Pool name too long"); + })) + .Updater(BIND([&] (const std::string& newPool) { + updatedPool = newPool; + })); + + auto sealed = std::move(configurator).Seal(); + + EXPECT_THROW_WITH_SUBSTRING( + sealed.Validate(oldSpec, longPoolSpec), + "Pool name too long"); + + sealed.Validate(oldSpec, shortPoolSpec); + sealed.Update(oldSpec, shortPoolSpec); + + EXPECT_EQ(updatedPool, "p"); +} + } // namespace } // namespace NYT::NYTree diff --git a/yt/yt/core/ytree/yson_struct_update-inl.h b/yt/yt/core/ytree/yson_struct_update-inl.h index 3ba88ec853..c6ae5f1ead 100644 --- a/yt/yt/core/ytree/yson_struct_update-inl.h +++ b/yt/yt/core/ytree/yson_struct_update-inl.h @@ -49,9 +49,27 @@ struct TUnwrapYsonStructIntrusivePtr<TIntrusivePtr<T>> //////////////////////////////////////////////////////////////////////////////// template <class TValue> +TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&, const TValue&)> validator) +{ + VerifyEmptyValidator(); + Validator_ = validator; + return *this; +} + +template <class TValue> +TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&)> validator) +{ + VerifyEmptyValidator(); + Validator_ = BIND_NO_PROPAGATE([validator = std::move(validator)] (const TValue& /*oldValue*/, const TValue& newValue) { + validator(std::move(newValue)); + }); + return *this; +} + +template <class TValue> TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&, const TValue&)> updater) { - VerifyEmpty(); + VerifyEmptyUpdater(); Updater_ = updater; return *this; } @@ -59,7 +77,7 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const T template <class TValue> TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&)> updater) { - VerifyEmpty(); + VerifyEmptyUpdater(); Updater_ = BIND_NO_PROPAGATE([updater = std::move(updater)] (const TValue& /*oldValue*/, const TValue& newValue) { updater(std::move(newValue)); }); @@ -90,7 +108,7 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::NestedUpdater( TUnwrappedValue, typename NDetail::TUnwrapYsonStructIntrusivePtr<TValue>::TStruct>); - VerifyEmpty(); + VerifyEmptyUpdater(); auto configurator = configureCallback(); Updater_ = BIND_NO_PROPAGATE([configurator = std::move(configurator)] (const TValue& oldValue, const TValue& newValue) { configurator.Update(oldValue, newValue); @@ -99,6 +117,23 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::NestedUpdater( } template <class TValue> +void TFieldRegistrar<TValue>::DoValidate( + IYsonStructParameterPtr parameter, + TYsonStructBase* oldStruct, + TYsonStructBase* newStruct) const +{ + if (!Validator_) { + return; + } + + auto typedParameter = DynamicPointerCast<TYsonStructParameter<TValue>>(parameter); + YT_VERIFY(typedParameter); + Validator_( + typedParameter->GetValue(oldStruct), + typedParameter->GetValue(newStruct)); +} + +template <class TValue> void TFieldRegistrar<TValue>::DoUpdate( IYsonStructParameterPtr parameter, TYsonStructBase* oldStruct, @@ -116,7 +151,13 @@ void TFieldRegistrar<TValue>::DoUpdate( } template <class TValue> -void TFieldRegistrar<TValue>::VerifyEmpty() const +void TFieldRegistrar<TValue>::VerifyEmptyValidator() const +{ + YT_VERIFY(!Validator_); +} + +template <class TValue> +void TFieldRegistrar<TValue>::VerifyEmptyUpdater() const { YT_VERIFY(!Updater_); } @@ -169,6 +210,7 @@ TSealedConfigurator<TStruct> TConfigurator<TStruct>::Seal() && { return std::move(*this); } + //////////////////////////////////////////////////////////////////////////////// template <CYsonStructDerived TStruct> @@ -177,10 +219,27 @@ TSealedConfigurator<TStruct>::TSealedConfigurator(TConfigurator<TStruct> configu { } template <CYsonStructDerived TStruct> +void TSealedConfigurator<TStruct>::Validate( + TIntrusivePtr<TStruct> oldStruct, + TIntrusivePtr<TStruct> newStruct) const +{ + Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoValidate); +} + +template <CYsonStructDerived TStruct> void TSealedConfigurator<TStruct>::Update( TIntrusivePtr<TStruct> oldStruct, TIntrusivePtr<TStruct> newStruct) const { + Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoUpdate); +} + +template <CYsonStructDerived TStruct> +void TSealedConfigurator<TStruct>::Do( + TIntrusivePtr<TStruct> oldStruct, + TIntrusivePtr<TStruct> newStruct, + TFieldRegistrarMethod fieldMethod) const +{ const auto* meta = oldStruct->GetMeta(); YT_VERIFY(meta == newStruct->GetMeta()); const auto& parameterToFieldRegistrar = RegisteredFields_->ParameterToFieldRegistrar; @@ -194,7 +253,7 @@ void TSealedConfigurator<TStruct>::Update( if (fieldDescIter == parameterToFieldRegistrar.end()) { THROW_ERROR_EXCEPTION("Field %Qv is not marked as updatable, but was changed", name); } else { - fieldDescIter->second->DoUpdate(parameter, oldStruct.Get(), newStruct.Get()); + (*(fieldDescIter->second).*fieldMethod)(parameter, oldStruct.Get(), newStruct.Get()); } } } diff --git a/yt/yt/core/ytree/yson_struct_update.h b/yt/yt/core/ytree/yson_struct_update.h index 4b472021d1..7a517d6f57 100644 --- a/yt/yt/core/ytree/yson_struct_update.h +++ b/yt/yt/core/ytree/yson_struct_update.h @@ -26,6 +26,11 @@ DECLARE_REFCOUNTED_STRUCT(TRegisteredFieldDirectory); struct IFieldRegistrar : public TRefCounted { + virtual void DoValidate( + IYsonStructParameterPtr parameter, + TYsonStructBase* oldStruct, + TYsonStructBase* newStruct) const = 0; + virtual void DoUpdate( IYsonStructParameterPtr parameter, TYsonStructBase* oldStruct, @@ -42,6 +47,12 @@ class TFieldRegistrar : public IFieldRegistrar { public: + // Registers validator that accepts old and new values as arguments. + TFieldRegistrar& Validator(TCallback<void(const TValue&, const TValue&)> validator); + + // Registers validator that accepts only new value as an argument. + TFieldRegistrar& Validator(TCallback<void(const TValue&)> validator); + // Registers updater that accepts old and new values as arguments. TFieldRegistrar& Updater(TCallback<void(const TValue&, const TValue&)> updater); @@ -58,10 +69,17 @@ public: TYsonStructBase* oldStruct, TYsonStructBase* newStruct) const override; + void DoValidate( + IYsonStructParameterPtr parameter, + TYsonStructBase* oldStruct, + TYsonStructBase* newStruct) const override; + private: - void VerifyEmpty() const; + void VerifyEmptyUpdater() const; + void VerifyEmptyValidator() const; TCallback<void(const TValue&, const TValue&)> Updater_; + TCallback<void(const TValue&, const TValue&)> Validator_; }; //////////////////////////////////////////////////////////////////////////////// @@ -114,11 +132,25 @@ class TSealedConfigurator public: TSealedConfigurator(TConfigurator<TStruct> configurator); + void Validate( + TIntrusivePtr<TStruct> oldStruct, + TIntrusivePtr<TStruct> newStruct) const; + void Update( TIntrusivePtr<TStruct> oldStruct, TIntrusivePtr<TStruct> newStruct) const; private: + using TFieldRegistrarMethod = void(NDetail::IFieldRegistrar::*)( + IYsonStructParameterPtr parameter, + TYsonStructBase* oldStruct, + TYsonStructBase* newStruct) const; + + void Do( + TIntrusivePtr<TStruct> oldStruct, + TIntrusivePtr<TStruct> newStruct, + TFieldRegistrarMethod fieldMethod) const; + NDetail::TRegisteredFieldDirectoryPtr RegisteredFields_; }; |