header_utils
Loading...
Searching...
No Matches
json_helpers.h
1
4
5#pragma once
6
7#include <nlohmann/json.hpp>
8#include <fstream>
9#include "string_ops.h"
10#include "mmap.h"
11#include "expected.h"
12#include "functional.h"
13
14namespace ghassanpl::formats
15{
16
17 /*
18 namespace text
19 {
23
26 inline std::string load_file(std::filesystem::path const& from, std::error_code& ec)
27 {
28 auto source = ghassanpl::make_mmap_source<char>(from, ec);
29 return ec ? std::string{} : std::string{ source.begin(), source.end() };
30 }
31
34 inline std::string load_file(std::filesystem::path const& from)
35 {
36 std::error_code ec;
37 auto source = ghassanpl::make_mmap_source<char>(from, ec);
38 if (ec == std::errc::file_too_large) /// zero-sized file
39 return {};
40 if (ec)
41 throw std::runtime_error(format("file '{}' could not be loaded: {}", from.string(), ec.message()));
42 return std::string{ source.begin(), source.end() };
43 }
44
46 inline expected<std::string, std::error_code> try_load_file(std::filesystem::path const& from)
47 {
48 std::error_code ec;
49 auto result = load_file(from, ec);
50 return ec ? unexpected(ec) : expected<std::string, std::error_code>{ std::move(result) };
51 }
52
53 inline bool save_file(std::filesystem::path const& to, std::string_view string, std::error_code& ec)
54 {
56 std::ofstream out{ to };
57 out.write(string.data(), string.size());
58 return out.fail();
59 }
60
61 inline void save_file(std::filesystem::path const& to, std::string_view string)
62 {
63 std::ofstream out{ to };
64 out.exceptions(std::ios::badbit | std::ios::failbit);
65 out.write(string.data(), string.size());
66 }
68 }
69
70 namespace text_lines
71 {
72
77
78 inline std::vector<std::string> load_file(std::filesystem::path const& from, std::error_code& ec)
79 {
80 auto source = ghassanpl::make_mmap_source<char>(from, ec);
81 if (ec)
82 return {};
83
84 return resulting([&](std::vector<std::string>& result) {
85 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', op::emplace_back_to(result));
86 });
87 }
88
89 inline std::vector<std::string> load_file(std::filesystem::path const& from)
90 {
91 std::error_code ec;
92 auto source = ghassanpl::make_mmap_source<char>(from, ec);
93 if (ec == std::errc::file_too_large) /// zero-sized file
94 return {};
95 if (ec)
96 throw std::runtime_error(format("file '{}' could not be loaded: {}", from.string(), ec.message()));
97
98 return resulting([&](std::vector<std::string>& result) {
99 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', op::emplace_back_to(result));
100 });
101 }
102
103 template <typename CALLBACK>
104 requires std::invocable<CALLBACK, std::string_view>
105 inline void load_file(std::filesystem::path const& from, CALLBACK&& callback)
106 {
107 std::error_code ec;
108 auto source = ghassanpl::make_mmap_source<char>(from, ec);
109 if (ec == std::errc::file_too_large) /// zero-sized file
110 return;
111 if (ec)
112 throw std::runtime_error(format("file '{}' could not be loaded: {}", from.string(), ec.message()));
113
114 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', callback);
115 }
116
117 inline expected<std::vector<std::string>, std::error_code> try_load_file(std::filesystem::path const& from)
118 {
119 std::error_code ec;
120 auto result = load_file(from, ec);
121 return ec ? unexpected(ec) : expected<std::vector<std::string>, std::error_code>{ std::move(result) };
122 }
123
124 template <std::ranges::range T>
125 inline bool save_file(std::filesystem::path const& to, T string_range, std::error_code& ec)
126 {
128 std::ofstream out{ to };
129 for (auto& string : string_range)
130 {
131 out.write(std::to_address(std::ranges::begin(string)), std::ranges::size(string));
132 out << "\n";
133 }
134 return out.fail();
135 }
136
137 template <std::ranges::range T>
138 inline void save_file(std::filesystem::path const& to, T string_range)
139 {
140 std::ofstream out{ to };
141 out.exceptions(std::ios::badbit | std::ios::failbit);
142 for (auto& string : string_range)
143 {
144 out.write(std::to_address(std::ranges::begin(string)), std::ranges::size(string));
145 out << "\n";
146 }
147 }
149 }
150 */
151
152 namespace json
153 {
157
158 inline const nlohmann::json empty_json = nlohmann::json{};
159 inline const nlohmann::json empty_json_array = nlohmann::json::array();
160 inline const nlohmann::json empty_json_object = nlohmann::json::object();
161
163 using jtype = nlohmann::json::value_t;
164
165 inline expected<nlohmann::json, std::error_code> load_file(std::filesystem::path const& from)
166 {
167 std::error_code ec{};
168 auto source = ghassanpl::make_mmap_source<char>(from, ec);
169 if (ec != std::error_code{})
170 return unexpected(ec);
171 return nlohmann::json::parse(source);
172 }
173
174 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json or_json)
175 {
176 std::error_code ec;
177 auto source = ghassanpl::make_mmap_source<char>(from, ec);
178 return ec ? std::move(or_json) : nlohmann::json::parse(source);
179 }
180
181 inline nlohmann::json try_load_file(std::filesystem::path const& from)
182 {
183 std::error_code ec;
184 auto source = ghassanpl::make_mmap_source<char>(from, ec);
185 return ec ? empty_json : nlohmann::json::parse(source);
186 }
187
188 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j, bool pretty = true)
189 {
190 std::ofstream out{ to };
191 nlohmann::detail::serializer<nlohmann::json> s{ nlohmann::detail::output_adapter<char, std::string>(out), '\t', nlohmann::detail::error_handler_t::strict };
192 s.dump(j, pretty, false, 1);
193 }
194
195 constexpr const char* type_name(nlohmann::json::value_t type) noexcept
196 {
197 switch (type)
198 {
199 using enum nlohmann::detail::value_t;
200 case null:
201 return "null";
202 case object:
203 return "object";
204 case array:
205 return "array";
206 case string:
207 return "string";
208 case boolean:
209 return "boolean";
210 case binary:
211 return "binary";
212 case discarded:
213 return "discarded";
214 default:
215 return "number";
216 }
217 }
218
220 template <typename VISIT_FUNC>
221 auto visit(nlohmann::json const& j, VISIT_FUNC&& func)
222 {
223 switch (j.type())
224 {
225 using enum nlohmann::detail::value_t;
226 case object: return func(j.get_ref<nlohmann::json::object_t const&>());
227 case array: return func(j.get_ref<nlohmann::json::array_t const&>());
228 case string: return func(j.get_ref<nlohmann::json::string_t const&>());
229 case boolean: return func(j.get_ref<nlohmann::json::boolean_t const&>());
230 case number_integer: return func(j.get_ref<nlohmann::json::number_integer_t const&>());
231 case number_unsigned: return func(j.get_ref<nlohmann::json::number_unsigned_t const&>());
232 case number_float: return func(j.get_ref<nlohmann::json::number_float_t const&>());
233 case binary: return func(j.get_ref<nlohmann::json::binary_t const&>());
234 default:
235 return func(nullptr);
236 }
237 }
238
240 }
241
242 namespace ubjson
243 {
247
248 inline expected<nlohmann::json, std::error_code> load_file(std::filesystem::path const& from)
249 {
250 std::error_code ec{};
251 auto source = ghassanpl::make_mmap_source<char>(from, ec);
252 if (ec != std::error_code{})
253 return unexpected(ec);
254 return nlohmann::json::from_ubjson(source);
255 }
256
257 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json or_json)
258 {
259 std::error_code ec;
260 auto source = ghassanpl::make_mmap_source<char>(from, ec);
261 return ec ? std::move(or_json) : nlohmann::json::from_ubjson(source);
262 }
263
264 inline nlohmann::json try_load_file(std::filesystem::path const& from)
265 {
266 std::error_code ec;
267 auto source = ghassanpl::make_mmap_source<char>(from, ec);
268 return ec ? json::empty_json : nlohmann::json::from_ubjson(source);
269 }
270
271 inline expected<void, std::error_code> save_file(std::filesystem::path const& to, nlohmann::json const& j)
272 {
273 std::ofstream out;
274 std::ios_base::iostate exceptionMask = out.exceptions() | std::ios::failbit;
275 out.exceptions(exceptionMask);
276 try
277 {
278 out.open(to, std::ios::binary);
279 }
280 catch (std::ios_base::failure& e)
281 {
282 return unexpected(e.code());
283 }
284 nlohmann::json::to_ubjson(j, nlohmann::detail::output_adapter<char, std::string>(out), true, true);
285 return {};
286 }
287
289 }
290
291
292 namespace cbor
293 {
297
298 inline expected<nlohmann::json, std::error_code> load_file(std::filesystem::path const& from)
299 {
300 std::error_code ec{};
301 auto source = ghassanpl::make_mmap_source<uint8_t>(from, ec);
302 if (ec != std::error_code{})
303 return unexpected(ec);
304 return nlohmann::json::from_cbor(source);
305 }
306
307 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json or_json)
308 {
309 std::error_code ec;
310 auto source = ghassanpl::make_mmap_source<uint8_t>(from, ec);
311 return ec ? std::move(or_json) : nlohmann::json::from_cbor(source);
312 }
313
314 inline nlohmann::json try_load_file(std::filesystem::path const& from)
315 {
316 std::error_code ec;
317 auto source = ghassanpl::make_mmap_source<uint8_t>(from, ec);
318 return ec ? json::empty_json : nlohmann::json::from_cbor(source);
319 }
320
321 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j)
322 {
323 std::ofstream out{ to, std::ios::binary };
324 nlohmann::json::to_cbor(j, nlohmann::detail::output_adapter<char, std::string>(out));
325 }
326
328 }
329 /*
330 namespace json
331 {
335
338 inline nlohmann::json const& get(nlohmann::json const& g, std::string_view key, jtype type = jtype::discarded)
339 {
340 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
341 return *it;
342 return json::empty_json;
343 }
344
347 inline std::string get(nlohmann::json const& g, std::string_view key, std::string_view default_value, jtype type = jtype::discarded)
348 {
349 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
350 return (std::string)*it;
351 return std::string{ default_value };
352 }
353
356 template <std::integral T>
357 inline T get(nlohmann::json const& g, std::string_view key, T default_value, jtype type = jtype::discarded)
358 {
359 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
360 return (T)*it;
361 return default_value;
362 }
363
366 template <std::floating_point T>
367 inline T get(nlohmann::json const& g, std::string_view key, T default_value, jtype type = jtype::discarded)
368 {
369 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
370 return (T)*it;
371 return default_value;
372 }
373
375 inline nlohmann::json const& get_array(nlohmann::json const& g, std::string_view key)
376 {
377 if (auto it = g.find(key); it != g.end() && it->type() == jtype::array)
378 return *it;
379 return json::empty_json_array;
380 }
381
384 template <typename T>
385 inline void field(T& val, nlohmann::json const& g, std::string_view key)
386 {
387 try
388 {
389 auto it = g.find(key);
390 if (it != g.end())
391 {
392 val = *it;
393 return;
394 }
395 }
396 catch (...)
397 {
398 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at key \"{}\" to type {}", key, typeid(T).name()) });
399 }
400
401 throw std::runtime_error(std::format("no key \"{}\" found", key));
402 }
403
406 template <typename T>
407 inline void field(T& val, nlohmann::json const& g, size_t key)
408 {
409 try
410 {
411 val = g.at(key);
412 return;
413 }
414 catch (...)
415 {
416 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at element {} to type {}", key, typeid(T).name()) });
417 }
418 }
419
422 template <typename T>
423 inline bool field_opt(T& val, nlohmann::json const& g, std::string_view key)
424 {
425 try
426 {
427 auto it = g.find(key);
428 if (it != g.end())
429 {
430 val = *it;
431 return true;
432 }
433 }
434 catch (...)
435 {
436 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at key \"{}\" to type {}", key, typeid(T).name()) });
437 }
438 return false;
439 }
440
442 }
443
444 namespace ubjson
445 {
449 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json const& or_json = json::empty_json)
450 {
451 std::error_code ec;
452 auto source = ghassanpl::make_mmap_source<char>(from, ec);
453 return ec ? or_json : nlohmann::json::from_ubjson(source);
454 }
455
456 inline nlohmann::json load_file(std::filesystem::path const& from, std::error_code& ec)
457 {
458 auto source = ghassanpl::make_mmap_source<char>(from, ec);
459 return ec ? nlohmann::json{} : nlohmann::json::from_ubjson(source);
460 }
461
462 inline nlohmann::json load_file(std::filesystem::path const& from)
463 {
464 try
465 {
466 return nlohmann::json::from_ubjson(ghassanpl::make_mmap_source<char>(from));
467 }
468 catch (...)
469 {
470 std::throw_with_nested(std::runtime_error{ format("while trying to load ubjson file {}", from.string()) });
471 }
472 }
473
474 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j)
475 {
476 std::ofstream out{ to };
477 nlohmann::json::to_ubjson(j, nlohmann::detail::output_adapter<char, std::string>(out), true, true);
478 }
480 }
481
482 namespace cbor
483 {
487
489 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json const& or_json = json::empty_json)
490 {
491 std::error_code ec;
492 auto source = ghassanpl::make_mmap_source<char>(from, ec);
493 return ec ? or_json : nlohmann::json::from_cbor(source);
494 }
495
497 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j)
498 {
499 std::ofstream out{ to };
500 nlohmann::json::to_cbor(j, nlohmann::detail::output_adapter<char, std::string>(out));
501 }
503 }
504 */
505}
nlohmann::json::value_t jtype
Smaller name for nlohmann::json::value_t.
auto visit(nlohmann::json const &j, VISIT_FUNC &&func)
Calls func with the actual value inside j; similar to std::visit.
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:386