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