diff options
author | max42 <max42@yandex-team.com> | 2023-07-29 00:02:16 +0300 |
---|---|---|
committer | max42 <max42@yandex-team.com> | 2023-07-29 00:02:16 +0300 |
commit | 73b89de71748a21e102d27b9f3ed1bf658766cb5 (patch) | |
tree | 188bbd2d622fa91cdcbb1b6d6d77fbc84a0646f5 /yt/cpp/mapreduce/interface/fluent.h | |
parent | 528e321bcc2a2b67b53aeba58c3bd88305a141ee (diff) | |
download | ydb-73b89de71748a21e102d27b9f3ed1bf658766cb5.tar.gz |
YT-19210: expose YQL shared library for YT.
After this, a new target libyqlplugin.so appears. in open-source cmake build.
Diff in open-source YDB repo looks like the following: https://paste.yandex-team.ru/f302bdb4-7ef2-4362-91c7-6ca45f329264
Diffstat (limited to 'yt/cpp/mapreduce/interface/fluent.h')
-rw-r--r-- | yt/cpp/mapreduce/interface/fluent.h | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/yt/cpp/mapreduce/interface/fluent.h b/yt/cpp/mapreduce/interface/fluent.h new file mode 100644 index 0000000000..8ca6e86336 --- /dev/null +++ b/yt/cpp/mapreduce/interface/fluent.h @@ -0,0 +1,678 @@ +#pragma once + +/// +/// @file yt/cpp/mapreduce/interface/fluent.h +/// +/// Adapters for working with @ref NYson::IYsonConsumer in a structured way, with compile-time syntax checks. +/// +/// The following documentation is copied verbatim from `yt/core/ytree/fluent.h`. +/// +/// WHAT IS THIS +/// +/// Fluent adapters encapsulate invocation of IYsonConsumer methods in a +/// convenient structured manner. Key advantage of fluent-like code is that +/// attempt of building syntactically incorrect YSON structure will result +/// in a compile-time error. +/// +/// Each fluent object is associated with a context that defines possible YSON +/// tokens that may appear next. For example, TFluentMap is a fluent object +/// that corresponds to a location within YSON map right before a key-value +/// pair or the end of the map. +/// +/// More precisely, each object that may be obtained by a sequence of fluent +/// method calls has the full history of its enclosing YSON composite types in +/// its single template argument hereinafter referred to as TParent. This allows +/// us not to forget the original context after opening and closing the embedded +/// composite structure. +/// +/// It is possible to invoke a separate YSON building procedure by calling +/// one of convenience Do* methods. There are two possibilities here: it is +/// possible to delegate invocation context either as a fluent object (like +/// TFluentMap, TFluentList, TFluentAttributes or TFluentAny) or as a raw +/// IYsonConsumer*. The latter is discouraged since it is impossible to check +/// if a given side-built YSON structure fits current fluent context. +/// For example it is possible to call Do() method inside YSON map passing +/// consumer to a procedure that will treat context like it is in a list. +/// Passing typed fluent builder saves you from such a misbehaviour. +/// +/// TFluentXxx corresponds to an internal class of TXxx +/// without any history hidden in template argument. It allows you to +/// write procedures of form: +/// +/// void BuildSomeAttributesInYson(TFluentMap fluent) { ... } +/// +/// without thinking about the exact way how this procedure is nested in other +/// procedures. +/// +/// An important notation: we will refer to a function whose first argument +/// is TFluentXxx as TFuncXxx. +/// +/// +/// BRIEF LIST OF AVAILABLE METHODS +/// +/// Only the most popular methods are covered here. Refer to the code for the +/// rest of them. +/// +/// TAny: +/// * Value(T value) -> TParent, serialize `value` using underlying consumer. +/// T should be such that free function Serialize(NYson::IYsonConsumer*, const T&) is +/// defined; +/// * BeginMap() -> TFluentMap, open map; +/// * BeginList() -> TFluentList, open list; +/// * BeginAttributes() -> TFluentAttributes, open attributes; +/// +/// * Do(TFuncAny func) -> TAny, delegate invocation to a separate procedure. +/// * DoIf(bool condition, TFuncAny func) -> TAny, same as Do() but invoke +/// `func` only if `condition` is true; +/// * DoFor(TCollection collection, TFuncAny func) -> TAny, same as Do() +/// but iterate over `collection` and pass each of its elements as a second +/// argument to `func`. Instead of passing a collection you may it is possible +/// to pass two iterators as an argument; +/// +/// * DoMap(TFuncMap func) -> TAny, open a map, delegate invocation to a separate +/// procedure and close map; +/// * DoMapFor(TCollection collection, TFuncMap func) -> TAny, open a map, iterate +/// over `collection` and pass each of its elements as a second argument to `func` +/// and close map; +/// * DoList(TFuncList func) -> TAny, same as DoMap(); +/// * DoListFor(TCollection collection, TFuncList func) -> TAny; same as DoMapFor(). +/// +/// +/// TFluentMap: +/// * Item(TStringBuf key) -> TAny, open an element keyed with `key`; +/// * EndMap() -> TParent, close map; +/// * Do(TFuncMap func) -> TFluentMap, same as Do() for TAny; +/// * DoIf(bool condition, TFuncMap func) -> TFluentMap, same as DoIf() for TAny; +/// * DoFor(TCollection collection, TFuncMap func) -> TFluentMap, same as DoFor() for TAny. +/// +/// +/// TFluentList: +/// * Item() -> TAny, open an new list element; +/// * EndList() -> TParent, close list; +/// * Do(TFuncList func) -> TFluentList, same as Do() for TAny; +/// * DoIf(bool condition, TFuncList func) -> TFluentList, same as DoIf() for TAny; +/// * DoFor(TCollection collection, TListMap func) -> TFluentList, same as DoFor() for TAny. +/// +/// +/// TFluentAttributes: +/// * Item(TStringBuf key) -> TAny, open an element keyed with `key`. +/// * EndAttributes() -> TParentWithoutAttributes, close attributes. Note that +/// this method leads to a context that is forces not to have attributes, +/// preventing us from putting attributes twice before an object. +/// * Do(TFuncAttributes func) -> TFluentAttributes, same as Do() for TAny; +/// * DoIf(bool condition, TFuncAttributes func) -> TFluentAttributes, same as DoIf() +/// for TAny; +/// * DoFor(TCollection collection, TListAttributes func) -> TFluentAttributes, same as DoFor() +/// for TAny. +/// + + +#include "common.h" +#include "serialize.h" + +#include <library/cpp/yson/node/serialize.h> +#include <library/cpp/yson/node/node_builder.h> + +#include <library/cpp/yson/consumer.h> +#include <library/cpp/yson/writer.h> + +#include <util/generic/noncopyable.h> +#include <util/generic/ptr.h> +#include <util/stream/str.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +struct TFluentYsonUnwrapper +{ + using TUnwrapped = T; + + static TUnwrapped Unwrap(T t) + { + return std::move(t); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct TFluentYsonVoid +{ }; + +template <> +struct TFluentYsonUnwrapper<TFluentYsonVoid> +{ + using TUnwrapped = void; + + static TUnwrapped Unwrap(TFluentYsonVoid) + { } +}; + +//////////////////////////////////////////////////////////////////////////////// + +/// This class is actually a namespace for specific fluent adapter classes. +class TFluentYsonBuilder + : private TNonCopyable +{ +private: + template <class T> + static void WriteValue(NYT::NYson::IYsonConsumer* consumer, const T& value) + { + Serialize(value, consumer); + } + +public: + class TFluentAny; + template <class TParent> class TAny; + template <class TParent> class TToAttributes; + template <class TParent> class TAttributes; + template <class TParent> class TListType; + template <class TParent> class TMapType; + + /// Base class for all fluent adapters. + template <class TParent> + class TFluentBase + { + public: + /// Implicit conversion to yson consumer + operator NYT::NYson::IYsonConsumer* () const + { + return Consumer; + } + + protected: + /// @cond Doxygen_Suppress + NYT::NYson::IYsonConsumer* Consumer; + TParent Parent; + + TFluentBase(NYT::NYson::IYsonConsumer* consumer, TParent parent) + : Consumer(consumer) + , Parent(std::move(parent)) + { } + + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + TUnwrappedParent GetUnwrappedParent() + { + return TFluentYsonUnwrapper<TParent>::Unwrap(std::move(Parent)); + } + /// @endcond Doxygen_Suppress + }; + + /// Base class for fluent adapters for fragment of list, map or attributes. + template <template <class TParent> class TThis, class TParent> + class TFluentFragmentBase + : public TFluentBase<TParent> + { + public: + using TDeepThis = TThis<TParent>; + using TShallowThis = TThis<TFluentYsonVoid>; + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + explicit TFluentFragmentBase(NYT::NYson::IYsonConsumer* consumer, TParent parent = TParent()) + : TFluentBase<TParent>(consumer, std::move(parent)) + { } + + /// Delegate invocation to a separate procedure. + template <class TFunc> + TDeepThis& Do(const TFunc& func) + { + func(TShallowThis(this->Consumer)); + return *static_cast<TDeepThis*>(this); + } + + /// Conditionally delegate invocation to a separate procedure. + template <class TFunc> + TDeepThis& DoIf(bool condition, const TFunc& func) + { + if (condition) { + func(TShallowThis(this->Consumer)); + } + return *static_cast<TDeepThis*>(this); + } + + /// Calls `func(*this, element)` for each `element` in range `[begin, end)`. + template <class TFunc, class TIterator> + TDeepThis& DoFor(const TIterator& begin, const TIterator& end, const TFunc& func) + { + for (auto current = begin; current != end; ++current) { + func(TShallowThis(this->Consumer), current); + } + return *static_cast<TDeepThis*>(this); + } + + /// Calls `func(*this, element)` for each `element` in `collection`. + template <class TFunc, class TCollection> + TDeepThis& DoFor(const TCollection& collection, const TFunc& func) + { + for (const auto& item : collection) { + func(TShallowThis(this->Consumer), item); + } + return *static_cast<TDeepThis*>(this); + } + + }; + + /// Fluent adapter of a value without attributes. + template <class TParent> + class TAnyWithoutAttributes + : public TFluentBase<TParent> + { + public: + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + TAnyWithoutAttributes(NYT::NYson::IYsonConsumer* consumer, TParent parent) + : TFluentBase<TParent>(consumer, std::move(parent)) + { } + + /// Pass `value` to underlying consumer. + template <class T> + TUnwrappedParent Value(const T& value) + { + WriteValue(this->Consumer, value); + return this->GetUnwrappedParent(); + } + + /// Call `OnEntity()` of underlying consumer. + TUnwrappedParent Entity() + { + this->Consumer->OnEntity(); + return this->GetUnwrappedParent(); + } + + /// Serialize `collection` to underlying consumer as a list. + template <class TCollection> + TUnwrappedParent List(const TCollection& collection) + { + this->Consumer->OnBeginList(); + for (const auto& item : collection) { + this->Consumer->OnListItem(); + WriteValue(this->Consumer, item); + } + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + + /// Serialize maximum `maxSize` elements of `collection` to underlying consumer as a list. + template <class TCollection> + TUnwrappedParent ListLimited(const TCollection& collection, size_t maxSize) + { + this->Consumer->OnBeginAttributes(); + this->Consumer->OnKeyedItem("count"); + this->Consumer->OnInt64Scalar(collection.size()); + this->Consumer->OnEndAttributes(); + this->Consumer->OnBeginList(); + size_t printedSize = 0; + for (const auto& item : collection) { + if (printedSize >= maxSize) + break; + this->Consumer->OnListItem(); + WriteValue(this->Consumer, item); + ++printedSize; + } + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + + /// Open a list. + TListType<TParent> BeginList() + { + this->Consumer->OnBeginList(); + return TListType<TParent>(this->Consumer, this->Parent); + } + + /// Open a list, delegate invocation to `func`, then close the list. + template <class TFunc> + TUnwrappedParent DoList(const TFunc& func) + { + this->Consumer->OnBeginList(); + func(TListType<TFluentYsonVoid>(this->Consumer)); + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + + /// Open a list, call `func(*this, element)` for each `element` of range, then close the list. + template <class TFunc, class TIterator> + TUnwrappedParent DoListFor(const TIterator& begin, const TIterator& end, const TFunc& func) + { + this->Consumer->OnBeginList(); + for (auto current = begin; current != end; ++current) { + func(TListType<TFluentYsonVoid>(this->Consumer), current); + } + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + + /// Open a list, call `func(*this, element)` for each `element` of `collection`, then close the list. + template <class TFunc, class TCollection> + TUnwrappedParent DoListFor(const TCollection& collection, const TFunc& func) + { + this->Consumer->OnBeginList(); + for (const auto& item : collection) { + func(TListType<TFluentYsonVoid>(this->Consumer), item); + } + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + + /// Open a map. + TMapType<TParent> BeginMap() + { + this->Consumer->OnBeginMap(); + return TMapType<TParent>(this->Consumer, this->Parent); + } + + /// Open a map, delegate invocation to `func`, then close the map. + template <class TFunc> + TUnwrappedParent DoMap(const TFunc& func) + { + this->Consumer->OnBeginMap(); + func(TMapType<TFluentYsonVoid>(this->Consumer)); + this->Consumer->OnEndMap(); + return this->GetUnwrappedParent(); + } + + /// Open a map, call `func(*this, element)` for each `element` of range, then close the map. + template <class TFunc, class TIterator> + TUnwrappedParent DoMapFor(const TIterator& begin, const TIterator& end, const TFunc& func) + { + this->Consumer->OnBeginMap(); + for (auto current = begin; current != end; ++current) { + func(TMapType<TFluentYsonVoid>(this->Consumer), current); + } + this->Consumer->OnEndMap(); + return this->GetUnwrappedParent(); + } + + /// Open a map, call `func(*this, element)` for each `element` of `collection`, then close the map. + template <class TFunc, class TCollection> + TUnwrappedParent DoMapFor(const TCollection& collection, const TFunc& func) + { + this->Consumer->OnBeginMap(); + for (const auto& item : collection) { + func(TMapType<TFluentYsonVoid>(this->Consumer), item); + } + this->Consumer->OnEndMap(); + return this->GetUnwrappedParent(); + } + }; + + /// Fluent adapter of any value. + template <class TParent> + class TAny + : public TAnyWithoutAttributes<TParent> + { + public: + using TBase = TAnyWithoutAttributes<TParent>; + + explicit TAny(NYT::NYson::IYsonConsumer* consumer, TParent parent) + : TBase(consumer, std::move(parent)) + { } + + /// Open attributes. + TAttributes<TBase> BeginAttributes() + { + this->Consumer->OnBeginAttributes(); + return TAttributes<TBase>( + this->Consumer, + TBase(this->Consumer, this->Parent)); + } + }; + + /// Fluent adapter of attributes fragment (the inside part of attributes). + template <class TParent = TFluentYsonVoid> + class TAttributes + : public TFluentFragmentBase<TAttributes, TParent> + { + public: + using TThis = TAttributes<TParent>; + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + explicit TAttributes(NYT::NYson::IYsonConsumer* consumer, TParent parent = TParent()) + : TFluentFragmentBase<TFluentYsonBuilder::TAttributes, TParent>(consumer, std::move(parent)) + { } + + /// Pass attribute key to underlying consumer. + TAny<TThis> Item(const TStringBuf& key) + { + this->Consumer->OnKeyedItem(key); + return TAny<TThis>(this->Consumer, *this); + } + + /// Pass attribute key to underlying consumer. + template <size_t Size> + TAny<TThis> Item(const char (&key)[Size]) + { + return Item(TStringBuf(key, Size - 1)); + } + + //TODO: from TNode + + /// Close the attributes. + TUnwrappedParent EndAttributes() + { + this->Consumer->OnEndAttributes(); + return this->GetUnwrappedParent(); + } + }; + + /// Fluent adapter of list fragment (the inside part of a list). + template <class TParent = TFluentYsonVoid> + class TListType + : public TFluentFragmentBase<TListType, TParent> + { + public: + using TThis = TListType<TParent>; + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + explicit TListType(NYT::NYson::IYsonConsumer* consumer, TParent parent = TParent()) + : TFluentFragmentBase<TFluentYsonBuilder::TListType, TParent>(consumer, std::move(parent)) + { } + + /// Call `OnListItem()` of underlying consumer. + TAny<TThis> Item() + { + this->Consumer->OnListItem(); + return TAny<TThis>(this->Consumer, *this); + } + + // TODO: from TNode + + /// Close the list. + TUnwrappedParent EndList() + { + this->Consumer->OnEndList(); + return this->GetUnwrappedParent(); + } + }; + + /// Fluent adapter of map fragment (the inside part of a map). + template <class TParent = TFluentYsonVoid> + class TMapType + : public TFluentFragmentBase<TMapType, TParent> + { + public: + using TThis = TMapType<TParent>; + using TUnwrappedParent = typename TFluentYsonUnwrapper<TParent>::TUnwrapped; + + explicit TMapType(NYT::NYson::IYsonConsumer* consumer, TParent parent = TParent()) + : TFluentFragmentBase<TFluentYsonBuilder::TMapType, TParent>(consumer, std::move(parent)) + { } + + /// Pass map key to underlying consumer. + template <size_t Size> + TAny<TThis> Item(const char (&key)[Size]) + { + return Item(TStringBuf(key, Size - 1)); + } + + /// Pass map key to underlying consumer. + TAny<TThis> Item(const TStringBuf& key) + { + this->Consumer->OnKeyedItem(key); + return TAny<TThis>(this->Consumer, *this); + } + + // TODO: from TNode + + /// Close the map. + TUnwrappedParent EndMap() + { + this->Consumer->OnEndMap(); + return this->GetUnwrappedParent(); + } + }; + +}; + +//////////////////////////////////////////////////////////////////////////////// + +/// Builder representing any value. +using TFluentAny = TFluentYsonBuilder::TAny<TFluentYsonVoid>; + +/// Builder representing the inside of a list (list fragment). +using TFluentList = TFluentYsonBuilder::TListType<TFluentYsonVoid>; + +/// Builder representing the inside of a map (map fragment). +using TFluentMap = TFluentYsonBuilder::TMapType<TFluentYsonVoid>; + +/// Builder representing the inside of attributes. +using TFluentAttributes = TFluentYsonBuilder::TAttributes<TFluentYsonVoid>; + +//////////////////////////////////////////////////////////////////////////////// + +/// Create a fluent adapter to invoke methods of `consumer`. +static inline TFluentAny BuildYsonFluently(NYT::NYson::IYsonConsumer* consumer) +{ + return TFluentAny(consumer, TFluentYsonVoid()); +} + +/// Create a fluent adapter to invoke methods of `consumer` describing the contents of a list. +static inline TFluentList BuildYsonListFluently(NYT::NYson::IYsonConsumer* consumer) +{ + return TFluentList(consumer); +} + +/// Create a fluent adapter to invoke methods of `consumer` describing the contents of a map. +static inline TFluentMap BuildYsonMapFluently(NYT::NYson::IYsonConsumer* consumer) +{ + return TFluentMap(consumer); +} + +//////////////////////////////////////////////////////////////////////////////// + +class TFluentYsonWriterState + : public TThrRefBase +{ +public: + using TValue = TString; + + explicit TFluentYsonWriterState(::NYson::EYsonFormat format) + : Writer(&Output, format) + { } + + TString GetValue() + { + return Output.Str(); + } + + NYT::NYson::IYsonConsumer* GetConsumer() + { + return &Writer; + } + +private: + TStringStream Output; + ::NYson::TYsonWriter Writer; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class TFluentYsonBuilderState + : public TThrRefBase +{ +public: + using TValue = TNode; + + explicit TFluentYsonBuilderState() + : Builder(&Node) + { } + + TNode GetValue() + { + return std::move(Node); + } + + NYT::NYson::IYsonConsumer* GetConsumer() + { + return &Builder; + } + +private: + TNode Node; + TNodeBuilder Builder; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TState> +class TFluentYsonHolder +{ +public: + explicit TFluentYsonHolder(::TIntrusivePtr<TState> state) + : State(state) + { } + + ::TIntrusivePtr<TState> GetState() const + { + return State; + } + +private: + ::TIntrusivePtr<TState> State; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TState> +struct TFluentYsonUnwrapper< TFluentYsonHolder<TState> > +{ + using TUnwrapped = typename TState::TValue; + + static TUnwrapped Unwrap(const TFluentYsonHolder<TState>& holder) + { + return std::move(holder.GetState()->GetValue()); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TState> +TFluentYsonBuilder::TAny<TFluentYsonHolder<TState>> +BuildYsonFluentlyWithState(::TIntrusivePtr<TState> state) +{ + return TFluentYsonBuilder::TAny<TFluentYsonHolder<TState>>( + state->GetConsumer(), + TFluentYsonHolder<TState>(state)); +} + +/// Create a fluent adapter returning a `TString` with corresponding YSON when construction is finished. +inline TFluentYsonBuilder::TAny<TFluentYsonHolder<TFluentYsonWriterState>> +BuildYsonStringFluently(::NYson::EYsonFormat format = ::NYson::EYsonFormat::Text) +{ + ::TIntrusivePtr<TFluentYsonWriterState> state(new TFluentYsonWriterState(format)); + return BuildYsonFluentlyWithState(state); +} + +/// Create a fluent adapter returning a @ref NYT::TNode when construction is finished. +inline TFluentYsonBuilder::TAny<TFluentYsonHolder<TFluentYsonBuilderState>> +BuildYsonNodeFluently() +{ + ::TIntrusivePtr<TFluentYsonBuilderState> state(new TFluentYsonBuilderState); + return BuildYsonFluentlyWithState(state); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT |