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