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