1// __ _____ _____ _____ 2// __| | __| | | | JSON for Modern C++ (supporting code) 3// | | |__ | | | | | | version 3.11.2 4// |_____|_____|_____|_|___| https://github.com/nlohmann/json 5// 6// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me> 7// SPDX-FileCopyrightText: 2018 Vitaliy Manushkin <agri@akamo.info> 8// SPDX-License-Identifier: MIT 9 10#include "doctest_compatibility.h" 11 12#include <nlohmann/json.hpp> 13 14#include <string> 15#include <utility> 16 17 18/* forward declarations */ 19class alt_string; 20bool operator<(const char* op1, const alt_string& op2) noexcept; 21void int_to_string(alt_string& target, std::size_t value); 22 23/* 24 * This is virtually a string class. 25 * It covers std::string under the hood. 26 */ 27class alt_string 28{ 29 public: 30 using value_type = std::string::value_type; 31 32 static constexpr auto npos = static_cast<std::size_t>(-1); 33 34 alt_string(const char* str): str_impl(str) {} 35 alt_string(const char* str, std::size_t count): str_impl(str, count) {} 36 alt_string(size_t count, char chr): str_impl(count, chr) {} 37 alt_string() = default; 38 39 template <typename...TParams> 40 alt_string& append(TParams&& ...params) 41 { 42 str_impl.append(std::forward<TParams>(params)...); 43 return *this; 44 } 45 46 void push_back(char c) 47 { 48 str_impl.push_back(c); 49 } 50 51 template <typename op_type> 52 bool operator==(const op_type& op) const 53 { 54 return str_impl == op; 55 } 56 57 bool operator==(const alt_string& op) const 58 { 59 return str_impl == op.str_impl; 60 } 61 62 template <typename op_type> 63 bool operator!=(const op_type& op) const 64 { 65 return str_impl != op; 66 } 67 68 bool operator!=(const alt_string& op) const 69 { 70 return str_impl != op.str_impl; 71 } 72 73 std::size_t size() const noexcept 74 { 75 return str_impl.size(); 76 } 77 78 void resize (std::size_t n) 79 { 80 str_impl.resize(n); 81 } 82 83 void resize (std::size_t n, char c) 84 { 85 str_impl.resize(n, c); 86 } 87 88 template <typename op_type> 89 bool operator<(const op_type& op) const noexcept 90 { 91 return str_impl < op; 92 } 93 94 bool operator<(const alt_string& op) const noexcept 95 { 96 return str_impl < op.str_impl; 97 } 98 99 const char* c_str() const 100 { 101 return str_impl.c_str(); 102 } 103 104 char& operator[](std::size_t index) 105 { 106 return str_impl[index]; 107 } 108 109 const char& operator[](std::size_t index) const 110 { 111 return str_impl[index]; 112 } 113 114 char& back() 115 { 116 return str_impl.back(); 117 } 118 119 const char& back() const 120 { 121 return str_impl.back(); 122 } 123 124 void clear() 125 { 126 str_impl.clear(); 127 } 128 129 const value_type* data() const 130 { 131 return str_impl.data(); 132 } 133 134 bool empty() const 135 { 136 return str_impl.empty(); 137 } 138 139 std::size_t find(const alt_string& str, std::size_t pos = 0) const 140 { 141 return str_impl.find(str.str_impl, pos); 142 } 143 144 std::size_t find_first_of(char c, std::size_t pos = 0) const 145 { 146 return str_impl.find_first_of(c, pos); 147 } 148 149 alt_string substr(std::size_t pos = 0, std::size_t count = npos) const 150 { 151 std::string s = str_impl.substr(pos, count); 152 return {s.data(), s.size()}; 153 } 154 155 alt_string& replace(std::size_t pos, std::size_t count, const alt_string& str) 156 { 157 str_impl.replace(pos, count, str.str_impl); 158 return *this; 159 } 160 161 private: 162 std::string str_impl {}; 163 164 friend bool operator<(const char* /*op1*/, const alt_string& /*op2*/) noexcept; 165}; 166 167void int_to_string(alt_string& target, std::size_t value) 168{ 169 target = std::to_string(value).c_str(); 170} 171 172using alt_json = nlohmann::basic_json < 173 std::map, 174 std::vector, 175 alt_string, 176 bool, 177 std::int64_t, 178 std::uint64_t, 179 double, 180 std::allocator, 181 nlohmann::adl_serializer >; 182 183 184bool operator<(const char* op1, const alt_string& op2) noexcept 185{ 186 return op1 < op2.str_impl; 187} 188 189TEST_CASE("alternative string type") 190{ 191 SECTION("dump") 192 { 193 { 194 alt_json doc; 195 doc["pi"] = 3.141; 196 alt_string dump = doc.dump(); 197 CHECK(dump == R"({"pi":3.141})"); 198 } 199 200 { 201 alt_json doc; 202 doc["happy"] = true; 203 alt_string dump = doc.dump(); 204 CHECK(dump == R"({"happy":true})"); 205 } 206 207 { 208 alt_json doc; 209 doc["name"] = "I'm Batman"; 210 alt_string dump = doc.dump(); 211 CHECK(dump == R"({"name":"I'm Batman"})"); 212 } 213 214 { 215 alt_json doc; 216 doc["nothing"] = nullptr; 217 alt_string dump = doc.dump(); 218 CHECK(dump == R"({"nothing":null})"); 219 } 220 221 { 222 alt_json doc; 223 doc["answer"]["everything"] = 42; 224 alt_string dump = doc.dump(); 225 CHECK(dump == R"({"answer":{"everything":42}})"); 226 } 227 228 { 229 alt_json doc; 230 doc["list"] = { 1, 0, 2 }; 231 alt_string dump = doc.dump(); 232 CHECK(dump == R"({"list":[1,0,2]})"); 233 } 234 235 { 236 alt_json doc; 237 doc["object"] = { {"currency", "USD"}, {"value", 42.99} }; 238 alt_string dump = doc.dump(); 239 CHECK(dump == R"({"object":{"currency":"USD","value":42.99}})"); 240 } 241 } 242 243 SECTION("parse") 244 { 245 auto doc = alt_json::parse(R"({"foo": "bar"})"); 246 alt_string dump = doc.dump(); 247 CHECK(dump == R"({"foo":"bar"})"); 248 } 249 250 SECTION("items") 251 { 252 auto doc = alt_json::parse(R"({"foo": "bar"})"); 253 254 for (const auto& item : doc.items()) 255 { 256 CHECK(item.key() == "foo"); 257 CHECK(item.value() == "bar"); 258 } 259 260 auto doc_array = alt_json::parse(R"(["foo", "bar"])"); 261 262 for (const auto& item : doc_array.items()) 263 { 264 if (item.key() == "0" ) 265 { 266 CHECK( item.value() == "foo" ); 267 } 268 else if (item.key() == "1" ) 269 { 270 CHECK(item.value() == "bar"); 271 } 272 else 273 { 274 CHECK(false); 275 } 276 } 277 } 278 279 SECTION("equality") 280 { 281 alt_json doc; 282 doc["Who are you?"] = "I'm Batman"; 283 284 CHECK("I'm Batman" == doc["Who are you?"]); 285 CHECK(doc["Who are you?"] == "I'm Batman"); 286 CHECK_FALSE("I'm Batman" != doc["Who are you?"]); 287 CHECK_FALSE(doc["Who are you?"] != "I'm Batman"); 288 289 CHECK("I'm Bruce Wayne" != doc["Who are you?"]); 290 CHECK(doc["Who are you?"] != "I'm Bruce Wayne"); 291 CHECK_FALSE("I'm Bruce Wayne" == doc["Who are you?"]); 292 CHECK_FALSE(doc["Who are you?"] == "I'm Bruce Wayne"); 293 294 { 295 const alt_json& const_doc = doc; 296 297 CHECK("I'm Batman" == const_doc["Who are you?"]); 298 CHECK(const_doc["Who are you?"] == "I'm Batman"); 299 CHECK_FALSE("I'm Batman" != const_doc["Who are you?"]); 300 CHECK_FALSE(const_doc["Who are you?"] != "I'm Batman"); 301 302 CHECK("I'm Bruce Wayne" != const_doc["Who are you?"]); 303 CHECK(const_doc["Who are you?"] != "I'm Bruce Wayne"); 304 CHECK_FALSE("I'm Bruce Wayne" == const_doc["Who are you?"]); 305 CHECK_FALSE(const_doc["Who are you?"] == "I'm Bruce Wayne"); 306 } 307 } 308 309 SECTION("JSON pointer") 310 { 311 // conversion from json to alt_json fails to compile (see #3425); 312 // attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer) 313 // (*) disable implicit conversion for json_refs of any basic_json type 314 // alt_json j = R"( 315 // { 316 // "foo": ["bar", "baz"] 317 // } 318 // )"_json; 319 auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})"); 320 321 CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]); 322 CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]); 323 } 324} 325