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