6#include "nlohmann/json.hpp"
10using json = nlohmann::json;
14 const auto now = std::chrono::system_clock::now();
15 const auto t = std::chrono::system_clock::to_time_t(now);
19 gmtime_s(&tm_utc, &t);
21 gmtime_r(&t, &tm_utc);
23 std::strftime(buf,
sizeof(buf),
"%Y-%m-%dT%H:%M:%SZ", &tm_utc);
28 std::vector<std::string> args;
29 if (!opts.rom_path.empty()) {
30 args.push_back(
"--rom=" + opts.rom_path);
32 if (opts.strict_readiness) {
33 args.push_back(
"--strict-readiness");
35 if (opts.min_d6_track_rooms != 0) {
36 args.push_back(
"--min-d6-track-rooms=" +
37 std::to_string(opts.min_d6_track_rooms));
39 args.push_back(
"--format=json");
40 if (!opts.report_path.empty()) {
41 args.push_back(
"--report=" + opts.report_path);
47 std::vector<std::string> args;
48 if (!opts.rom_path.empty()) {
49 args.push_back(
"--rom=" + opts.rom_path);
51 if (!opts.required_collision_rooms.empty()) {
52 args.push_back(
"--required-collision-rooms=" +
53 opts.required_collision_rooms);
55 if (opts.require_write_support) {
56 args.push_back(
"--require-write-support");
58 if (opts.skip_collision_maps) {
59 args.push_back(
"--skip-collision-maps");
61 args.push_back(
"--format=json");
62 if (!opts.report_path.empty()) {
63 args.push_back(
"--report=" + opts.report_path);
69 const std::vector<std::string>& args) {
70 std::string cmd =
"z3ed " + command_name;
71 for (
const auto& arg : args) {
78 const auto doc = json::parse(json_str,
nullptr,
false);
79 if (doc.is_discarded()) {
80 return absl::InvalidArgumentError(
81 "ParseSmokeCheckOutput: JSON parse failed");
84 if (!doc.contains(
"Oracle Smoke Check")) {
85 return absl::InvalidArgumentError(
86 "ParseSmokeCheckOutput: missing 'Oracle Smoke Check' key");
88 const auto& root = doc.at(
"Oracle Smoke Check");
89 const auto& checks = root.value(
"checks", json::object());
92 r.ok = root.value(
"ok",
false);
93 r.status = root.value(
"status",
"");
94 r.strict_readiness = root.value(
"strict_readiness",
false);
96 const auto& d4 = checks.value(
"d4_zora_temple", json::object());
97 r.d4.structural_ok = d4.value(
"structural_ok",
false);
98 r.d4.required_rooms_check = d4.value(
"required_rooms_check",
"skipped");
99 if (d4.contains(
"required_rooms_ok")) {
100 r.d4.required_rooms_ok = d4.at(
"required_rooms_ok").get<
bool>();
103 const auto& d6 = checks.value(
"d6_goron_mines", json::object());
104 r.d6.ok = d6.value(
"ok",
false);
105 r.d6.track_rooms_found = d6.value(
"track_rooms_found", 0);
106 r.d6.min_track_rooms = d6.value(
"min_track_rooms", 0);
107 r.d6.meets_min_track_rooms = d6.value(
"meets_min_track_rooms",
false);
109 const auto& d3 = checks.value(
"d3_kalyxo_castle", json::object());
110 r.d3.readiness_check = d3.value(
"readiness_check",
"skipped");
111 if (d3.contains(
"ok")) {
112 r.d3.ok = d3.at(
"ok").get<
bool>();
119 const std::string& json_str) {
120 const auto doc = json::parse(json_str,
nullptr,
false);
121 if (doc.is_discarded()) {
122 return absl::InvalidArgumentError(
123 "ParsePreflightOutput: JSON parse failed");
125 if (!doc.contains(
"Dungeon Oracle Preflight")) {
126 return absl::InvalidArgumentError(
127 "ParsePreflightOutput: missing 'Dungeon Oracle Preflight' key");
129 const auto& root = doc.at(
"Dungeon Oracle Preflight");
132 r.ok = root.value(
"ok",
false);
133 r.error_count = root.value(
"error_count", 0);
134 r.water_fill_region_ok = root.value(
"water_fill_region_ok",
false);
135 r.water_fill_table_ok = root.value(
"water_fill_table_ok",
false);
136 r.custom_collision_maps_ok = root.value(
"custom_collision_maps_ok",
false);
137 r.required_rooms_check = root.value(
"required_rooms_check",
"skipped");
138 if (root.contains(
"required_rooms_ok")) {
139 r.required_rooms_ok = root.at(
"required_rooms_ok").get<
bool>();
141 r.status = root.value(
"status",
"");
143 for (
const auto& e : root.value(
"errors",
json::array())) {
145 err.code = e.value(
"code",
"");
146 err.message = e.value(
"message",
"");
147 err.status_code = e.value(
"status",
"");
148 if (e.contains(
"room_id")) {
149 err.room_id = e.at(
"room_id").get<std::string>();
151 r.errors.push_back(std::move(err));
std::vector< std::string > BuildSmokeArgs(const SmokeOptions &opts)
std::vector< std::string > BuildPreflightArgs(const PreflightOptions &opts)
std::string CurrentTimestamp()
std::string BuildCliCommand(const std::string &command_name, const std::vector< std::string > &args)
absl::StatusOr< SmokeResult > ParseSmokeCheckOutput(const std::string &json_str)
absl::StatusOr< PreflightResult > ParsePreflightOutput(const std::string &json_str)