@@ -50,7 +50,7 @@ auto invalid(std::string message) -> modulus::error {
|
||||
}
|
||||
|
||||
auto usage() -> std::string {
|
||||
return "usage: vamp-zero [--config PATH] [--check-config] [--health-check] [--version] [--once]";
|
||||
return "usage: vamp-zero [--config PATH] [--check-config] [--health-check] [--build-info] [--once]";
|
||||
}
|
||||
|
||||
auto bind_port(vamp_zero::config const &config) -> std::uint16_t {
|
||||
@@ -127,7 +127,6 @@ auto write_text_atomic(std::filesystem::path const &destination, std::string con
|
||||
|
||||
auto write_heartbeat(vamp_zero::config const &config) -> std::expected<void, modulus::error> {
|
||||
auto heartbeat = boost::json::object{};
|
||||
heartbeat["version"] = VAMP_VERSION;
|
||||
heartbeat["git_sha"] = VAMP_GIT_SHA;
|
||||
heartbeat["status"] = "ok";
|
||||
heartbeat["endpoint"] = "http://" + config.control.zero_ip + ":" + std::to_string(bind_port(config));
|
||||
@@ -457,8 +456,8 @@ auto vamp_zero::parse_command_line(std::int32_t argc, std::char_t **argv) -> std
|
||||
result.check_config = true;
|
||||
} else if (argument == "--health-check") {
|
||||
result.health_check = true;
|
||||
} else if (argument == "--version") {
|
||||
result.print_version = true;
|
||||
} else if (argument == "--build-info") {
|
||||
result.print_build_info = true;
|
||||
} else if (argument == "--once") {
|
||||
result.exit_after_startup = true;
|
||||
} else if (argument == "--help" || argument == "-h") {
|
||||
@@ -469,7 +468,7 @@ auto vamp_zero::parse_command_line(std::int32_t argc, std::char_t **argv) -> std
|
||||
}
|
||||
auto command_count = static_cast<std::uint32_t>(result.check_config ? 1 : 0) +
|
||||
static_cast<std::uint32_t>(result.health_check ? 1 : 0) +
|
||||
static_cast<std::uint32_t>(result.print_version ? 1 : 0) +
|
||||
static_cast<std::uint32_t>(result.print_build_info ? 1 : 0) +
|
||||
static_cast<std::uint32_t>(result.exit_after_startup ? 1 : 0);
|
||||
if (command_count > 1U) {
|
||||
return std::unexpected(invalid("only one command flag may be selected"));
|
||||
@@ -478,8 +477,8 @@ auto vamp_zero::parse_command_line(std::int32_t argc, std::char_t **argv) -> std
|
||||
}
|
||||
|
||||
auto vamp_zero::run(vamp_zero::command_line command) -> std::expected<void, modulus::error> {
|
||||
if (command.print_version) {
|
||||
std::cout << VAMP_VERSION << " " << VAMP_GIT_SHA << '\n';
|
||||
if (command.print_build_info) {
|
||||
std::cout << "product-vamp " << VAMP_GIT_SHA << '\n';
|
||||
return {};
|
||||
}
|
||||
auto config = vamp_zero::load_config(command.config_path);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
namespace {
|
||||
|
||||
constexpr auto application_id = std::int64_t{0x56414D50};
|
||||
constexpr auto catalog_schema_version = std::int64_t{3};
|
||||
|
||||
auto connection_config(vamp_zero::database_config const &config) -> noto::connection_config {
|
||||
auto result = noto::file_connection_config(config.path, "product-vamp");
|
||||
@@ -89,6 +88,34 @@ auto count_value(noto::sqlite_value const &value) -> std::size_t {
|
||||
return count == nullptr || *count <= 0 ? 0U : static_cast<std::size_t>(*count);
|
||||
}
|
||||
|
||||
auto compatible_table(noto::connection &connection, std::string const &table) -> boost::asio::awaitable<std::bool_t> {
|
||||
auto columns = std::vector<std::string_view>{"id", "title", "composer", "key_signature", "source_path", "source_json", "measure_count", "total_pages"};
|
||||
if (table == "practice_exercises") {
|
||||
columns.push_back("exercise_id");
|
||||
}
|
||||
columns.push_back("part");
|
||||
auto parameters = std::vector<noto::sqlite_value>{noto::sqlite_value{table}};
|
||||
auto selected = co_await connection.async_execute("SELECT name, type, \"notnull\", pk FROM pragma_table_info(?1) "
|
||||
"WHERE EXISTS (SELECT 1 FROM pragma_table_list WHERE schema = 'main' AND name = ?1 AND type = 'table' AND strict = 1) ORDER BY cid",
|
||||
std::move(parameters));
|
||||
if (!selected || selected->rows.size() != columns.size()) {
|
||||
co_return false;
|
||||
}
|
||||
for (auto index = std::size_t{}; index < columns.size(); ++index) {
|
||||
auto const &row = selected->rows[index];
|
||||
if (row.size() != 4U) {
|
||||
co_return false;
|
||||
}
|
||||
auto required = std::get_if<std::int64_t>(&row[2].value);
|
||||
auto primary = std::get_if<std::int64_t>(&row[3].value);
|
||||
auto type = index == 6U || index == 7U ? "INTEGER" : "TEXT";
|
||||
if (text_value(row[0]) != columns[index] || text_value(row[1]) != type || required == nullptr || *required != 1 || primary == nullptr || *primary != (index == 0U ? 1 : 0)) {
|
||||
co_return false;
|
||||
}
|
||||
}
|
||||
co_return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, vamp_zero::database_config config, std::bool_t integrity) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
|
||||
@@ -102,11 +129,15 @@ auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, v
|
||||
co_return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "cannot open catalog: " + config.path + "; rebuild or reinstall Product Vamp: " + connected.error().describe()));
|
||||
}
|
||||
auto application = co_await connection.async_application_id();
|
||||
auto version = co_await connection.async_user_version();
|
||||
auto status = std::expected<void, modulus::error>{};
|
||||
if (!application || !version || *application != application_id || *version != catalog_schema_version) {
|
||||
if (!application || *application != application_id) {
|
||||
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "invalid or incompatible catalog: " + config.path + "; rebuild or reinstall Product Vamp"));
|
||||
}
|
||||
for (auto table : {"songs", "practice_exercises"}) {
|
||||
if (status && !(co_await compatible_table(connection, table))) {
|
||||
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "incompatible catalog table " + std::string{table} + ": " + config.path + "; rebuild or reinstall Product Vamp"));
|
||||
}
|
||||
}
|
||||
if (status && integrity) {
|
||||
auto checked = co_await connection.async_execute("PRAGMA quick_check(1)");
|
||||
if (!checked || checked->rows.size() != 1U || checked->rows[0].size() != 1U || text_value(checked->rows[0][0]) != "ok") {
|
||||
|
||||
@@ -50,9 +50,6 @@ auto vamp_zero::parse_notation_document(std::string_view json, std::string sourc
|
||||
return invalid("expected a JSON object");
|
||||
}
|
||||
auto const &root = value.as_object();
|
||||
if (!integer_equals(root, "schema_version", 3)) {
|
||||
return invalid("unsupported schema; regenerate this score and its page assets");
|
||||
}
|
||||
auto music = object_field(root, "music");
|
||||
auto presentation = object_field(root, "presentation");
|
||||
if (music == nullptr || presentation == nullptr) {
|
||||
|
||||
@@ -132,9 +132,8 @@ auto vamp_zero::prepare_playback(vamp_zero::score_document const &score, std::fi
|
||||
return std::unexpected(invalid("regenerate this score to enable playback"));
|
||||
}
|
||||
auto const &object = playback->as_object();
|
||||
auto version = number(object, "version");
|
||||
auto available = object.if_contains("available");
|
||||
if (!version || *version != 1U || !available || !available->is_bool()) {
|
||||
if (!available || !available->is_bool()) {
|
||||
return std::unexpected(invalid("unsupported playback manifest"));
|
||||
}
|
||||
if (!available->as_bool()) {
|
||||
@@ -205,9 +204,8 @@ auto vamp_zero::validate_playback_assets(vamp_zero::score_document const &score,
|
||||
if (playback->is_object()) {
|
||||
auto available = playback->as_object().if_contains("available");
|
||||
if (available && available->is_bool() && !available->as_bool()) {
|
||||
auto version = number(playback->as_object(), "version");
|
||||
auto reason = text(playback->as_object(), "reason");
|
||||
if (version && *version == 1U && reason && !reason->empty()) {
|
||||
if (reason && !reason->empty()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ auto vamp_zero::portal_status_json(vamp_zero::config const &config, vamp_zero::a
|
||||
control["endpoint"] = "http://" + config.control.zero_ip + ":" + endpoint_port(config.control.endpoint.bind);
|
||||
auto root = boost::json::object{};
|
||||
root["device"] = "zero";
|
||||
root["version"] = VAMP_VERSION;
|
||||
root["git_sha"] = VAMP_GIT_SHA;
|
||||
root["control"] = std::move(control);
|
||||
root["state"] = vamp_zero::app_status_json(state);
|
||||
|
||||
Reference in New Issue
Block a user