userver: userver/formats/serialize/write_to_stream.hpp Source File
Loading...
Searching...
No Matches
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