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_UTIL_JSON_H
17 #define API_UTIL_JSON_H
18 
19 #include <core/json/json.h>
20 
21 #include <util/namespace.h>
22 
23 UTIL_BEGIN_NAMESPACE()
24 
25 constexpr int JSON_DEFAULT_INDENTATION = 2;
26 
27 template<typename T>
28 BASE_NS::string to_formatted_string(const CORE_NS::json::value_t<T>& value,
29     const size_t indentation = JSON_DEFAULT_INDENTATION, const size_t currentIndentation = 0);
30 
31 #ifdef JSON_IMPL
32 using namespace CORE_NS::json;
33 
34 template<typename T>
append(BASE_NS::string& out, const typename value_t<T>::string& string)35 void append(BASE_NS::string& out, const typename value_t<T>::string& string)
36 {
37     out += '"';
38     out.append(escape(string));
39     out += '"';
40 }
41 
42 template<typename T>
append(BASE_NS::string& out, const typename value_t<T>::object& object, const size_t indentation, size_t currentIndentation)43 void append(BASE_NS::string& out, const typename value_t<T>::object& object, const size_t indentation,
44     size_t currentIndentation)
45 {
46     if (object.empty()) {
47         // Keep empty objects on one row.
48         out.append("{}");
49         return;
50     }
51 
52     out += "{\n";
53     currentIndentation += indentation;
54     out.append(currentIndentation, ' ');
55 
56     int count = 0;
57     for (const auto& v : object) {
58         if (count++) {
59             out += ",\n";
60             out.append(currentIndentation, ' ');
61         }
62         append<T>(out, v.key);
63         out += ": ";
64         out += to_formatted_string(v.value, indentation, currentIndentation);
65     }
66     currentIndentation -= indentation;
67     out += '\n';
68     out.append(currentIndentation, ' ');
69     out += '}';
70 }
71 
72 template<typename T>
append( BASE_NS::string& out, const typename value_t<T>::array& array, const size_t indentation, size_t currentIndentation)73 void append(
74     BASE_NS::string& out, const typename value_t<T>::array& array, const size_t indentation, size_t currentIndentation)
75 {
76     if (array.empty()) {
77         // Keep empty arrays on one row.
78         out.append("[]");
79         return;
80     }
81 
82     out += "[\n";
83     currentIndentation += indentation;
84     out.append(currentIndentation, ' ');
85     int count = 0;
86     for (const auto& v : array) {
87         if (count++) {
88             out += ",\n";
89             out.append(currentIndentation, ' ');
90         }
91         out += to_formatted_string(v, indentation, currentIndentation);
92     }
93     currentIndentation -= indentation;
94     out += '\n';
95     out.append(currentIndentation, ' ');
96     out += ']';
97 }
98 
99 template<typename T>
append(BASE_NS::string& out, const double floatingPoint)100 void append(BASE_NS::string& out, const double floatingPoint)
101 {
102     constexpr const char* FLOATING_FORMAT_STR = "%.17g";
103     const int size = snprintf(nullptr, 0, FLOATING_FORMAT_STR, floatingPoint);
104     const size_t oldSize = out.size();
105     out.resize(oldSize + size);
106     const size_t newSize = out.size();
107     // "At most bufsz - 1 characters are written." string has size() characters + 1 for null so use size() +
108     // 1 as the total size. If resize() failed string size() hasn't changed, buffer will point to the null
109     // character and bufsz will be 1 i.e. only the null character will be written.
110 }
111 
112 template<typename T>
to_formatted_string(const value_t<T>& value, const size_t indentation, const size_t currentIndentation)113 BASE_NS::string to_formatted_string(const value_t<T>& value, const size_t indentation, const size_t currentIndentation)
114 {
115     BASE_NS::string out;
116     switch (value.type) {
117         case type::uninitialized:
118             out += "{}";
119             break;
120 
121         case type::object:
122             append<T>(out, value.object_, indentation, currentIndentation);
123             break;
124 
125         case type::array:
126             append<T>(out, value.array_, indentation, currentIndentation);
127             break;
128 
129         case type::string:
130             append<T>(out, value.string_);
131             break;
132 
133         case type::floating_point:
134             append<T>(out, value.float_);
135             break;
136 
137         case type::signed_int:
138             out += BASE_NS::to_string(value.signed_);
139             break;
140 
141         case type::unsigned_int:
142             out += BASE_NS::to_string(value.unsigned_);
143             break;
144 
145         case type::boolean:
146             if (value.boolean_) {
147                 out += "true";
148             } else {
149                 out += "false";
150             }
151             break;
152 
153         case type::null:
154             out += "null";
155             break;
156 
157         default:
158             break;
159     }
160     return out;
161 }
162 
163 // Explicit template instantiation for the needed types.
164 template BASE_NS::string to_formatted_string(
165     const value& value, const size_t indentation, const size_t currentIndentation);
166 template BASE_NS::string to_formatted_string(
167     const standalone_value& value, const size_t indentation, const size_t currentIndentation);
168 
169 #endif // JSON_IMPL
170 
171 UTIL_END_NAMESPACE()
172 
173 #endif // API_UTIL_JSON_H
174