#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 #include #include #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// template struct TFluentYsonUnwrapper { using TUnwrapped = T; static TUnwrapped Unwrap(T t) { return std::move(t); } }; //////////////////////////////////////////////////////////////////////////////// struct TFluentYsonVoid { }; template <> struct TFluentYsonUnwrapper { using TUnwrapped = void; static TUnwrapped Unwrap(TFluentYsonVoid) { } }; //////////////////////////////////////////////////////////////////////////////// /// This class is actually a namespace for specific fluent adapter classes. class TFluentYsonBuilder : private TNonCopyable { private: template static void WriteValue(NYT::NYson::IYsonConsumer* consumer, const T& value) { Serialize(value, consumer); } public: class TFluentAny; template class TAny; template class TToAttributes; template class TAttributes; template class TListType; template class TMapType; /// Base class for all fluent adapters. template 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::TUnwrapped; TUnwrappedParent GetUnwrappedParent() { return TFluentYsonUnwrapper::Unwrap(std::move(Parent)); } /// @endcond Doxygen_Suppress }; /// Base class for fluent adapters for fragment of list, map or attributes. template