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