userver: userver/formats/serialize/write_to_stream.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
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
17USERVER_NAMESPACE_BEGIN
18
19namespace utils::impl::strong_typedef {
20struct StrongTypedefTag;
21}
22
23namespace formats::serialize {
24
25/// An ADL helper that allows searching for `WriteToStream` functions in
26/// namespace of the stream. Derive your SAX string builder from this type.
27struct SaxStream {};
28
29/// Variant serialization
30template <typename... Types, typename StringBuilder>
31void WriteToStream(const std::variant<Types...>& value, StringBuilder& sw) {
32 return std::visit([&sw](const auto& item) { WriteToStream(item, sw); },
33 value);
34}
35
36/// std::optional serialization
37template <typename T, typename StringBuilder>
38void WriteToStream(const std::optional<T>& value, StringBuilder& sw) {
39 if (!value) {
40 sw.WriteNull();
41 return;
42 }
43
44 WriteToStream(*value, sw);
45}
46
47namespace impl {
48
49// We specialize kIsSerializeAllowedInWriteToStream to catch the usages of
50// Serialize for your type. Make sure that kIsSerializeAllowedInWriteToStream is
51// visible at the point of WriteToStream instantiation.
52template <class T, class StringBuilder>
53constexpr inline bool kIsSerializeAllowedInWriteToStream = true;
54
55// Array like types serialization
56template <typename T, typename StringBuilder>
57void WriteToStreamArray(const T& value, StringBuilder& sw) {
58 typename StringBuilder::ArrayGuard guard(sw);
59 for (const auto& item : value) {
60 // explicit cast for vector<bool> shenanigans
61 WriteToStream(static_cast<const meta::RangeValueType<T>&>(item), sw);
62 }
63}
64
65// Dict like types serialization
66template <typename T, typename StringBuilder>
67void WriteToStreamDict(const T& value, StringBuilder& sw) {
68 typename StringBuilder::ObjectGuard guard(sw);
69 for (const auto& [key, item] : value) {
70 sw.Key(key);
71 WriteToStream(item, sw);
72 }
73}
74
75} // namespace impl
76
77/// Handle ranges, fall back to using formats::*::Serialize
78///
79/// The signature of this WriteToStream must remain the less specialized one, so
80/// that it is not preferred over other functions.
81template <typename T, typename StringBuilder>
86 using Value = typename StringBuilder::Value;
87
88 if constexpr (meta::kIsMap<T>) {
89 impl::WriteToStreamDict(value, sw);
90 } else if constexpr (meta::kIsRange<T>) {
91 static_assert(!meta::kIsRecursiveRange<T>,
92 "Trying to log a recursive range, which can be dangerous. "
93 "(boost::filesystem::path?) Please implement WriteToStream "
94 "for your type");
95 impl::WriteToStreamArray(value, sw);
96 } else if constexpr (common::impl::kHasSerialize<Value, T>) {
97 static_assert(
98 !sizeof(T) ||
99 impl::kIsSerializeAllowedInWriteToStream<T, StringBuilder>,
100 "SAX serialization falls back to Serialize call, which is not "
101 "allowed. Please implement WriteToStream for your type");
102 sw.WriteValue(Serialize(value, serialize::To<Value>{}));
103 } else {
104 static_assert(!sizeof(T),
105 "Please implement WriteToStream or Serialize for your type");
106 }
107}
108
109} // namespace formats::serialize
110
111USERVER_NAMESPACE_END