mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-07 19:57:45 +03:00
d1bc157e9f
Previously we'd VERIFY() that the user had called finish(). This makes the following code incorrect though: auto json = TRY(JsonObjectSerializer<>::try_create(builder)); TRY(json.add("total_time"sv, total_time_scheduled.total)); TRY(json.finish()); return ...; If the second TRY() returns early we'd fail at the VERIFY() call in the destructor. Calling finish() in the destructor - like we had done earlier - is also not helpful because we have no idea whether the builder is still valid. Plus we wouldn't be able to handle any errors for that call. Verifying that either finish() was called or an error occurred doesn't work either because the caller might have multiple Json*Serializer objects, e.g. when inserting a JSON array into a JSON object. Forcing the user to call finish() on their "main" object when a sub-object caused an error seems unnecessarily tedious.
266 lines
7.1 KiB
C++
266 lines
7.1 KiB
C++
/*
|
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Error.h>
|
|
#include <AK/JsonArraySerializer.h>
|
|
#include <AK/Try.h>
|
|
|
|
#ifndef KERNEL
|
|
# include <AK/JsonValue.h>
|
|
#endif
|
|
|
|
namespace AK {
|
|
|
|
template<typename Builder>
|
|
class JsonObjectSerializer {
|
|
public:
|
|
static ErrorOr<JsonObjectSerializer> try_create(Builder& builder)
|
|
{
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(builder.try_append('{'));
|
|
else
|
|
TRY(builder.append('{'));
|
|
return JsonObjectSerializer { builder };
|
|
}
|
|
|
|
JsonObjectSerializer(JsonObjectSerializer&& other)
|
|
: m_builder(other.m_builder)
|
|
, m_empty(other.m_empty)
|
|
, m_finished(exchange(other.m_finished, true))
|
|
{
|
|
}
|
|
|
|
JsonObjectSerializer(JsonObjectSerializer const&) = delete;
|
|
|
|
#ifndef KERNEL
|
|
ErrorOr<void> add(StringView key, JsonValue const& value)
|
|
{
|
|
TRY(begin_item(key));
|
|
value.serialize(m_builder);
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
ErrorOr<void> add(StringView key, StringView value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>) {
|
|
TRY(m_builder.try_append('"'));
|
|
TRY(m_builder.try_append_escaped_for_json(value));
|
|
TRY(m_builder.try_append('"'));
|
|
} else {
|
|
TRY(m_builder.append('"'));
|
|
TRY(m_builder.append_escaped_for_json(value));
|
|
TRY(m_builder.append('"'));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
ErrorOr<void> add(StringView key, String const& value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>) {
|
|
TRY(m_builder.try_append('"'));
|
|
TRY(m_builder.try_append_escaped_for_json(value));
|
|
TRY(m_builder.try_append('"'));
|
|
} else {
|
|
TRY(m_builder.append('"'));
|
|
TRY(m_builder.append_escaped_for_json(value));
|
|
TRY(m_builder.append('"'));
|
|
}
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
ErrorOr<void> add(StringView key, char const* value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>) {
|
|
TRY(m_builder.try_append('"'));
|
|
TRY(m_builder.try_append_escaped_for_json({ value, strlen(value) }));
|
|
TRY(m_builder.try_append('"'));
|
|
} else {
|
|
TRY(m_builder.append('"'));
|
|
TRY(m_builder.append_escaped_for_json({ value, strlen(value) }));
|
|
TRY(m_builder.append('"'));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, bool value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_append(value ? "true"sv : "false"sv));
|
|
else
|
|
TRY(m_builder.append(value ? "true"sv : "false"sv));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, int value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, unsigned value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, long value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, long unsigned value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, long long value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, long long unsigned value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
ErrorOr<void> add(StringView key, float value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> add(StringView key, double value)
|
|
{
|
|
TRY(begin_item(key));
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_appendff("{}", value));
|
|
else
|
|
TRY(m_builder.appendff("{}", value));
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
ErrorOr<JsonArraySerializer<Builder>> add_array(StringView key)
|
|
{
|
|
TRY(begin_item(key));
|
|
return JsonArraySerializer<Builder>::try_create(m_builder);
|
|
}
|
|
|
|
ErrorOr<JsonObjectSerializer<Builder>> add_object(StringView key)
|
|
{
|
|
TRY(begin_item(key));
|
|
return JsonObjectSerializer::try_create(m_builder);
|
|
}
|
|
|
|
ErrorOr<void> finish()
|
|
{
|
|
VERIFY(!m_finished);
|
|
m_finished = true;
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_append('}'));
|
|
else
|
|
TRY(m_builder.append('}'));
|
|
return {};
|
|
}
|
|
|
|
private:
|
|
explicit JsonObjectSerializer(Builder& builder)
|
|
: m_builder(builder)
|
|
{
|
|
}
|
|
|
|
ErrorOr<void> begin_item(StringView key)
|
|
{
|
|
VERIFY(!m_finished);
|
|
if (!m_empty) {
|
|
if constexpr (IsLegacyBuilder<Builder>)
|
|
TRY(m_builder.try_append(','));
|
|
else
|
|
TRY(m_builder.append(','));
|
|
}
|
|
m_empty = false;
|
|
|
|
if constexpr (IsLegacyBuilder<Builder>) {
|
|
TRY(m_builder.try_append('"'));
|
|
TRY(m_builder.try_append_escaped_for_json(key));
|
|
TRY(m_builder.try_append("\":"sv));
|
|
} else {
|
|
TRY(m_builder.append('"'));
|
|
TRY(m_builder.append_escaped_for_json(key));
|
|
TRY(m_builder.append("\":"sv));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Builder& m_builder;
|
|
bool m_empty { true };
|
|
bool m_finished { false };
|
|
};
|
|
|
|
// Template magic to allow for JsonObjectSerializer<>::try_create(...) - Blame CxByte
|
|
template<>
|
|
struct JsonObjectSerializer<void> {
|
|
template<typename Builder>
|
|
static ErrorOr<JsonObjectSerializer<Builder>> try_create(Builder& builder)
|
|
{
|
|
return JsonObjectSerializer<Builder>::try_create(builder);
|
|
}
|
|
};
|
|
|
|
template<typename Builder>
|
|
ErrorOr<JsonObjectSerializer<Builder>> JsonArraySerializer<Builder>::add_object()
|
|
{
|
|
TRY(begin_item());
|
|
return JsonObjectSerializer<Builder>::try_create(m_builder);
|
|
}
|
|
|
|
}
|
|
|
|
using AK::JsonObjectSerializer;
|