Browse Source

YT-21310: Introduce CYsonStructSource to remove code duplication in TYsonStruct implementation (now with a fixed bug)
94a777c1510546c0a8a7ef3e3b327add7dfc3813

arkady-e1ppa 10 months ago
parent
commit
b9848536fa

+ 16 - 0
library/cpp/yt/misc/concepts.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include <concepts>
+#include <vector>
 
 namespace NYT {
 
@@ -46,4 +47,19 @@ concept CInvocable = NDetail::TIsInvocable<T, TSignature>::Value;
 
 ////////////////////////////////////////////////////////////////////////////////
 
+template <class V>
+concept CStdVector = requires (V& vec) {
+    [] <class... T> (std::vector<T...>&) { } (vec);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class M>
+concept CAnyMap = requires {
+    typename M::mapped_type;
+    typename M::key_type;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
 } // namespace NYT

+ 4 - 20
yt/yt/core/ytree/yson_struct-inl.h

@@ -264,8 +264,7 @@ TYsonStructRegistrar<TStruct>::operator TYsonStructRegistrar<TBase>()
 
 ////////////////////////////////////////////////////////////////////////////////
 
-template <class T>
-    requires CExternallySerializable<T>
+template <CExternallySerializable T>
 void Serialize(const T& value, NYson::IYsonConsumer* consumer)
 {
     using TSerializer = typename TGetExternalizedYsonStructTraits<T>::TExternalSerializer;
@@ -273,28 +272,13 @@ void Serialize(const T& value, NYson::IYsonConsumer* consumer)
     Serialize(serializer, consumer);
 }
 
-template <class T>
-    requires CExternallySerializable<T>
-void DeserializeExternalized(T& value, INodePtr node, bool postprocess, bool setDefaults)
+template <CExternallySerializable T, CYsonStructSource TSource>
+void Deserialize(T& value, TSource source, bool postprocess, bool setDefaults)
 {
     using TTraits = TGetExternalizedYsonStructTraits<T>;
     using TSerializer = typename TTraits::TExternalSerializer;
     auto serializer = TSerializer::template CreateWritable<T, TSerializer>(value, setDefaults);
-    serializer.Load(node, postprocess, setDefaults);
-}
-
-template <class T>
-    requires CExternallySerializable<T>
-void Deserialize(T& value, INodePtr node)
-{
-    DeserializeExternalized(value, std::move(node), /*postprocess*/ true, /*setDefaults*/ true);
-}
-
-template <class T>
-    requires CExternallySerializable<T>
-void Deserialize(T& value, NYson::TYsonPullParserCursor* cursor)
-{
-    Deserialize(value, NYson::ExtractTo<NYTree::INodePtr>(cursor));
+    serializer.Load(std::move(source), postprocess, setDefaults);
 }
 
 template <class T>

+ 3 - 3
yt/yt/core/ytree/yson_struct.cpp

@@ -51,16 +51,16 @@ void TYsonStructBase::Load(
     INodePtr node,
     bool postprocess,
     bool setDefaults,
-    const TYPath& path)
+    const NYPath::TYPath& path)
 {
-    Meta_->LoadStruct(this, node, postprocess, setDefaults, path);
+    Meta_->LoadStruct(this, std::move(node), postprocess, setDefaults, path);
 }
 
 void TYsonStructBase::Load(
     TYsonPullParserCursor* cursor,
     bool postprocess,
     bool setDefaults,
-    const TYPath& path)
+    const NYPath::TYPath& path)
 {
     Meta_->LoadStruct(this, cursor, postprocess, setDefaults, path);
 }

+ 6 - 25
yt/yt/core/ytree/yson_struct.h

@@ -1,13 +1,14 @@
 #pragma once
 
 #include "node.h"
-#include "yson_struct_enum.h"
+#include "yson_struct_public.h"
 
 #include <yt/yt/core/misc/error.h>
 #include <yt/yt/core/misc/mpl.h>
 #include <yt/yt/core/misc/property.h>
 
 #include <yt/yt/core/yson/public.h>
