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); }, value);
33}
34
35/// std::optional serialization
36template <typename T, typename StringBuilder>
37void WriteToStream(const std::optional<T>& value, StringBuilder& sw) {
38 if (!value) {
39 sw.WriteNull();
40 return;
41 }
42
43 WriteToStream(*value, sw);
44}
45
46namespace impl {
47
48// We specialize kIsSerializeAllowedInWriteToStream to catch the usages of
49// Serialize for your type. Make sure that kIsSerializeAllowedInWriteToStream is
50// visible at the point of WriteToStream instantiation.
51template <class T, class StringBuilder>
52constexpr inline bool kIsSerializeAllowedInWriteToStream = true;
53
54// Array like types serialization
55template <typename T, typename StringBuilder>
56void WriteToStreamArray(const T& value, StringBuilder& sw) {
57 typename StringBuilder::ArrayGuard guard(sw);
58 for (const auto& item : value) {
59 // explicit cast for vector<bool> shenanigans
60 WriteToStream(static_cast<const meta::RangeValueType<T>&>(item), sw);
61 }
62}
63
64// Dict like types serialization
65template <typename T, typename StringBuilder>
66void WriteToStreamDict(const T& value, StringBuilder& sw) {
67 typename StringBuilder::ObjectGuard guard(sw);
68 for (const auto& [key, item] : value) {
69 sw.Key(key);
70 WriteToStream(item, sw);
71 }
72}
73
74} // namespace impl
75
76/// Handle ranges, fall back to using formats::*::Serialize
77///
78/// The signature of this WriteToStream must remain the less specialized one, so
79/// that it is not preferred over other functions.
80template <typename T, typename StringBuilder>
83 using Value = typename StringBuilder::Value;
84
85 if constexpr (meta::kIsMap<T>) {
86 impl::WriteToStreamDict(value, sw);
87 } else if constexpr (meta::kIsRange<T>) {
88 static_assert(
89 !meta::kIsRecursiveRange<T>,
90 "Trying to log a recursive range, which can be dangerous. "
91 "(boost::filesystem::path?) Please implement WriteToStream "
92 "for your type"
93 );
94 impl::WriteToStreamArray(value, sw);
95 } else if constexpr (common::impl::kHasSerialize<Value, T>) {
96 static_assert(
97 !sizeof(T) || impl::kIsSerializeAllowedInWriteToStream<T, StringBuilder>,
98 "SAX serialization falls back to Serialize call, which is not "
99 "allowed. Please implement WriteToStream for your type"
100 );
101 sw.WriteValue(Serialize(value, serialize::To<Value>{}));
102 } else {
103 static_assert(!sizeof(T), "Please implement WriteToStream or Serialize for your type");
104 }
105}
106
107} // namespace formats::serialize
108
109USERVER_NAMESPACE_END