Serenity Operating System
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
18namespace AK {
19
20template<typename Builder>
21class JsonObjectSerializer {
22public:
23 static ErrorOr<JsonObjectSerializer> try_create(Builder& builder)
24 {
25 if constexpr (IsLegacyBuilder<Builder>)
26 TRY(builder.try_append('{'));
27 else
28 TRY(builder.append('{'));
29 return JsonObjectSerializer { builder };
30 }
31
32 JsonObjectSerializer(JsonObjectSerializer&& other)
33 : m_builder(other.m_builder)
34 , m_empty(other.m_empty)
35 , m_finished(exchange(other.m_finished, true))
36 {
37 }
38
39 JsonObjectSerializer(JsonObjectSerializer const&) = delete;
40
41#ifndef KERNEL
42 ErrorOr<void> add(StringView key, JsonValue const& value)
43 {
44 TRY(begin_item(key));
45 value.serialize(m_builder);
46 return {};
47 }
48#endif
49
50 ErrorOr<void> add(StringView key, StringView value)
51 {
52 TRY(begin_item(key));
53 if constexpr (IsLegacyBuilder<Builder>) {
54 TRY(m_builder.try_append('"'));
55 TRY(m_builder.try_append_escaped_for_json(value));
56 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 return {};
63 }
64
65#ifndef KERNEL
66 ErrorOr<void> add(StringView key, DeprecatedString 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 {
84 TRY(begin_item(key));
85 if constexpr (IsLegacyBuilder<Builder>) {
86 TRY(m_builder.try_append('"'));
87 TRY(m_builder.try_append_escaped_for_json({ value, __builtin_strlen(value) }));
88 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 return {};
95 }
96
97 ErrorOr<void> add(StringView key, bool value)
98 {
99 TRY(begin_item(key));
100 if constexpr (IsLegacyBuilder<Builder>)
101 TRY(m_builder.try_append(value ? "true"sv : "false"sv));
102 else
103 TRY(m_builder.append(value ? "true"sv : "false"sv));
104 return {};
105 }
106
107 ErrorOr<void> add(StringView key, int value)
108 {
109 TRY(begin_item(key));
110 if constexpr (IsLegacyBuilder<Builder>)
111 TRY(m_builder.try_appendff("{}", value));
112 else
113 TRY(m_builder.appendff("{}", value));
114 return {};
115 }
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 {
139 TRY(begin_item(key));
140 if constexpr (IsLegacyBuilder<Builder>)
141 TRY(m_builder.try_appendff("{}", value));
142 else
143 TRY(m_builder.appendff("{}", value));
144 return {};
145 }
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 {
191 TRY(begin_item(key));
192 return JsonArraySerializer<Builder>::try_create(m_builder);
193 }
194
195 ErrorOr<JsonObjectSerializer<Builder>> add_object(StringView key)
196 {
197 TRY(begin_item(key));
198 return JsonObjectSerializer::try_create(m_builder);
199 }
200
201 ErrorOr<void> finish()
202 {
203 VERIFY(!m_finished);
204 m_finished = true;
205 if constexpr (IsLegacyBuilder<Builder>)
206 TRY(m_builder.try_append('}'));
207 else
208 TRY(m_builder.append('}'));
209 return {};
210 }
211
212private:
213 explicit JsonObjectSerializer(Builder& builder)
214 : m_builder(builder)
215 {
216 }
217
218 ErrorOr<void> begin_item(StringView key)
219 {
220 VERIFY(!m_finished);
221 if (!m_empty) {
222 if constexpr (IsLegacyBuilder<Builder>)
223 TRY(m_builder.try_append(','));
224 else
225 TRY(m_builder.append(','));
226 }
227 m_empty = false;
228
229 if constexpr (IsLegacyBuilder<Builder>) {
230 TRY(m_builder.try_append('"'));
231 TRY(m_builder.try_append_escaped_for_json(key));
232 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 return {};
239 }
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
247template<>
248struct JsonObjectSerializer<void> {
249 template<typename Builder>
250 static ErrorOr<JsonObjectSerializer<Builder>> try_create(Builder& builder)
251 {
252 return JsonObjectSerializer<Builder>::try_create(builder);
253 }
254};
255
256template<typename Builder>
257ErrorOr<JsonObjectSerializer<Builder>> JsonArraySerializer<Builder>::add_object()
258{
259 TRY(begin_item());
260 return JsonObjectSerializer<Builder>::try_create(m_builder);
261}
262
263}
264
265#if USING_AK_GLOBALLY
266using AK::JsonObjectSerializer;
267#endif