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/Try.h>
12
13#ifndef KERNEL
14# include <AK/JsonValue.h>
15#endif
16
17namespace AK {
18
19template<typename Builder>
20inline constexpr bool IsLegacyBuilder = requires(Builder builder) { builder.try_append('\0'); };
21
22template<typename Builder = void>
23class JsonObjectSerializer;
24
25template<typename Builder = void>
26class JsonArraySerializer {
27public:
28 static ErrorOr<JsonArraySerializer> try_create(Builder& builder)
29 {
30 if constexpr (IsLegacyBuilder<Builder>)
31 TRY(builder.try_append('['));
32 else
33 TRY(builder.append('['));
34 return JsonArraySerializer { builder };
35 }
36
37 JsonArraySerializer(JsonArraySerializer&& other)
38 : m_builder(other.m_builder)
39 , m_empty(other.m_empty)
40 , m_finished(exchange(other.m_finished, true))
41 {
42 }
43
44 JsonArraySerializer(JsonArraySerializer const&) = delete;
45
46#ifndef KERNEL
47 ErrorOr<void> add(JsonValue const& value)
48 {
49 TRY(begin_item());
50 value.serialize(m_builder);
51 return {};
52 }
53#endif
54
55 ErrorOr<void> add(StringView value)
56 {
57 TRY(begin_item());
58 if constexpr (IsLegacyBuilder<Builder>) {
59 TRY(m_builder.try_append('"'));
60 TRY(m_builder.try_append_escaped_for_json(value));
61 TRY(m_builder.try_append('"'));
62 } else {
63 TRY(m_builder.append('"'));
64 TRY(m_builder.append_escaped_for_json(value));
65 TRY(m_builder.append('"'));
66 }
67 return {};
68 }
69
70#ifndef KERNEL
71 ErrorOr<void> add(DeprecatedString const& value)
72 {
73 TRY(begin_item());
74 if constexpr (IsLegacyBuilder<Builder>) {
75 TRY(m_builder.try_append('"'));
76 TRY(m_builder.try_append_escaped_for_json(value));
77 TRY(m_builder.try_append('"'));
78 } else {
79 TRY(m_builder.append('"'));
80 TRY(m_builder.append_escaped_for_json(value));
81 TRY(m_builder.append('"'));
82 }
83 return {};
84 }
85#endif
86
87 ErrorOr<void> add(char const* value)
88 {
89 TRY(begin_item());
90 if constexpr (IsLegacyBuilder<Builder>) {
91 TRY(m_builder.try_append('"'));
92 TRY(m_builder.try_append_escaped_for_json(value));
93 TRY(m_builder.try_append('"'));
94 } else {
95 TRY(m_builder.append('"'));
96 TRY(m_builder.append_escaped_for_json(value));
97 TRY(m_builder.append('"'));
98 }
99 return {};
100 }
101
102 ErrorOr<void> add(bool value)
103 {
104 TRY(begin_item());
105 if constexpr (IsLegacyBuilder<Builder>)
106 TRY(m_builder.try_append(value ? "true"sv : "false"sv));
107 else
108 TRY(m_builder.append(value ? "true"sv : "false"sv));
109 return {};
110 }
111
112 ErrorOr<void> add(int value)
113 {
114 TRY(begin_item());
115 if constexpr (IsLegacyBuilder<Builder>)
116 TRY(m_builder.try_appendff("{}", value));
117 else
118 TRY(m_builder.appendff("{}", value));
119 return {};
120 }
121
122 ErrorOr<void> add(unsigned value)
123 {
124 TRY(begin_item());
125 if constexpr (IsLegacyBuilder<Builder>)
126 TRY(m_builder.try_appendff("{}", value));
127 else
128 TRY(m_builder.appendff("{}", value));
129 return {};
130 }
131
132 ErrorOr<void> add(long value)
133 {
134 TRY(begin_item());
135 if constexpr (IsLegacyBuilder<Builder>)
136 TRY(m_builder.try_appendff("{}", value));
137 else
138 TRY(m_builder.appendff("{}", value));
139 return {};
140 }
141
142 ErrorOr<void> add(long unsigned value)
143 {
144 TRY(begin_item());
145 if constexpr (IsLegacyBuilder<Builder>)
146 TRY(m_builder.try_appendff("{}", value));
147 else
148 TRY(m_builder.appendff("{}", value));
149 return {};
150 }
151
152 ErrorOr<void> add(long long value)
153 {
154 TRY(begin_item());
155 if constexpr (IsLegacyBuilder<Builder>)
156 TRY(m_builder.try_appendff("{}", value));
157 else
158 TRY(m_builder.appendff("{}", value));
159 return {};
160 }
161
162 ErrorOr<void> add(long long unsigned value)
163 {
164 TRY(begin_item());
165 if constexpr (IsLegacyBuilder<Builder>)
166 TRY(m_builder.try_appendff("{}", value));
167 else
168 TRY(m_builder.appendff("{}", value));
169 return {};
170 }
171
172 ErrorOr<JsonArraySerializer<Builder>> add_array()
173 {
174 TRY(begin_item());
175 return JsonArraySerializer::try_create(m_builder);
176 }
177
178 // Implemented in JsonObjectSerializer.h
179 ErrorOr<JsonObjectSerializer<Builder>> add_object();
180
181 ErrorOr<void> finish()
182 {
183 VERIFY(!m_finished);
184 m_finished = true;
185 if constexpr (IsLegacyBuilder<Builder>)
186 TRY(m_builder.try_append(']'));
187 else
188 TRY(m_builder.append(']'));
189 return {};
190 }
191
192private:
193 explicit JsonArraySerializer(Builder& builder)
194 : m_builder(builder)
195 {
196 }
197
198 ErrorOr<void> begin_item()
199 {
200 VERIFY(!m_finished);
201 if (!m_empty) {
202 if constexpr (IsLegacyBuilder<Builder>)
203 TRY(m_builder.try_append(','));
204 else
205 TRY(m_builder.append(','));
206 }
207 m_empty = false;
208 return {};
209 }
210
211 Builder& m_builder;
212 bool m_empty { true };
213 bool m_finished { false };
214};
215
216// Template magic to allow for JsonArraySerializer<>::try_create(...) - Blame CxByte
217template<>
218struct JsonArraySerializer<void> {
219 template<typename Builder>
220 static ErrorOr<JsonArraySerializer<Builder>> try_create(Builder& builder)
221 {
222 return JsonArraySerializer<Builder>::try_create(builder);
223 }
224};
225
226}
227
228#if USING_AK_GLOBALLY
229using AK::JsonArraySerializer;
230#endif