+
 #include <yt/yt/library/syncmap/map.h>
 
 #include <library/cpp/yt/misc/enum.h>
@@ -62,7 +63,7 @@ public:
     virtual ~TYsonStructBase() = default;
 
     void Load(
-        NYTree::INodePtr node,
+        INodePtr node,
         bool postprocess = true,
         bool setDefaults = true,
         const NYPath::TYPath& path = {});
@@ -292,21 +293,6 @@ private:
 
 ////////////////////////////////////////////////////////////////////////////////
 
-template <class T>
-concept CExternalizedYsonStructTraits = requires {
-    typename T::TExternalSerializer;
-};
-
-template <class T>
-concept CExternallySerializable = requires (T t) {
-    { GetExternalizedYsonStructTraits(t) } -> CExternalizedYsonStructTraits;
-};
-
-template <CExternallySerializable T>
-using TGetExternalizedYsonStructTraits = decltype(GetExternalizedYsonStructTraits(std::declval<T>()));
-
-////////////////////////////////////////////////////////////////////////////////
-
 template <class T>
 TIntrusivePtr<T> CloneYsonStruct(const TIntrusivePtr<const T>& obj);
 template <class T>
@@ -320,15 +306,10 @@ void Serialize(const TYsonStructBase& value, NYson::IYsonConsumer* consumer);
 void Deserialize(TYsonStructBase& value, INodePtr node);
 void Deserialize(TYsonStructBase& value, NYson::TYsonPullParserCursor* cursor);
 
-template <class T>
-    requires CExternallySerializable<T>
+template <CExternallySerializable T>
 void Serialize(const T& value, NYson::IYsonConsumer* consumer);
