AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
Conversion.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include <range/v3/range/concepts.hpp>
   15#include <AUI/Json/AJson.h>
   16#include <AUI/IO/APath.h>
   17#include "AUI/Traits/parameter_pack.h"
   18#include "AUI/Traits/members.h"
   19#include <AUI/Reflect/AEnumerate.h>
   20#include <AUI/Traits/strings.h>
   21
   42template<typename T, typename Specialization = void>
   44
   45namespace aui {
   46    template<typename T>
   47    constexpr bool has_json_converter = aui::is_complete<AJsonConv<T>>;
   48
   49    template<typename T>
   50    inline AJson to_json(const T& t) {
   51        static_assert(aui::has_json_converter<T>, "this type does not implement AJsonConv<T> trait");
   52        return AJsonConv<T>::toJson(t);
   53    }
   54    template<typename T>
   55    inline T from_json(const AJson& v) {
   56        try {
   57            static_assert(aui::has_json_converter<T>, "this type does not implement AJsonConv<T> trait");
   58            T dst;
   59            AJsonConv<T>::fromJson(v, dst);
   60            return dst;
   61        } catch (...) {
   62            throw AJsonException("While converting from json to cpp\n" + AJson::toString(v), std::current_exception());
   63        }
   64    }
   65
   66    template<typename T>
   67    inline void from_json(const AJson& v, T& dst) {
   68        try {
   69            static_assert(aui::has_json_converter<T>, "this type does not implement AJsonConv<T> trait");
   70            AJsonConv<T>::fromJson(v, dst);
   71        } catch (...) {
   72            throw AJsonException("While converting from json to cpp\n" + AJson::toString(v), std::current_exception());
   73        }
   74    }
   75}
   76
   77// win fix
   78#ifdef OPTIONAL
   79#undef OPTIONAL
   80#endif
   81
   82
   83AUI_ENUM_FLAG(AJsonFieldFlags) {
   84    DEFAULT = 0b0,
   85
   89    OPTIONAL = 0b1,
   90};
   91
   92namespace aui::impl::json {
   93
   94    template<typename T>
   95    struct Field {
   96        T& value;
   97        const char* name;
   98        AJsonFieldFlags flags;
   99
  100        Field(T& value, const char* name, AJsonFieldFlags flags) : value(value), name(name), flags(flags) {}
  101
  102        void operator()(const AJson::Object& object) {
  103            if (auto c = object.contains(name)) {
  104                aui::from_json<T>(c->second, value);
  105            } else {
  106                if (!(flags & AJsonFieldFlags::OPTIONAL)) {
  107                    throw AJsonException(R"(field "{}" is not present)"_format(name));
  108                }
  109            }
  110        }
  111        void operator()(AJson::Object& object) {
  112            object[name] = aui::to_json<T>(value);
  113        }
  114    };
  115
  116    template<typename... Items>
  117    struct my_tuple: std::tuple<Items...> {
  118        my_tuple(Items... items): std::tuple<Items...>(std::move(items)...) {}
  119
  120        template<typename T>
  121        auto operator()(T& v, const char* n, AJsonFieldFlags flags = AJsonFieldFlags::DEFAULT) {
  122            return (std::apply)([&](auto&&... args) {
  123                return aui::impl::json::my_tuple(args..., aui::impl::json::Field(v, n, flags));
  124            }, stdTuple());
  125        }
  126
  127        std::tuple<Items...>& stdTuple() {
  128            return (std::tuple<Items...>&)*this;
  129        }
  130    };
  131
  132    struct empty_tuple {
  133        template<typename T>
  134        auto operator()(T& v, const char* n, AJsonFieldFlags flags = AJsonFieldFlags::DEFAULT) {
  135            return my_tuple(aui::impl::json::Field(v, n, flags));
  136        }
  137    };
  138}
  139
  140
  141template<typename T>
  143
  192#define AJSON_FIELDS(N, ...) \
  193template<> struct AJsonConvFieldDescriptor<N>: N { \
  194    auto operator()() { \
  195        return aui::impl::json::empty_tuple() \
  196                __VA_ARGS__ \
  197                ; \
  198    } \
  199};
  200
  205#define AJSON_FIELDS_ENTRY(name) (name, AUI_PP_STRINGIZE(name))
  206
  212template<typename T>
  213struct AJsonConv<T, std::enable_if_t<aui::is_complete<AJsonConvFieldDescriptor<T>>>> {
  214
  215    static AJson toJson(const T& t) {
  216        AJson::Object json;
  217        std::apply([&](auto&&... fields) {
  218            aui::parameter_pack::for_each([&](auto&& field) {
  219                field(json);
  220            }, fields...);
  221        }, ((AJsonConvFieldDescriptor<T>&)t)().stdTuple());
  222        return std::move(json);
  223    }
  224
  225    static void fromJson(const AJson& json, T& dst) {
  226        const auto& jsonObject = json.asObject();
  227        std::apply([&](auto&&... fields) {
  228            aui::parameter_pack::for_each([&](auto&& field) {
  229                field(jsonObject);
  230            }, fields...);
  231        }, ((AJsonConvFieldDescriptor<T>&)dst)().stdTuple());
  232    }
  233};
  234
  235
  236template<>
  237struct AJsonConv<int> {
  238    static AJson toJson(int v) {
  239        return v;
  240    }
  241    static void fromJson(const AJson& json, int& dst) {
  242        dst = json.asInt();
  243    }
  244};
  245
  246template<typename T>
  247struct AJsonConv<std::shared_ptr<T>> {
  248    static AJson toJson(const std::shared_ptr<T>& v) {
  249        return aui::to_json(*v);
  250    }
  251    static void fromJson(const AJson& json, std::shared_ptr<T>& dst) {
  252        dst = std::make_shared<T>(aui::from_json<T>(json));
  253    }
  254};
  255
  256template<typename T>
  257struct AJsonConv<_<T>>: AJsonConv<std::shared_ptr<T>> {};
  258
  259template<>
  260struct AJsonConv<int64_t> {
  261    static AJson toJson(int64_t v) {
  262        return v;
  263    }
  264    static void fromJson(const AJson& json, int64_t& dst) {
  265        dst = json.asLongInt();
  266    }
  267};
  268template<>
  269struct AJsonConv<short> {
  270    static AJson toJson(int v) {
  271        return v;
  272    }
  273    static int fromJson(const AJson& json) {
  274        return json.asInt();
  275    }
  276};
  277
  278template<>
  279struct AJsonConv<float> {
  280    static AJson toJson(float v) {
  281        return v;
  282    }
  283    static void fromJson(const AJson& json, float& dst) {
  284        dst = json.asNumber();
  285    }
  286};
  287
  288template<>
  289struct AJsonConv<double> {
  290    static AJson toJson(double v) {
  291        return v;
  292    }
  293    static void fromJson(const AJson& json, double& dst) {
  294        dst = json.asNumber();
  295    }
  296};
  297
  298template<aui::arithmetic UnderlyingType, auto min, auto max>
  299    requires aui::convertible_to<decltype(min), UnderlyingType> && aui::convertible_to<decltype(max), UnderlyingType>
  300struct AJsonConv<aui::ranged_number<UnderlyingType, min, max>> {
  301    static AJson toJson(aui::ranged_number<UnderlyingType, min, max> v) {
  302        return (UnderlyingType) v;
  303    }
  304    static void fromJson(const AJson& json, aui::ranged_number<UnderlyingType, min, max>& dst) {
  306            dst = (UnderlyingType) json.asNumber();
  307        } else {
  308            dst = (UnderlyingType) json.asLongInt();
  309        }
  310    }
  311};
  312
  313template<>
  314struct AJsonConv<bool> {
  315    static AJson toJson(bool v) {
  316        return v;
  317    }
  318    static void fromJson(const AJson& json, bool& dst) {
  319        dst = json.asBool();
  320    }
  321};
  322
  323template<>
  325    static AJson toJson(AString v) {
  326        return v;
  327    }
  328    static void fromJson(const AJson& json, AString& dst) {
  329        dst = json.asString();
  330    }
  331};
  332template<>
  334    static AJson toJson(APath v) {
  335        return v;
  336    }
  337    static void fromJson(const AJson& json, APath& dst) {
  338        dst = json.asString();
  339    }
  340};
  341
  342template<typename T1, typename T2>
  343struct AJsonConv<std::pair<T1, T2>> {
  344    static AJson toJson(std::pair<T1, T2> v) {
  345        return AJson::Array({aui::to_json(v.first), aui::to_json(v.second)});
  346    }
  347    static void fromJson(const AJson& json, std::pair<T1, T2>& dst) {
  348        const auto& array = json.asArray();
  349        dst = { aui::from_json<T1>(array.at(0)), aui::from_json<T2>(array.at(1)) };
  350    }
  351};
  352
  353template<>
  354struct AJsonConv<AJson::Array> {
  355    static AJson toJson(AJson::Array v) {
  356        return std::move(v);
  357    }
  358    static void fromJson(const AJson& json, AJson::Array& dst) {
  359        dst = json.asArray();
  360    }
  361};
  362
  363template<>
  364struct AJsonConv<AJson::Object> {
  365    static AJson toJson(AJson::Object v) {
  366        return std::move(v);
  367    }
  368    static void fromJson(const AJson& json, AJson::Object& dst) {
  369        dst = json.asObject();
  370    }
  371};
  372
  373template<ranges::range T>
  374struct AJsonConv<T> {
  375    static AJson toJson(const T& v) {
  376        AJson::Array array;
  377        if constexpr (ranges::sized_range<T>) {
  378            array.reserve(v.size());
  379        }
  380        for (const auto& elem : v) {
  381            array << aui::to_json(elem);
  382        }
  383        return std::move(array);
  384    }
  385    static void fromJson(const AJson& json, T& dst) {
  386        auto& array = json.asArray();
  387        dst.clear();
  388
  389        if constexpr (requires(T&& t) { t.reserve(static_cast<size_t>(0)); }) {
  390            dst.reserve(array.size());
  391        }
  392        for (const auto& elem : array) {
  393            dst << aui::from_json<std::decay_t<decltype(*dst.begin())>>(elem);
  394        }
  395    }
  396};
  397
  398
  399template<typename T>
  400struct AJsonConv<T, typename std::enable_if_t<std::is_enum_v<T>>> {
  401    static AJson toJson(const T& v) {
  402        return AEnumerate<T>::valueToNameMap()[v];
  403    }
  404    static void fromJson(const AJson& json, T& dst) {
  405        dst = AEnumerate<T>::byName(json.asString());
  406    }
  407};
  408
  409
  410template<APropertyWritable T>
  411struct AJsonConv<T> {
  412    static AJson toJson(const T& t) {
  413        return t.value();
  414    }
  415    static void fromJson(const AJson& json, T& dst) {
  416        dst = aui::from_json<typename T::Underlying>(json);
  417    }
  418};
static enum_t byName(const AString &name)
Map runtime name to enum value. Transforms name to uppercase as a fallback. Throws an exception if no...
Definition AEnumerate.h:121
static const AMap< enum_t, AString, enum_less > & valueToNameMap()
Map runtime enum value to name.
Definition AEnumerate.h:183
Definition Exception.h:17
Json atom.
Definition AJson.h:84
An add-on to AString with functions for working with the path.
Definition APath.h:128
Represents a Unicode character string.
Definition AString.h:38
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:179
Definition concepts.h:42
Definition concepts.h:70
#define AUI_ENUM_FLAG(name)
Make a bitfield-style enum class.
Definition AEnumerate.h:227
API_AUI_CORE const ACommandLineArgs & args() noexcept
constexpr bool is_complete
Determines whether T is complete or not.
Definition types.h:23
@ DEFAULT
There's no concrete input action. Let the OS decide which action is the most appropriate.
Definition ATextInputActionIcon.h:36
Definition Conversion.h:142
Definition Conversion.h:43
Definition Conversion.h:95
Definition Conversion.h:132
Definition Conversion.h:117
Clamps the possible values for a number to the specified range: [min;max].
Definition values.h:423