1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef API_RUNTIME_JSONUTIL_H
17#define API_RUNTIME_JSONUTIL_H
18
19#include <cstdlib>
20#include <type_traits>
21
22#include <base/containers/string.h>
23#include <base/containers/string_view.h>
24#include <base/math/quaternion.h>
25#include <base/math/vector.h>
26#include <base/util/uid_util.h>
27#include <core/json/json.h>
28
29CORE_BEGIN_NAMESPACE()
30
31inline BASE_NS::string JsonUnescape(BASE_NS::string_view str)
32{
33    return CORE_NS::json::unescape(str);
34}
35
36template<class T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
37inline bool SafeGetJsonValue(
38    const CORE_NS::json::value& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, T& output)
39{
40    if (auto const pos = jsonData.find(element); pos) {
41        if (pos->is_number()) {
42            output = pos->as_number<T>();
43            return true;
44        } else if (pos->is_boolean()) {
45            output = pos->boolean_;
46            return true;
47        } else {
48            error += element + ": expected number.\n";
49        }
50    }
51    return false;
52}
53
54template<class T, std::enable_if_t<std::is_convertible_v<T, BASE_NS::string_view>, bool> = true>
55inline bool SafeGetJsonValue(
56    const CORE_NS::json::value& jsonData, const BASE_NS::string_view element, BASE_NS::string& error, T& output)
57{
58    if (auto const pos = jsonData.find(element); pos) {
59        if (pos->is_string()) {
60            output = JsonUnescape(T(pos->string_.data(), pos->string_.size()));
61            return true;
62        } else {
63            error += element + ": expected string.\n";
64        }
65    }
66    return false;
67}
68
69template<class T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
70inline bool FromJson(const CORE_NS::json::value& jsonIn, T& output)
71{
72    if (jsonIn.is_number()) {
73        output = jsonIn.as_number<T>();
74        return true;
75    }
76    return false;
77}
78
79template<class T, std::enable_if_t<std::is_convertible_v<T, BASE_NS::string_view>, bool> = true>
80inline bool FromJson(const CORE_NS::json::value& jsonIn, T& output)
81{
82    if (jsonIn.is_string()) {
83        output = JsonUnescape(static_cast<T>(jsonIn.string_));
84        return true;
85    }
86    return false;
87}
88
89inline bool FromJson(const CORE_NS::json::value& jsonIn, bool& output)
90{
91    if (jsonIn.is_boolean()) {
92        output = jsonIn.boolean_;
93        return true;
94    }
95    return false;
96}
97
98inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Uid& output)
99{
100    constexpr size_t UID_LENGTH = 36;
101    if (jsonIn.is_string() && jsonIn.string_.size() == UID_LENGTH) {
102        output = StringToUid(JsonUnescape(jsonIn.string_));
103        return true;
104    }
105    return false;
106}
107
108template<class T>
109inline bool FromJsonArray(const CORE_NS::json::value& jsonIn, T* output, size_t size)
110{
111    if (jsonIn.is_array() && jsonIn.array_.size() == size) {
112        for (const auto& element : jsonIn.array_) {
113            if (!FromJson(element, *output)) {
114                return false;
115            }
116            output++;
117        }
118        return true;
119    }
120    return false;
121}
122
123inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::Vec2& output)
124{
125    return FromJsonArray(jsonIn, output.data, 2);
126}
127
128inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::Vec3& output)
129{
130    return FromJsonArray(jsonIn, output.data, 3);
131}
132
133inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::Vec4& output)
134{
135    return FromJsonArray(jsonIn, output.data, 4);
136}
137
138inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::UVec2& output)
139{
140    return FromJsonArray(jsonIn, output.data, 2);
141}
142
143inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::UVec3& output)
144{
145    return FromJsonArray(jsonIn, output.data, 3);
146}
147
148inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::UVec4& output)
149{
150    return FromJsonArray(jsonIn, output.data, 4);
151}
152
153inline bool FromJson(const CORE_NS::json::value& jsonIn, BASE_NS::Math::Quat& output)
154{
155    return FromJsonArray(jsonIn, output.data, 4);
156}
157
158template<class T>
159inline CORE_NS::json::standalone_value ToJson(T value)
160{
161    return CORE_NS::json::standalone_value(value);
162}
163
164// FIXME: how to make more generic?, Does not understand fixed_string
165template<>
166inline CORE_NS::json::standalone_value ToJson(BASE_NS::string_view value)
167{
168    return CORE_NS::json::standalone_value(BASE_NS::string { value });
169}
170
171template<>
172inline CORE_NS::json::standalone_value ToJson(BASE_NS::string value)
173{
174    return CORE_NS::json::standalone_value(value);
175}
176
177template<>
178inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::Vec2>(BASE_NS::Math::Vec2 value)
179{
180    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
181    json.array_.reserve(2);
182    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
183    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
184    return json;
185}
186template<>
187inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::UVec2>(BASE_NS::Math::UVec2 value)
188{
189    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
190    json.array_.reserve(2);
191    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
192    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
193    return json;
194}
195
196template<>
197inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::Vec3>(BASE_NS::Math::Vec3 value)
198{
199    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
200    json.array_.reserve(3);
201    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
202    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
203    json.array_.emplace_back(CORE_NS::json::standalone_value(value.z));
204    return json;
205}
206template<>
207inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::UVec3>(BASE_NS::Math::UVec3 value)
208{
209    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
210    json.array_.reserve(3);
211    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
212    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
213    json.array_.emplace_back(CORE_NS::json::standalone_value(value.z));
214    return json;
215}
216
217template<>
218inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::Vec4>(BASE_NS::Math::Vec4 value)
219{
220    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
221    json.array_.reserve(4);
222    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
223    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
224    json.array_.emplace_back(CORE_NS::json::standalone_value(value.z));
225    json.array_.emplace_back(CORE_NS::json::standalone_value(value.w));
226    return json;
227}
228template<>
229inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::UVec4>(BASE_NS::Math::UVec4 value)
230{
231    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
232    json.array_.reserve(4);
233    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
234    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
235    json.array_.emplace_back(CORE_NS::json::standalone_value(value.z));
236    json.array_.emplace_back(CORE_NS::json::standalone_value(value.w));
237    return json;
238}
239
240template<>
241inline CORE_NS::json::standalone_value ToJson<BASE_NS::Math::Quat>(BASE_NS::Math::Quat value)
242{
243    CORE_NS::json::standalone_value json = CORE_NS::json::standalone_value::array();
244    json.array_.reserve(4);
245    json.array_.emplace_back(CORE_NS::json::standalone_value(value.x));
246    json.array_.emplace_back(CORE_NS::json::standalone_value(value.y));
247    json.array_.emplace_back(CORE_NS::json::standalone_value(value.z));
248    json.array_.emplace_back(CORE_NS::json::standalone_value(value.w));
249    return json;
250}
251
252template<>
253inline CORE_NS::json::standalone_value ToJson<BASE_NS::Uid>(BASE_NS::Uid value)
254{
255    return ToJson(BASE_NS::string_view { to_string(value) });
256}
257
258CORE_END_NAMESPACE()
259#endif // SCENE_PLUGIN_JSONUTIL_H
260