-template <class T>
-    requires CExternallySerializable<T>
-void Deserialize(T& value, INodePtr node);
-template <class T>
-    requires CExternallySerializable<T>
-void Deserialize(T& value, NYson::TYsonPullParserCursor* cursor);
+template <CExternallySerializable T, CYsonStructSource TSource>
+void Deserialize(T& value, TSource source, bool postprocess = true, bool setDefaults = true);
 
 template <class T>
 TIntrusivePtr<T> UpdateYsonStruct(

+ 218 - 222
yt/yt/core/ytree/yson_struct_detail-inl.h

@@ -35,223 +35,175 @@ concept SupportsDontSerializeDefault =
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Primitive type
 template <class T>
-void LoadFromNode(
-    T& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> /*recursiveUnrecognizedStrategy*/)
-{
-    try {
-        Deserialize(parameter, node);
-    } catch (const std::exception& ex) {
-        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
-            << ex;
-    }
-}
-
-// INodePtr
-template <>
-inline void LoadFromNode(
-    NYTree::INodePtr& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& /*path*/,
-    std::optional<EUnrecognizedStrategy> /*recursiveUnrecognizedStrategy*/)
+T DeserializeMapKey(TStringBuf value)
 {
-    if (!parameter) {
-        parameter = node;
+    if constexpr (TEnumTraits<T>::IsEnum) {
+        return ParseEnum<T>(value);
+    } else if constexpr (std::is_same_v<T, TGuid>) {
+        return TGuid::FromString(value);
+    } else if constexpr (TStrongTypedefTraits<T>::IsStrongTypedef) {
+        return T(DeserializeMapKey<typename TStrongTypedefTraits<T>::TUnderlying>(value));
     } else {
-        parameter = PatchNode(parameter, node);
+        return FromString<T>(value);
     }
 }
 
-// TYsonStruct
-template <CYsonStructDerived T>
-void LoadFromNode(
-    TIntrusivePtr<T>& parameterValue,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+concept CNodePtr = requires (T node) {
+    [] (INodePtr) { } (node);
+};
+
+template <CNodePtr TNodePtr>
+struct TYsonSourceTraits<TNodePtr>
 {
-    if (!parameterValue) {
-        parameterValue = New<T>();
+    static constexpr bool IsValid = true;
+
+    static INodePtr AsNode(TNodePtr& source)
+    {
+        // NRVO.
+        return source;
     }
 
-    if (recursiveUnrecognizedStrategy) {
-        parameterValue->SetUnrecognizedStrategy(*recursiveUnrecognizedStrategy);
+    static bool IsEmpty(TNodePtr& source)
+    {
+        return source->GetType() == ENodeType::Entity;
     }
 
-    parameterValue->Load(node, /*postprocess*/ false, /*setDefaults*/ false, path);
-}
+    static void Advance(TNodePtr& /*source*/)
+    { }
 
-// YsonStructLite
-template <std::derived_from<TYsonStructLite> T>
-void LoadFromNode(
-    T& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> /*recursiveUnrecognizedStrategy*/)
-{
-    try {
-        parameter.Load(node, /*postprocess*/ false, /*setDefaults*/ false);
-    } catch (const std::exception& ex) {
-        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
-            << ex;
+    template <class... TArgs, class TFiller>
+    static void FillVector(TNodePtr& source, std::vector<TArgs...>& vector, TFiller filler)
+    {
+        auto listNode = source->AsList();
+        auto size = listNode->GetChildCount();
+        vector.reserve(size);
+        for (int i = 0; i < size; ++i) {
+            filler(vector, std::move(listNode->GetChildOrThrow(i)));
+        }
     }
-}
 
-// ExternalizedYsonStruct
-template <CExternallySerializable T>
-void LoadFromNode(
-    T& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> /*recursiveUnrecognizedStrategy*/)
-{
-    try {
-        DeserializeExternalized(parameter, node, /*postprocess*/ false, /*setDefaults*/ false);
-    } catch (const std::exception& ex) {
-        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
-            << ex;
+    template <CAnyMap TMap, class TFiller>
+    static void FillMap(TNodePtr& source, TMap& map, TFiller filler)
+    {
+        auto mapNode = source->AsMap();
+
+        // NB: We iterate over temporary object anyway.
+        // Might as well move key/child into the filler
+        for (auto [key, child] : mapNode->GetChildren()) {
+            filler(map, std::move(key), std::move(child));
+        }
     }
-}
+};
 
-// std::optional
-template <class T>
-void LoadFromNode(
-    std::optional<T>& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
+template <>
+struct TYsonSourceTraits<NYson::TYsonPullParserCursor*>
 {
-    if (node->GetType() == NYTree::ENodeType::Entity) {
-        parameter = std::nullopt;
-        return;
+    static constexpr bool IsValid = true;
+
+    static INodePtr AsNode(NYson::TYsonPullParserCursor*& source)
+    {
+        return NYson::ExtractTo<NYTree::INodePtr>(source);
     }
 
-    if (parameter.has_value()) {
-        LoadFromNode(*parameter, node, path, recursiveUnrecognizedStrategy);
-    } else {
-        T value;
-        LoadFromNode(value, node, path, recursiveUnrecognizedStrategy);
-        parameter = std::move(value);
+    static bool IsEmpty(NYson::TYsonPullParserCursor*& source)
+    {
+        return (*source)->GetType() == NYson::EYsonItemType::EntityValue;
     }
-}
 
-// std::vector
-template <class... T>
-void LoadFromNode(
-    std::vector<T...>& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
-{
-    auto listNode = node->AsList();
-    auto size = listNode->GetChildCount();
-    parameter.clear();
-    parameter.reserve(size);
-    for (int i = 0; i < size; ++i) {
-        LoadFromNode(
-            parameter.emplace_back(),
-            listNode->GetChildOrThrow(i),
-            path + "/" + NYPath::ToYPathLiteral(i),
-            recursiveUnrecognizedStrategy);
+    static void Advance(NYson::TYsonPullParserCursor*& source)
+    {
+        source->Next();
     }
-}
 
-template <class T>
-T DeserializeMapKey(TStringBuf value)
-{
-    if constexpr (TEnumTraits<T>::IsEnum) {
-        return ParseEnum<T>(value);
-    } else if constexpr (std::is_same_v<T, TGuid>) {
-        return TGuid::FromString(value);
-    } else if constexpr (TStrongTypedefTraits<T>::IsStrongTypedef) {
-        return T(DeserializeMapKey<typename TStrongTypedefTraits<T>::TUnderlying>(value));
-    } else {
-        return FromString<T>(value);
+    template <class... TArgs, class TFiller>
+    static void FillVector(NYson::TYsonPullParserCursor*& source, std::vector<TArgs...>& vector, TFiller filler)
+    {
+        source->ParseList([&](NYson::TYsonPullParserCursor* cursor) {
+            filler(vector, cursor);
+        });
     }
-}
 
-// For any map.
-template <template <typename...> class Map, class... T, class M = typename Map<T...>::mapped_type>
-void LoadFromNode(
-    Map<T...>& parameter,
-    NYTree::INodePtr node,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
-{
-    auto mapNode = node->AsMap();
-    for (const auto& [key, child] : mapNode->GetChildren()) {
-        M value;
-        LoadFromNode(
-            value,
-            child,
-            path + "/" + NYPath::ToYPathLiteral(key),
-            recursiveUnrecognizedStrategy);
-        parameter[DeserializeMapKey<typename Map<T...>::key_type>(key)] = std::move(value);
+    template <CAnyMap TMap, class TFiller>
+    static void FillMap(NYson::TYsonPullParserCursor*& source, TMap& map, TFiller filler)
+    {
+        source->ParseMap([&] (NYson::TYsonPullParserCursor* cursor) {
+            auto key = ExtractTo<TString>(cursor);
+            filler(map, std::move(key), source);
+        });
     }
-}
+};
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Primitive type or YsonStructLite
-// See LoadFromNode for further specialization.
-template <class T>
-void LoadFromCursor(
-    T& parameter,
-    NYson::TYsonPullParserCursor* cursor,
-    const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
-{
-    LoadFromNode(parameter, NYson::ExtractTo<NYTree::INodePtr>(cursor), path, recursiveUnrecognizedStrategy);
-}
-
-////////////////////////////////////////////////////////////////////////////////
+// NB(arkady-e1ppa): We perform forward declaration of containers
+// so that we can find the correct overload for any composition of them
+// e.g. std::optional<std::vector<T>>.
 
-template <CYsonStructDerived T>
-void LoadFromCursor(
-    TIntrusivePtr<T>& parameterValue,
-    NYson::TYsonPullParserCursor* cursor,
+// std::optional
+template <class T, CYsonStructSource TSource>
+void LoadFromSource(
+    std::optional<T>& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy);
 
-template <class... T>
-void LoadFromCursor(
-    std::vector<T...>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+// std::vector
+template <CStdVector TVector, CYsonStructSource TSource>
+void LoadFromSource(
+    TVector& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy);
 
-// std::optional
-template <class T>
-void LoadFromCursor(
-    std::optional<T>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+// any map.
+template <CAnyMap TMap, CYsonStructSource TSource>
+void LoadFromSource(
+    TMap& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy);
 
-template <template <typename...> class Map, class... T, class M = typename Map<T...>::mapped_type>
-void LoadFromCursor(
-    Map<T...>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+////////////////////////////////////////////////////////////////////////////////
+
+// Primitive type
+template <class T, CYsonStructSource TSource>
+void LoadFromSource(
+    T& parameter,
+    TSource source,
     const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy);
+    std::optional<EUnrecognizedStrategy> /*ignored*/)
+{
+    using TTraits = TYsonSourceTraits<TSource>;
 
-////////////////////////////////////////////////////////////////////////////////
+    try {
+        Deserialize(parameter, TTraits::AsNode(source));
+    } catch (const std::exception& ex) {
+        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
+            << ex;
+    }
+}
 
 // INodePtr
-template <>
-inline void LoadFromCursor(
-    NYTree::INodePtr& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+template <CYsonStructSource TSource>
+void LoadFromSource(
+    INodePtr& parameter,
+    TSource source,
     const NYPath::TYPath& path,
-    std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
+    std::optional<EUnrecognizedStrategy> /*ignored*/)
 {
+    using TTraits = TYsonSourceTraits<TSource>;
+
     try {
-        auto node = NYson::ExtractTo<INodePtr>(cursor);
-        LoadFromNode(parameter, std::move(node), path, recursiveUnrecognizedStrategy);
+        auto node = TTraits::AsNode(source);
+        if (!parameter) {
+            parameter = std::move(node);
+        } else {
+            parameter = PatchNode(parameter, std::move(node));
+        }
     } catch (const std::exception& ex) {
         THROW_ERROR_EXCEPTION("Error loading parameter %v", path)
             << ex;
@@ -259,45 +211,82 @@ inline void LoadFromCursor(
 }
 
 // TYsonStruct
-template <CYsonStructDerived T>
-void LoadFromCursor(
-    TIntrusivePtr<T>& parameterValue,
-    NYson::TYsonPullParserCursor* cursor,
+template <CYsonStructDerived T, CYsonStructSource TSource>
+void LoadFromSource(
+    TIntrusivePtr<T>& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
 {
-    if (!parameterValue) {
-        parameterValue = New<T>();
+    if (!parameter) {
+        parameter = New<T>();
     }
 
     if (recursiveUnrecognizedStrategy) {
-        parameterValue->SetUnrecognizedStrategy(*recursiveUnrecognizedStrategy);
+        parameter->SetUnrecognizedStrategy(*recursiveUnrecognizedStrategy);
     }
 
-    parameterValue->Load(cursor, /*postprocess*/ false, /*setDefaults*/ false, path);
+    parameter->Load(std::move(source), /*postprocess*/ false, /*setDefaults*/ false, path);
+}
+
+// YsonStructLite
+template <std::derived_from<TYsonStructLite> T, CYsonStructSource TSource>
+void LoadFromSource(
+    T& parameter,
+    TSource source,
+    const NYPath::TYPath& path,
+    std::optional<EUnrecognizedStrategy> /*ignored*/)
+{
+    try {
+        parameter.Load(std::move(source), /*postprocess*/ false, /*setDefaults*/ false, path);
+    } catch (const std::exception& ex) {
+        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
+            << ex;
+    }
+}
+
+// ExternalizedYsonStruct
+template <CExternallySerializable T, CYsonStructSource TSource>
+void LoadFromSource(
+    T& parameter,
+    TSource source,
+    const NYPath::TYPath& path,
+    std::optional<EUnrecognizedStrategy> /*ignored*/)
+{
+    try {
+        Deserialize(parameter, std::move(source), /*postprocess*/ false, /*setDefaults*/ false);
+    } catch (const std::exception& ex) {
+        THROW_ERROR_EXCEPTION("Error reading parameter %v", path)
+            << ex;
+    }
 }
 
 // std::optional
-template <class T>
-void LoadFromCursor(
+template <class T, CYsonStructSource TSource>
+void LoadFromSource(
     std::optional<T>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
 {
+    using TTraits = TYsonSourceTraits<TSource>;
+
     try {
-        if ((*cursor)->GetType() == NYson::EYsonItemType::EntityValue) {
+        if (TTraits::IsEmpty(source)) {
             parameter = std::nullopt;
-            cursor->Next();
-        } else {
-            if (parameter.has_value()) {
-                LoadFromCursor(*parameter, cursor, path, recursiveUnrecognizedStrategy);
-            } else {
-                T value;
-                LoadFromCursor(value, cursor, path, recursiveUnrecognizedStrategy);
-                parameter = std::move(value);
-            }
+            TTraits::Advance(source);
+            return;
+        }
+
+        if (parameter.has_value()) {
+            LoadFromSource(*parameter, std::move(source), path, recursiveUnrecognizedStrategy);
+            return;
         }
+
+        T value;
+        LoadFromSource(value, std::move(source), path, recursiveUnrecognizedStrategy);
+        parameter = std::move(value);
+
     } catch (const std::exception& ex) {
         THROW_ERROR_EXCEPTION("Error loading parameter %v", path)
             << ex;
@@ -305,20 +294,23 @@ void LoadFromCursor(
 }
 
 // std::vector
-template <class... T>
-void LoadFromCursor(
-    std::vector<T...>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+template <CStdVector TVector, CYsonStructSource TSource>
+void LoadFromSource(
+    TVector& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
 {
+    using TTraits = TYsonSourceTraits<TSource>;
+
     try {
         parameter.clear();
         int index = 0;
-        cursor->ParseList([&] (NYson::TYsonPullParserCursor* cursor) {
-            LoadFromCursor(
-                parameter.emplace_back(),
-                cursor,
+
+        TTraits::FillVector(source, parameter, [&] (auto& vector, auto elementSource) {
+            LoadFromSource(
+                vector.emplace_back(),
+                elementSource,
                 path + "/" + NYPath::ToYPathLiteral(index),
                 recursiveUnrecognizedStrategy);
             ++index;
@@ -329,24 +321,28 @@ void LoadFromCursor(
     }
 }
 
-// For any map.
-template <template <typename...> class Map, class... T, class M>
-void LoadFromCursor(
-    Map<T...>& parameter,
-    NYson::TYsonPullParserCursor* cursor,
+// any map.
+template <CAnyMap TMap, CYsonStructSource TSource>
+void LoadFromSource(
+    TMap& parameter,
+    TSource source,
     const NYPath::TYPath& path,
     std::optional<EUnrecognizedStrategy> recursiveUnrecognizedStrategy)
 {
+    using TTraits = TYsonSourceTraits<TSource>;
+    // TODO(arkady-e1ppa): Remove "typename" when clang-14 is abolished.
+    using TKey = typename TMap::key_type;
+    using TValue = typename TMap::mapped_type;
+
     try {
-        cursor->ParseMap([&] (NYson::TYsonPullParserCursor* cursor) {
-            auto key = ExtractTo<TString>(cursor);
-            M value;
-            LoadFromCursor(
+        TTraits::FillMap(source, parameter, [&] (TMap& map, const TString& key, auto childSource) {
+            TValue value;
+            LoadFromSource(
                 value,
-                cursor,
+                childSource,
                 path + "/" + NYPath::ToYPathLiteral(key),
                 recursiveUnrecognizedStrategy);
-            parameter[DeserializeMapKey<typename Map<T...>::key_type>(key)] = std::move(value);
+            map[DeserializeMapKey<TKey>(key)] = std::move(value);
         });
     } catch (const std::exception& ex) {
         THROW_ERROR_EXCEPTION("Error loading parameter %v", path)
@@ -440,9 +436,9 @@ inline void PostprocessRecursive(
 }
 
 // std::vector
-template <class T>
+template <CStdVector TVector>
 inline void PostprocessRecursive(
-    std::vector<T>& parameter,
+    TVector& parameter,
     const NYPath::TYPath& path)
 {
     for (size_t i = 0; i < parameter.size(); ++i) {
@@ -453,9 +449,9 @@ inline void PostprocessRecursive(
 }
 
 // any map
-template <template <typename...> class Map, class... T, class M = typename Map<T...>::mapped_type>
+template <CAnyMap TMap>
 inline void PostprocessRecursive(
-    Map<T...>& parameter,
+    TMap& parameter,
     const NYPath::TYPath& path)
 {
     for (auto& [key, value] : parameter) {
@@ -503,15 +499,15 @@ inline void ResetOnLoad(std::optional<T>& parameter)
 }
 
 // std::vector
-template <class T>
-inline void ResetOnLoad(std::vector<T>& parameter)
+template <CStdVector TVector>
+inline void ResetOnLoad(TVector& parameter)
 {
     parameter.clear();
 }
 
 // any map
-template <template <typename...> class Map, class... T, class M = typename Map<T...>::mapped_type>
-inline void ResetOnLoad(Map<T...>& parameter)
+template <CAnyMap TMap>
+inline void ResetOnLoad(TMap& parameter)
 {
     parameter.clear();
 }
@@ -564,7 +560,7 @@ void TYsonStructParameter<TValue>::Load(
         if (ResetOnLoad_) {
             NPrivate::ResetOnLoad(FieldAccessor_->GetValue(self));
         }
-        NPrivate::LoadFromNode(
+        NPrivate::LoadFromSource(
             FieldAccessor_->GetValue(self),
             std::move(node),
             options.Path,
@@ -585,7 +581,7 @@ void TYsonStructParameter<TValue>::Load(
         if (ResetOnLoad_) {
             NPrivate::ResetOnLoad(FieldAccessor_->GetValue(self));
         }
-        NPrivate::LoadFromCursor(
+        NPrivate::LoadFromSource(
             FieldAccessor_->GetValue(self),
             cursor,
             options.Path,
@@ -607,7 +603,7 @@ void TYsonStructParameter<TValue>::SafeLoad(
         TValue oldValue = FieldAccessor_->GetValue(self);
         try {
             FieldAccessor_->GetValue(self) = TValue();
-            NPrivate::LoadFromNode(
+            NPrivate::LoadFromSource(
                 FieldAccessor_->GetValue(self),
                 node,
                 options.Path,

+ 33 - 1
yt/yt/core/ytree/yson_struct_detail.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "yson_struct_enum.h"
+#include "yson_struct_public.h"
 
 #include <yt/yt/core/yson/public.h>
 #include <yt/yt/core/ypath/public.h>
@@ -12,6 +12,38 @@ namespace NYT::NYTree {
 
 ////////////////////////////////////////////////////////////////////////////////
 
+namespace NPrivate {
+
+// Least common denominator between INodePtr
+// and TYsonPullParserCursor.
+// Maybe something else in the future.
+template <class T>
+struct TYsonSourceTraits
+{
+    static constexpr bool IsValid = false;
+
+    static INodePtr AsNode(T& source)
+        requires false;
+
+    static bool IsEmpty(T& source)
+        requires false;
+
+    static void Advance(T& source)
+        requires false;
+
+    template <CStdVector TVector, class TFiller>
+    static void FillVector(T& source, TVector& vector, TFiller filler)
+        requires false;
+
+    template <CAnyMap TMap, class TFiller>
+    static void FillMap(T& source, TMap& map, TFiller filler)
+        requires false;
+};
+
+} // namespace NPrivate
+
+////////////////////////////////////////////////////////////////////////////////
+
 template <class TStruct, class TValue>
 using TYsonStructField = TValue(TStruct::*);
 

+ 0 - 21
yt/yt/core/ytree/yson_struct_enum.h

@@ -1,21 +0,0 @@
-#pragma once
-
-#include <library/cpp/yt/memory/serialize.h>
-
-#include <library/cpp/yt/misc/enum.h>
-
-namespace NYT::NYTree {
-
-///////////////////////////////////////////////////////////////////////////////
-
-DEFINE_ENUM(EUnrecognizedStrategy,
-    (Drop)
-    (Keep)
-    (KeepRecursive)
-    (Throw)
-    (ThrowRecursive)
-);
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NYTree

+ 48 - 0
yt/yt/core/ytree/yson_struct_public.h

@@ -0,0 +1,48 @@
+#pragma once
+
+#include <library/cpp/yt/memory/serialize.h>
+
+#include <library/cpp/yt/misc/enum.h>
+
+namespace NYT::NYTree {
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEFINE_ENUM(EUnrecognizedStrategy,
+    (Drop)
+    (Keep)
+    (KeepRecursive)
+    (Throw)
+    (ThrowRecursive)
+);
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+concept CExternalizedYsonStructTraits = requires {
+    typename T::TExternalSerializer;
+};
+
+template <class T>
+concept CExternallySerializable = requires (T t) {
+    { GetExternalizedYsonStructTraits(t) } -> CExternalizedYsonStructTraits;
+};
+
+template <CExternallySerializable T>
+using TGetExternalizedYsonStructTraits = decltype(GetExternalizedYsonStructTraits(std::declval<T>()));
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace NPrivate {
+
+template <class T>
+struct TYsonSourceTraits;
+
+} // namespace NPrivate
+
+template <class T>
+concept CYsonStructSource = NPrivate::TYsonSourceTraits<T>::IsValid;
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT::NYTree