1 /**
2 * Copyright (c) 2022 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 LIBPANDABASE_UTILS_JSON_BUILDER_H
17 #define LIBPANDABASE_UTILS_JSON_BUILDER_H
18
19 #include <cmath>
20 #include <cstddef>
21 #include <functional>
22 #include <ostream>
23 #include <sstream>
24 #include <string>
25 #include <string_view>
26 #include <type_traits>
27 #include <utility>
28
29 namespace panda {
30 class JsonArrayBuilder;
31 class JsonObjectBuilder;
32 void JsonEscape(std::ostream & /* os */, std::string_view /* string */);
33
34 template <char startDelimiter, char endDelimiter>
35 class JsonBuilderBase {
36 public:
JsonBuilderBase()37 JsonBuilderBase()
38 {
39 ss_ << startDelimiter;
40 }
41
Build()42 std::string Build() &&
43 {
44 ss_ << endDelimiter;
45 return ss_.str();
46 }
47
48 protected:
Entry()49 void Entry()
50 {
51 if (firstEntry_) {
52 firstEntry_ = false;
53 } else {
54 ss_ << ',';
55 }
56 }
57
58 template <typename T>
Append(T && value)59 void Append(T &&value)
60 {
61 ss_ << (std::forward<T>(value));
62 }
63
Stringify(std::nullptr_t)64 void Stringify(std::nullptr_t)
65 {
66 ss_ << "null";
67 }
68
Stringify(bool boolean)69 void Stringify(bool boolean)
70 {
71 ss_ << (boolean ? "true" : "false");
72 }
73
74 template <typename T, std::enable_if_t<std::is_convertible_v<T, double> && !std::is_same_v<T, bool>, int> = 0>
Stringify(T && number)75 void Stringify(T &&number)
76 {
77 auto value = static_cast<double>(std::forward<T>(number));
78 if (std::isfinite(value)) {
79 ss_ << value;
80 } else {
81 ss_ << "null";
82 }
83 }
84
Stringify(std::string_view string)85 void Stringify(std::string_view string)
86 {
87 JsonEscape(ss_, string);
88 }
89
Stringify(const char * string)90 void Stringify(const char *string)
91 {
92 JsonEscape(ss_, string);
93 }
94
95 template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonArrayBuilder &>, int> = 0>
96 void Stringify(T &&array);
97
98 template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonObjectBuilder &>, int> = 0>
99 void Stringify(T &&object);
100
101 private:
102 std::stringstream ss_;
103 bool firstEntry_ {true};
104 };
105
106 class JsonArrayBuilder : public JsonBuilderBase<'[', ']'> {
107 public:
108 template <typename T>
Add(T && value)109 JsonArrayBuilder &Add(T &&value) &
110 {
111 Entry();
112 Stringify(std::forward<T>(value));
113 return *this;
114 }
115
116 template <typename T>
Add(T && value)117 JsonArrayBuilder &&Add(T &&value) &&
118 {
119 Add(std::forward<T>(value));
120 return std::move(*this);
121 }
122 };
123
124 // Trick CodeChecker (G.FMT.03).
125 using JsonObjectBuilderBase = JsonBuilderBase<'{', '}'>;
126
127 class JsonObjectBuilder : public JsonObjectBuilderBase {
128 public:
129 template <typename T>
AddProperty(std::string_view key,T && value)130 JsonObjectBuilder &AddProperty(std::string_view key, T &&value) &
131 {
132 Entry();
133 Stringify(key);
134 Append(":");
135 Stringify(std::forward<T>(value));
136 return *this;
137 }
138
139 template <typename T>
AddProperty(std::string_view key,T && value)140 JsonObjectBuilder &&AddProperty(std::string_view key, T &&value) &&
141 {
142 AddProperty(key, std::forward<T>(value));
143 return std::move(*this);
144 }
145 };
146
147 template <char startDelimiter, char endDelimiter>
148 template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonArrayBuilder &>, int>>
Stringify(T && array)149 void JsonBuilderBase<startDelimiter, endDelimiter>::Stringify(T &&array)
150 {
151 JsonArrayBuilder builder;
152 std::invoke(std::forward<T>(array), builder);
153 ss_ << std::move(builder).Build();
154 }
155
156 template <char startDelimiter, char endDelimiter>
157 template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonObjectBuilder &>, int>>
Stringify(T && object)158 void JsonBuilderBase<startDelimiter, endDelimiter>::Stringify(T &&object)
159 {
160 JsonObjectBuilder builder;
161 std::invoke(std::forward<T>(object), builder);
162 ss_ << std::move(builder).Build();
163 }
164 } // namespace panda
165
166 #endif // LIBPANDABASE_UTILS_JSON_BUILDER_H
167