Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/JsonObjectSerializer.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
3
 * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/Error.h>
11
#include <AK/JsonArraySerializer.h>
12
#include <AK/Try.h>
13
14
#ifndef KERNEL
15
#    include <AK/JsonValue.h>
16
#endif
17
18
namespace AK {
19
20
template<typename Builder>
21
class JsonObjectSerializer {
22
public:
23
    static ErrorOr<JsonObjectSerializer> try_create(Builder& builder)
24
0
    {
25
        if constexpr (IsLegacyBuilder<Builder>)
26
0
            TRY(builder.try_append('{'));
27
        else
28
            TRY(builder.append('{'));
29
0
        return JsonObjectSerializer { builder };
30
0
    }
31
32
    JsonObjectSerializer(JsonObjectSerializer&& other)
33
0
        : m_builder(other.m_builder)
34
0
        , m_empty(other.m_empty)
35
0
        , m_finished(exchange(other.m_finished, true))
36
0
    {
37
0
    }
38
39
    JsonObjectSerializer(JsonObjectSerializer const&) = delete;
40
41
#ifndef KERNEL
42
    ErrorOr<void> add(StringView key, JsonValue const& value)
43
0
    {
44
0
        TRY(begin_item(key));
45
0
        value.serialize(m_builder);
46
0
        return {};
47
0
    }
48
#endif
49
50
    ErrorOr<void> add(StringView key, StringView value)
51
0
    {
52
0
        TRY(begin_item(key));
53
0
        if constexpr (IsLegacyBuilder<Builder>) {
54
0
            TRY(m_builder.try_append('"'));
55
0
            TRY(m_builder.try_append_escaped_for_json(value));
56
0
            TRY(m_builder.try_append('"'));
57
        } else {
58
            TRY(m_builder.append('"'));
59
            TRY(m_builder.append_escaped_for_json(value));
60
            TRY(m_builder.append('"'));
61
        }
62
0
        return {};
63
0
    }
64
65
#ifndef KERNEL
66
    ErrorOr<void> add(StringView key, ByteString const& value)
67
    {
68
        TRY(begin_item(key));
69
        if constexpr (IsLegacyBuilder<Builder>) {
70
            TRY(m_builder.try_append('"'));
71
            TRY(m_builder.try_append_escaped_for_json(value));
72
            TRY(m_builder.try_append('"'));
73
        } else {
74
            TRY(m_builder.append('"'));
75
            TRY(m_builder.append_escaped_for_json(value));
76
            TRY(m_builder.append('"'));
77
        }
78
        return {};
79
    }
80
#endif
81
82
    ErrorOr<void> add(StringView key, char const* value)
83
0
    {
84
0
        TRY(begin_item(key));
85
0
        if constexpr (IsLegacyBuilder<Builder>) {
86
0
            TRY(m_builder.try_append('"'));
87
0
            TRY(m_builder.try_append_escaped_for_json({ value, __builtin_strlen(value) }));
88
0
            TRY(m_builder.try_append('"'));
89
        } else {
90
            TRY(m_builder.append('"'));
91
            TRY(m_builder.append_escaped_for_json({ value, __builtin_strlen(value) }));
92
            TRY(m_builder.append('"'));
93
        }
94
0
        return {};
95
0
    }
96
97
    ErrorOr<void> add(StringView key, bool value)
98
0
    {
99
0
        TRY(begin_item(key));
100
        if constexpr (IsLegacyBuilder<Builder>)
101
0
            TRY(m_builder.try_append(value ? "true"sv : "false"sv));
102
        else
103
            TRY(m_builder.append(value ? "true"sv : "false"sv));
104
0
        return {};
105
0
    }
106
107
    ErrorOr<void> add(StringView key, int value)
108
0
    {
109
0
        TRY(begin_item(key));
110
        if constexpr (IsLegacyBuilder<Builder>)
111
0
            TRY(m_builder.try_appendff("{}", value));
112
        else
113
            TRY(m_builder.appendff("{}", value));
114
0
        return {};
115
0
    }
116
117
    ErrorOr<void> add(StringView key, unsigned value)
118
    {
119
        TRY(begin_item(key));
120
        if constexpr (IsLegacyBuilder<Builder>)
121
            TRY(m_builder.try_appendff("{}", value));
122
        else
123
            TRY(m_builder.appendff("{}", value));
124
        return {};
125
    }
126
127
    ErrorOr<void> add(StringView key, long value)
128
    {
129
        TRY(begin_item(key));
130
        if constexpr (IsLegacyBuilder<Builder>)
131
            TRY(m_builder.try_appendff("{}", value));
132
        else
133
            TRY(m_builder.appendff("{}", value));
134
        return {};
135
    }
136
137
    ErrorOr<void> add(StringView key, long unsigned value)
138
0
    {
139
0
        TRY(begin_item(key));
140
        if constexpr (IsLegacyBuilder<Builder>)
141
0
            TRY(m_builder.try_appendff("{}", value));
142
        else
143
            TRY(m_builder.appendff("{}", value));
144
0
        return {};
145
0
    }
146
147
    ErrorOr<void> add(StringView key, long long value)
148
    {
149
        TRY(begin_item(key));
150
        if constexpr (IsLegacyBuilder<Builder>)
151
            TRY(m_builder.try_appendff("{}", value));
152
        else
153
            TRY(m_builder.appendff("{}", value));
154
        return {};
155
    }
156
157
    ErrorOr<void> add(StringView key, long long unsigned value)
158
    {
159
        TRY(begin_item(key));
160
        if constexpr (IsLegacyBuilder<Builder>)
161
            TRY(m_builder.try_appendff("{}", value));
162
        else
163
            TRY(m_builder.appendff("{}", value));
164
        return {};
165
    }
166
167
#ifndef KERNEL
168
    ErrorOr<void> add(StringView key, float value)
169
    {
170
        TRY(begin_item(key));
171
        if constexpr (IsLegacyBuilder<Builder>)
172
            TRY(m_builder.try_appendff("{}", value));
173
        else
174
            TRY(m_builder.appendff("{}", value));
175
        return {};
176
    }
177
178
    ErrorOr<void> add(StringView key, double value)
179
    {
180
        TRY(begin_item(key));
181
        if constexpr (IsLegacyBuilder<Builder>)
182
            TRY(m_builder.try_appendff("{}", value));
183
        else
184
            TRY(m_builder.appendff("{}", value));
185
        return {};
186
    }
187
#endif
188
189
    ErrorOr<JsonArraySerializer<Builder>> add_array(StringView key)
190
0
    {
191
0
        TRY(begin_item(key));
192
0
        return JsonArraySerializer<Builder>::try_create(m_builder);
193
0
    }
194
195
    ErrorOr<JsonObjectSerializer<Builder>> add_object(StringView key)
196
0
    {
197
0
        TRY(begin_item(key));
198
0
        return JsonObjectSerializer::try_create(m_builder);
199
0
    }
200
201
    ErrorOr<void> finish()
202
0
    {
203
0
        VERIFY(!m_finished);
204
0
        m_finished = true;
205
        if constexpr (IsLegacyBuilder<Builder>)
206
0
            TRY(m_builder.try_append('}'));
207
        else
208
            TRY(m_builder.append('}'));
209
0
        return {};
210
0
    }
211
212
private:
213
    explicit JsonObjectSerializer(Builder& builder)
214
0
        : m_builder(builder)
215
0
    {
216
0
    }
217
218
    ErrorOr<void> begin_item(StringView key)
219
0
    {
220
0
        VERIFY(!m_finished);
221
0
        if (!m_empty) {
222
            if constexpr (IsLegacyBuilder<Builder>)
223
0
                TRY(m_builder.try_append(','));
224
            else
225
                TRY(m_builder.append(','));
226
0
        }
227
0
        m_empty = false;
228
229
0
        if constexpr (IsLegacyBuilder<Builder>) {
230
0
            TRY(m_builder.try_append('"'));
231
0
            TRY(m_builder.try_append_escaped_for_json(key));
232
0
            TRY(m_builder.try_append("\":"sv));
233
        } else {
234
            TRY(m_builder.append('"'));
235
            TRY(m_builder.append_escaped_for_json(key));
236
            TRY(m_builder.append("\":"sv));
237
        }
238
0
        return {};
239
0
    }
240
241
    Builder& m_builder;
242
    bool m_empty { true };
243
    bool m_finished { false };
244
};
245
246
// Template magic to allow for JsonObjectSerializer<>::try_create(...) - Blame CxByte
247
template<>
248
struct JsonObjectSerializer<void> {
249
    template<typename Builder>
250
    static ErrorOr<JsonObjectSerializer<Builder>> try_create(Builder& builder)
251
0
    {
252
0
        return JsonObjectSerializer<Builder>::try_create(builder);
253
0
    }
254
};
255
256
template<typename Builder>
257
ErrorOr<JsonObjectSerializer<Builder>> JsonArraySerializer<Builder>::add_object()
258
0
{
259
0
    TRY(begin_item());
260
0
    return JsonObjectSerializer<Builder>::try_create(m_builder);
261
0
}
262
263
}
264
265
#if USING_AK_GLOBALLY
266
using AK::JsonObjectSerializer;
267
#endif