• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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