1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-01 11:35:37 +00:00
serenity/AK/JsonValue.h
Timothy Flynn c7ea710b55 AK: Return a constant reference from JsonValue::as_string
Rather than making a copy of the held string, this returns a reference
so that expressions like the following:

    do_something(json.as_string().view());

are not disallowed once `ByteString::view() &&` is deleted.
2024-04-04 11:23:21 +02:00

254 lines
6.7 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#ifdef KERNEL
# error "JsonValue does not propagate allocation failures, so it is not safe to use in the kernel."
#endif
#include <AK/ByteString.h>
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <AK/StringBuilder.h>
namespace AK {
class JsonValue {
public:
enum class Type {
Null,
Bool,
Number,
String,
Array,
Object,
};
static ErrorOr<JsonValue> from_string(StringView);
JsonValue();
~JsonValue();
JsonValue(JsonValue const&);
JsonValue(JsonValue&&);
JsonValue& operator=(JsonValue const&);
JsonValue& operator=(JsonValue&&);
JsonValue(int);
JsonValue(unsigned);
JsonValue(long);
JsonValue(long unsigned);
JsonValue(long long);
JsonValue(long long unsigned);
JsonValue(double);
JsonValue(char const*);
JsonValue(ByteString const&);
JsonValue(StringView);
template<typename T>
requires(SameAs<RemoveCVReference<T>, bool>)
JsonValue(T value)
: m_value { static_cast<bool>(value) }
{
}
JsonValue(JsonArray const&);
JsonValue(JsonObject const&);
JsonValue(JsonArray&&);
JsonValue(JsonObject&&);
JsonValue& operator=(JsonArray const&);
JsonValue& operator=(JsonObject const&);
JsonValue& operator=(JsonArray&&);
JsonValue& operator=(JsonObject&&);
template<typename Builder>
typename Builder::OutputType serialized() const;
template<typename Builder>
void serialize(Builder&) const;
ByteString as_string_or(ByteString const& alternative) const
{
if (is_string())
return as_string();
return alternative;
}
ByteString deprecated_to_byte_string() const
{
if (is_string())
return as_string();
return serialized<StringBuilder>();
}
Optional<int> get_int() const { return get_integer<int>(); }
Optional<i32> get_i32() const { return get_integer<i32>(); }
Optional<i64> get_i64() const { return get_integer<i64>(); }
Optional<unsigned> get_uint() const { return get_integer<unsigned>(); }
Optional<u32> get_u32() const { return get_integer<u32>(); }
Optional<u64> get_u64() const { return get_integer<u64>(); }
Optional<float> get_float_with_precision_loss() const { return get_number_with_precision_loss<float>(); }
Optional<double> get_double_with_precision_loss() const { return get_number_with_precision_loss<double>(); }
Optional<FlatPtr> get_addr() const
{
// Note: This makes the lambda dependent on the template parameter, which is necessary
// for the `if constexpr` to not evaluate both branches.
auto fn = [&]<typename T>() -> Optional<T> {
if constexpr (IsSame<T, u64>) {
return get_u64();
} else {
return get_u32();
}
};
return fn.operator()<FlatPtr>();
}
Optional<bool> get_bool() const
{
if (!is_bool())
return {};
return as_bool();
}
bool as_bool() const
{
return m_value.get<bool>();
}
ByteString const& as_string() const
{
return m_value.get<ByteString>();
}
JsonObject& as_object()
{
return *m_value.get<NonnullOwnPtr<JsonObject>>();
}
JsonObject const& as_object() const
{
return *m_value.get<NonnullOwnPtr<JsonObject>>();
}
JsonArray& as_array()
{
return *m_value.get<NonnullOwnPtr<JsonArray>>();
}
JsonArray const& as_array() const
{
return *m_value.get<NonnullOwnPtr<JsonArray>>();
}
Variant<u64, i64, double> as_number() const
{
return m_value.downcast<u64, i64, double>();
}
Type type() const
{
return m_value.visit(
[](Empty const&) { return Type::Null; },
[](bool const&) { return Type::Bool; },
[](Arithmetic auto const&) { return Type::Number; },
[](ByteString const&) { return Type::String; },
[](NonnullOwnPtr<JsonArray> const&) { return Type::Array; },
[](NonnullOwnPtr<JsonObject> const&) { return Type::Object; });
}
bool is_null() const { return m_value.has<Empty>(); }
bool is_bool() const { return m_value.has<bool>(); }
bool is_string() const { return m_value.has<ByteString>(); }
bool is_array() const { return m_value.has<NonnullOwnPtr<JsonArray>>(); }
bool is_object() const { return m_value.has<NonnullOwnPtr<JsonObject>>(); }
bool is_number() const
{
return m_value.visit(
[](bool const&) { return false; },
[]<Arithmetic U>(U const&) { return true; },
[](auto const&) { return false; });
}
template<typename T>
Optional<T> get_number_with_precision_loss() const
{
return m_value.visit(
[](bool const&) { return Optional<T> {}; },
[]<Arithmetic U>(U const& value) { return Optional<T> { static_cast<T>(value) }; },
[](auto const&) { return Optional<T> {}; });
}
template<Integral T>
bool is_integer() const
{
return get_integer<T>().has_value();
}
template<Integral T>
T as_integer() const
{
return get_integer<T>().value();
}
template<Integral T>
Optional<T> get_integer() const
{
return m_value.visit(
[](bool const&) { return Optional<T> {}; },
[]<Arithmetic U>(U const& value) -> Optional<T> {
if constexpr (Integral<U>) {
if (!is_within_range<T>(value))
return {};
return static_cast<T>(value);
} else {
// FIXME: Make is_within_range work with floating point numbers.
if (static_cast<U>(static_cast<T>(value)) != value)
return {};
return static_cast<T>(value);
}
},
[](auto const&) { return Optional<T> {}; });
}
bool equals(JsonValue const& other) const;
private:
Variant<
Empty,
bool,
i64,
u64,
double,
ByteString,
NonnullOwnPtr<JsonArray>,
NonnullOwnPtr<JsonObject>>
m_value;
};
template<>
struct Formatter<JsonValue> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, JsonValue const& value)
{
return Formatter<StringView>::format(builder, value.serialized<StringBuilder>());
}
};
}
#if USING_AK_GLOBALLY
using AK::JsonValue;
#endif