vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
json_file.h
1#pragma once
2
3#include <cmath>
4#include <filesystem>
5#include <fstream>
6#include <string>
7#include <system_error>
8
9#include <nlohmann/json.hpp>
10
11#include "logger.h"
12
13namespace Vkm::Engine::detail {
14
25inline bool readJsonFile(const std::filesystem::path& path, nlohmann::json& out, const char* what) {
26 std::ifstream in(path);
27 if (!in) {
28 LOG_ERROR_C("IO", "%s: cannot open '%s'", what, path.string().c_str());
29 return false;
30 }
31 try {
32 in >> out;
33 } catch (const nlohmann::json::exception& e) {
34 LOG_ERROR_C("IO", "%s: malformed JSON in '%s': %s", what, path.string().c_str(), e.what());
35 return false;
36 }
37 return true;
38}
39
50inline void writeNonFiniteAsZero(nlohmann::json& node, const char* what, std::string& path) {
51 if (node.is_object()) {
52 const std::size_t parent = path.size();
53 for (auto& field : node.items()) {
54 path += '/';
55 path += field.key();
56 writeNonFiniteAsZero(field.value(), what, path);
57 path.resize(parent);
58 }
59 return;
60 }
61 if (node.is_array()) {
62 const std::size_t parent = path.size();
63 for (std::size_t i = 0; i < node.size(); ++i) {
64 path += '/';
65 path += std::to_string(i);
66 writeNonFiniteAsZero(node[i], what, path);
67 path.resize(parent);
68 }
69 return;
70 }
71 if (!node.is_number_float() || std::isfinite(node.get<double>())) return;
72
73 LOG_WARNING_C("IO", "%s: %s is not a finite number; written as 0", what, path.c_str());
74 node = 0.0;
75}
76
83inline void writeNonFiniteAsZero(nlohmann::json& doc, const char* what) {
84 std::string path;
85 writeNonFiniteAsZero(doc, what, path);
86}
87
99inline bool writeJsonFile(const std::filesystem::path& path, nlohmann::json& doc, const char* what) {
100 writeNonFiniteAsZero(doc, what);
101
102 std::error_code ec;
103 if (!path.parent_path().empty()) std::filesystem::create_directories(path.parent_path(), ec);
104
105 const std::filesystem::path tmp = std::filesystem::path(path).concat(".tmp");
106 {
107 std::ofstream out(tmp);
108 if (!out) {
109 LOG_ERROR_C("IO", "%s: cannot open '%s' for writing", what, tmp.string().c_str());
110 return false;
111 }
112 // Trailing newline, or every diff reports "no newline at end of file".
113 out << doc.dump(2) << '\n';
114 out.close();
115 if (!out) {
116 LOG_ERROR_C("IO", "%s: write to '%s' failed", what, tmp.string().c_str());
117 std::filesystem::remove(tmp, ec);
118 return false;
119 }
120 }
121 std::filesystem::rename(tmp, path, ec);
122 if (ec) {
123 LOG_ERROR_C(
124 "IO",
125 "%s: rename '%s' -> '%s' failed: %s",
126 what,
127 tmp.string().c_str(),
128 path.string().c_str(),
129 ec.message().c_str()
130 );
131 std::filesystem::remove(tmp, ec);
132 return false;
133 }
134 return true;
135}
136
137} // namespace Vkm::Engine::detail