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