#pragma once ///@file #include "serialise.hh" namespace nix { class Store; struct Source; // items being serialized class StorePath; struct ContentAddress; struct DrvOutput; struct Realisation; /** * Shared serializers between the worker protocol, serve protocol, and a * few others. * * This `struct` is basically just a `namespace`; We use a type rather * than a namespace just so we can use it as a template argument. */ struct CommonProto { /** * A unidirectional read connection, to be used by the read half of the * canonical serializers below. */ struct ReadConn { Source & from; }; /** * A unidirectional write connection, to be used by the write half of the * canonical serializers below. */ struct WriteConn { }; template struct Serialise; /** * Wrapper function around `CommonProto::Serialise::write` that allows us to * infer the type instead of having to write it down explicitly. */ template [[nodiscard]] static WireFormatGenerator write(const Store & store, WriteConn conn, const T & t) { return CommonProto::Serialise::write(store, conn, t); } }; #define DECLARE_COMMON_SERIALISER(T) \ struct CommonProto::Serialise< T > \ { \ static T read(const Store & store, CommonProto::ReadConn conn); \ [[nodiscard]] static WireFormatGenerator write(const Store & store, CommonProto::WriteConn conn, const T & str); \ } template<> DECLARE_COMMON_SERIALISER(std::string); template<> DECLARE_COMMON_SERIALISER(StorePath); template<> DECLARE_COMMON_SERIALISER(ContentAddress); template<> DECLARE_COMMON_SERIALISER(DrvOutput); template<> DECLARE_COMMON_SERIALISER(Realisation); template DECLARE_COMMON_SERIALISER(std::vector); template DECLARE_COMMON_SERIALISER(std::set); template DECLARE_COMMON_SERIALISER(std::tuple); #define COMMA_ , template DECLARE_COMMON_SERIALISER(std::map); #undef COMMA_ /** * These use the empty string for the null case, relying on the fact * that the underlying types never serialize to the empty string. * * We do this instead of a generic std::optional instance because * ordinal tags (0 or 1, here) are a bit of a compatability hazard. For * the same reason, we don't have a std::variant instances (ordinal * tags 0...n). * * We could the generic instances and then these as specializations for * compatability, but that's proven a bit finnicky, and also makes the * worker protocol harder to implement in other languages where such * specializations may not be allowed. */ template<> DECLARE_COMMON_SERIALISER(std::optional); template<> DECLARE_COMMON_SERIALISER(std::optional); }