userver: userver/formats/serialize/write_to_stream.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
write_to_stream.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/serialize/write_to_stream.hpp
4/// @brief Common WriteToStream functions for SAX serializers.
5/// @ingroup userver_universal userver_formats_serialize_sax
6
7#include <map>
8#include <optional>
9#include <type_traits>
10#include <unordered_map>
11#include <variant>
12
13#include <userver/utils/meta.hpp>
14
15#include <userver/formats/common/meta.hpp>
16
17namespace boost::uuids {
18struct uuid;
19}
20
21USERVER_NAMESPACE_BEGIN
22
23namespace utils::impl::strong_typedef {
24struct StrongTypedefTag;
25}
26
27namespace formats::serialize {
28
29/// An ADL helper that allows searching for `WriteToStream` functions in
30/// namespace of the stream. Derive your SAX string builder from this type.
31struct SaxStream {};
32
33/// Variant serialization
34template <typename... Types, typename StringBuilder>
35void WriteToStream(const std::variant<Types...>& value, StringBuilder& sw) {
36 return std::visit([&sw](const auto& item) { WriteToStream(item, sw); }, value);
37}
38
39/// std::optional serialization
40template <typename T, typename StringBuilder>
41void WriteToStream(const std::optional<T>& value, StringBuilder& sw) {
42 if (!value) {
43 sw.WriteNull();
44 return;
45 }
46
47 WriteToStream(*value, sw);
48}
49
50namespace impl {
51
52// We specialize kIsSerializeAllowedInWriteToStream to catch the usages of
53// Serialize for your type. Make sure that kIsSerializeAllowedInWriteToStream is
54// visible at the point of WriteToStream instantiation.
55template <class T, class StringBuilder>
56constexpr inline bool kIsSerializeAllowedInWriteToStream = true;
57
58// Array like types serialization
59template <typename T, typename StringBuilder>
60void WriteToStreamArray(const T& value, StringBuilder& sw) {
61 typename StringBuilder::ArrayGuard guard(sw);
62 for (const auto& item : value) {
63 // explicit cast for vector<bool> shenanigans
64 WriteToStream(static_cast<const meta::RangeValueType<T>&>(item), sw);
65 }
66}
67
68// Dict like types serialization
69template <typename T, typename StringBuilder>
70void WriteToStreamDict(const T& value, StringBuilder& sw) {
71 typename StringBuilder::ObjectGuard guard(sw);
72 for (const auto& [key, item] : value) {
73 sw.Key(key);
74 WriteToStream(item, sw);
75 }
76}
77
78} // namespace impl
79
80/// Handle ranges, fall back to using formats::*::Serialize
81///
82/// The signature of this WriteToStream must remain the less specialized one, so
83/// that it is not preferred over other functions.
84template <typename T, typename StringBuilder>
87 using Value = typename StringBuilder::Value;
88
89 if constexpr (meta::kIsMap<T>) {
90 impl::WriteToStreamDict(value, sw);
91 } else if constexpr (meta::kIsRange<T>) {
92 static_assert(
93 !std::is_same_v<T, boost::uuids::uuid>,
94 "Include <userver/formats/serialize/boost_uuid.hpp> to serialize 'boost::uuids::uuid"
95 );
96 static_assert(
97 !meta::kIsRecursiveRange<T>,
98 "Trying to log a recursive range, which can be dangerous. "
99 "(boost::filesystem::path?) Please implement WriteToStream "
100 "for your type"
101 );
102 impl::WriteToStreamArray(value, sw);
103 } else if constexpr (common::impl::kHasSerialize<Value, T>) {
104 static_assert(
105 !sizeof(T) || impl::kIsSerializeAllowedInWriteToStream<T, StringBuilder>,
106 "SAX serialization falls back to Serialize call, which is not "
107 "allowed. Please implement WriteToStream for your type"
108 );
109 sw.WriteValue(Serialize(value, serialize::To<Value>{}));
110 } else {
111 static_assert(!sizeof(T), "Please implement WriteToStream or Serialize for your type");
112 }
113}
114
115} // namespace formats::serialize
116
117USERVER_NAMESPACE_END