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 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