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 29namespace panda { 30class JsonArrayBuilder; 31class JsonObjectBuilder; 32void JsonEscape(std::ostream & /* os */, std::string_view /* string */); 33 34template <char startDelimiter, char endDelimiter> 35class JsonBuilderBase { 36public: 37 JsonBuilderBase() 38 { 39 ss_ << startDelimiter; 40 } 41 42 std::string Build() && 43 { 44 ss_ << endDelimiter; 45 return ss_.str(); 46 } 47 48protected: 49 void Entry() 50 { 51 if (firstEntry_) { 52 firstEntry_ = false; 53 } else { 54 ss_ << ','; 55 } 56 } 57 58 template <typename T> 59 void Append(T &&value) 60 { 61 ss_ << (std::forward<T>(value)); 62 } 63 64 void Stringify(std::nullptr_t) 65 { 66 ss_ << "null"; 67 } 68 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> 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 85 void Stringify(std::string_view string) 86 { 87 JsonEscape(ss_, string); 88 } 89 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 101private: 102 std::stringstream ss_; 103 bool firstEntry_ {true}; 104}; 105 106class JsonArrayBuilder : public JsonBuilderBase<'[', ']'> { 107public: 108 template <typename T> 109 JsonArrayBuilder &Add(T &&value) & 110 { 111 Entry(); 112 Stringify(std::forward<T>(value)); 113 return *this; 114 } 115 116 template <typename T> 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). 125using JsonObjectBuilderBase = JsonBuilderBase<'{', '}'>; 126 127class JsonObjectBuilder : public JsonObjectBuilderBase { 128public: 129 template <typename T> 130 JsonObjectBuilder &AddProperty(const 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> 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 147template <char startDelimiter, char endDelimiter> 148template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonArrayBuilder &>, int>> 149void 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 156template <char startDelimiter, char endDelimiter> 157template <typename T, std::enable_if_t<std::is_invocable_v<T, JsonObjectBuilder &>, int>> 158void 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