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 */
19 class alt_string;
20 bool operator<(const char* op1, const alt_string& op2) noexcept;
21 void 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 */
27 class 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
alt_string(const char * str)34 alt_string(const char* str): str_impl(str) {}
alt_string(const char * str,std::size_t count)35 alt_string(const char* str, std::size_t count): str_impl(str, count) {}
alt_string(size_t count,char chr)36 alt_string(size_t count, char chr): str_impl(count, chr) {}
37 alt_string() = default;
38
39 template <typename...TParams>
append(TParams &&...params)40 alt_string& append(TParams&& ...params)
41 {
42 str_impl.append(std::forward<TParams>(params)...);
43 return *this;
44 }
45
push_back(char c)46 void push_back(char c)
47 {
48 str_impl.push_back(c);
49 }
50
51 template <typename op_type>
operator ==(const op_type & op) const52 bool operator==(const op_type& op) const
53 {
54 return str_impl == op;
55 }
56
operator ==(const alt_string & op) const57 bool operator==(const alt_string& op) const
58 {
59 return str_impl == op.str_impl;
60 }
61
62 template <typename op_type>
operator !=(const op_type & op) const63 bool operator!=(const op_type& op) const
64 {
65 return str_impl != op;
66 }
67
operator !=(const alt_string & op) const68 bool operator!=(const alt_string& op) const
69 {
70 return str_impl != op.str_impl;
71 }
72
size() const73 std::size_t size() const noexcept
74 {
75 return str_impl.size();
76 }
77
resize(std::size_t n)78 void resize (std::size_t n)
79 {
80 str_impl.resize(n);
81 }
82
resize(std::size_t n,char c)83 void resize (std::size_t n, char c)
84 {
85 str_impl.resize(n, c);
86 }
87
88 template <typename op_type>
operator <(const op_type & op) const89 bool operator<(const op_type& op) const noexcept
90 {
91 return str_impl < op;
92 }
93
operator <(const alt_string & op) const94 bool operator<(const alt_string& op) const noexcept
95 {
96 return str_impl < op.str_impl;
97 }
98
c_str() const99 const char* c_str() const
100 {
101 return str_impl.c_str();
102 }
103
operator [](std::size_t index)104 char& operator[](std::size_t index)
105 {
106 return str_impl[index];
107 }
108
operator [](std::size_t index) const109 const char& operator[](std::size_t index) const
110 {
111 return str_impl[index];
112 }
113
back()114 char& back()
115 {
116 return str_impl.back();
117 }
118
back() const119 const char& back() const
120 {
121 return str_impl.back();
122 }
123
clear()124 void clear()
125 {
126 str_impl.clear();
127 }
128
data() const129 const value_type* data() const
130 {
131 return str_impl.data();
132 }
133
empty() const134 bool empty() const
135 {
136 return str_impl.empty();
137 }
138
find(const alt_string & str,std::size_t pos=0) const139 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
find_first_of(char c,std::size_t pos=0) const144 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
substr(std::size_t pos=0,std::size_t count=npos) const149 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
replace(std::size_t pos,std::size_t count,const alt_string & str)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
int_to_string(alt_string & target,std::size_t value)167 void int_to_string(alt_string& target, std::size_t value)
168 {
169 target = std::to_string(value).c_str();
170 }
171
172 using 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
operator <(const char * op1,const alt_string & op2)184 bool operator<(const char* op1, const alt_string& op2) noexcept
185 {
186 return op1 < op2.str_impl;
187 }
188
189 TEST_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