diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de5b8c7..9f3879da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,4 +48,4 @@ endif() ################################################################################ add_subdirectory(daemon) add_subdirectory(web) -add_subdirectory(dbcli) +add_subdirectory(db-cli) diff --git a/cmake/db-cli-deps.cmake b/cmake/db-cli-deps.cmake new file mode 100644 index 00000000..a1723f5e --- /dev/null +++ b/cmake/db-cli-deps.cmake @@ -0,0 +1,54 @@ +################################################################################ +# Dependencies for db-cli +# +# See also deps.cmake and bundled-deps.cmake +################################################################################ + +add_library(db-cli-deps INTERFACE) + +find_package(OpenSSL REQUIRED) +target_link_libraries(db-cli-deps INTERFACE openssl::openssl) + +find_package(fmt REQUIRED) +target_link_libraries(db-cli-deps INTERFACE fmt::fmt) + +find_package(CLI11 REQUIRED) +target_link_libraries(db-cli-deps INTERFACE CLI11::CLI11) + +find_package(lmdb REQUIRED) +target_link_libraries(db-cli-deps INTERFACE lmdb::lmdb) + +find_package(cereal REQUIRED) +target_link_libraries(db-cli-deps INTERFACE cereal::cereal) + +find_package(libcoro REQUIRED) +target_link_libraries(db-cli-deps INTERFACE libcoro::libcoro) + +find_package(Boost REQUIRED) +target_link_libraries(db-cli-deps INTERFACE boost::boost) + +find_package(frozen REQUIRED) +target_link_libraries(db-cli-deps INTERFACE frozen::frozen) + +find_package(LibArchive REQUIRED) +target_link_libraries(db-cli-deps INTERFACE LibArchive::LibArchive) + +find_package(phmap REQUIRED) +target_link_libraries(db-cli-deps INTERFACE phmap) + + +################################################################################ +# Bundled Dependencies for db-cli +# +################################################################################ + +include(FetchContent) + +FetchContent_Declare( + lmdbxx + GIT_REPOSITORY https://github.com/hoytech/lmdbxx + GIT_TAG b12d1b12f4c9793aef8bed03d07ecbf789e810b4 + GIT_PROGRESS TRUE +) + +FetchContent_MakeAvailable(lmdbxx) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 27a58e97..9f799c9e 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -10,7 +10,7 @@ find_package(OpenSSL REQUIRED) target_link_libraries(deps INTERFACE openssl::openssl) find_package(Boost REQUIRED) -target_link_libraries(deps INTERFACE Boost::boost) +target_link_libraries(deps INTERFACE boost::boost) find_package(date REQUIRED) target_link_libraries(deps INTERFACE date::date) diff --git a/commitlint.config.ts b/commitlint.config.ts index 620e577c..06bffeb0 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -14,7 +14,7 @@ module.exports = { 'always', [ 'daemon', - 'dbcli', + 'db-cli', 'web', ] ], diff --git a/conanfile.py b/conanfile.py index 216cab62..21c2fb9c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -33,6 +33,7 @@ def requirements(self): self.requires("cereal/1.3.2") self.requires("libcoro/0.12.1") self.requires("scope-lite/0.2.0") + self.requires("cli11/2.4.2") if self.options.testing: print("Testing enabled") diff --git a/daemon/utilities/MemoryLiterals.h b/daemon/utilities/MemoryLiterals.h new file mode 100644 index 00000000..68eae511 --- /dev/null +++ b/daemon/utilities/MemoryLiterals.h @@ -0,0 +1,57 @@ +/* === This file is part of bxt === + * + * SPDX-FileCopyrightText: 2024 Artem Grinev + * SPDX-License-Identifier: AGPL-3.0-or-later + * + */ + +#pragma once + +#include + +namespace bxt::MemoryLiterals { + +constexpr std::size_t BinaryBase = 1024UL; +constexpr std::size_t DecimalBase = 1000UL; + +constexpr std::size_t operator"" _KiB(unsigned long long size) { + return size * BinaryBase; +} + +constexpr std::size_t operator"" _MiB(unsigned long long size) { + return size * BinaryBase * BinaryBase; +} + +constexpr std::size_t operator"" _GiB(unsigned long long size) { + return size * BinaryBase * BinaryBase * BinaryBase; +} + +constexpr std::size_t operator"" _TiB(unsigned long long size) { + return size * BinaryBase * BinaryBase * BinaryBase * BinaryBase; +} + +constexpr std::size_t operator"" _PiB(unsigned long long size) { + return size * BinaryBase * BinaryBase * BinaryBase * BinaryBase * BinaryBase; +} + +constexpr std::size_t operator"" _KB(unsigned long long size) { + return size * DecimalBase; +} + +constexpr std::size_t operator"" _MB(unsigned long long size) { + return size * DecimalBase * DecimalBase; +} + +constexpr std::size_t operator"" _GB(unsigned long long size) { + return size * DecimalBase * DecimalBase * DecimalBase; +} + +constexpr std::size_t operator"" _TB(unsigned long long size) { + return size * DecimalBase * DecimalBase * DecimalBase * DecimalBase; +} + +constexpr std::size_t operator"" _PB(unsigned long long size) { + return size * DecimalBase * DecimalBase * DecimalBase * DecimalBase * DecimalBase; +} + +} // namespace bxt::MemoryLiterals diff --git a/dbcli/CMakeLists.txt b/db-cli/CMakeLists.txt similarity index 72% rename from dbcli/CMakeLists.txt rename to db-cli/CMakeLists.txt index cef643f9..4d272c36 100644 --- a/dbcli/CMakeLists.txt +++ b/db-cli/CMakeLists.txt @@ -1,10 +1,12 @@ ################################################################################ -# Set up dbcli utility +# Set up db-cli utility ################################################################################ -add_executable( - dbcli - dbcli.cpp +include("${CMAKE_SOURCE_DIR}/cmake/db-cli-deps.cmake") + +add_executable(db-cli + cli.cpp + validation.h ../daemon/core/domain/enums/PoolLocation.cpp ../daemon/utilities/alpmdb/Desc.cpp ../daemon/utilities/alpmdb/PkgInfo.cpp @@ -12,10 +14,10 @@ add_executable( ../daemon/utilities/libarchive/Reader.cpp ) -target_link_libraries(dbcli PRIVATE deps) +target_link_libraries(db-cli PRIVATE db-cli-deps) target_include_directories( - dbcli + db-cli PRIVATE ${CURL_INCLUDE_DIR} ${lmdbxx_SOURCE_DIR}/include diff --git a/db-cli/cli.cpp b/db-cli/cli.cpp new file mode 100644 index 00000000..e95914e6 --- /dev/null +++ b/db-cli/cli.cpp @@ -0,0 +1,181 @@ + +/* === This file is part of bxt === + * + * SPDX-FileCopyrightText: 2024 Artem Grinev + * SPDX-License-Identifier: AGPL-3.0-or-later + * + */ +// Local +#include "validation.h" + +// bxt +#include +#include +#include +#include + +// External +#include +#include +#include +#include +#include +#include +#include + +// STL +#include + +namespace bxt::cli { + +namespace { + using namespace bxt::MemoryLiterals; + constexpr size_t LmdbMaxDbs = 128; + constexpr size_t LmdbMapSize = 50_GiB; +} // namespace + +using Serializer = bxt::Utilities::LMDB::CerealSerializer; +namespace handlers { + int list(lmdb::txn& transaction, lmdb::dbi& db, std::string const& prefix) { + auto cursor = lmdb::cursor::open(transaction, db); + if (!prefix.empty()) { + std::string_view key; + if (!cursor.get(key, MDB_SET_RANGE) || !key.starts_with(prefix)) { + fmt::print(stderr, "No packages found with prefix {}\n", prefix); + return 1; + } + while (cursor.get(key, MDB_NEXT) && key.starts_with(prefix)) { + fmt::print("{}\n", key); + } + } else { + std::string_view key; + while (cursor.get(key, MDB_NEXT)) { + fmt::print("{}\n", key); + } + } + return 0; + } + + int get(lmdb::txn& transaction, lmdb::dbi& db, std::string const& key) { + std::string_view data; + auto result = db.get(transaction, key, data); + if (!result) { + fmt::print(stderr, "Failed to retrieve value or value not found.\n"); + return 1; + } + + auto const package = Serializer::deserialize(std::string(data)); + if (!package.has_value()) { + fmt::print(stderr, "Failed to deserialize package.\n"); + return 1; + } + + fmt::print("{}\nIs any arch: {}\nDescriptions:\n", package->id.to_string(), + package->is_any_architecture); + for (auto const& [key, value] : package->descriptions) { + fmt::print("==={}===\nFilepath: {}\nSignature path: " + "{}\nDescfile:\n\n{}\n", + bxt::to_string(key), value.filepath.string(), value.signature_path->string(), + value.descfile.desc); + } + return 0; + } + + int delete_(lmdb::txn& transaction, lmdb::dbi& db, std::string const& key) { + auto result = db.del(transaction, key); + if (result) { + fmt::print("Value deleted successfully.\n"); + return 0; + } else { + fmt::print(stderr, "Failed to delete value or value not found.\n"); + return 1; + } + } + + int validate(lmdb::txn& transaction, lmdb::dbi& db) { + Validator validator(transaction, db, false, false); + auto error_count = validator.validate_and_rebuild(); + if (error_count == 0) { + fmt::print("No errors found.\n"); + } else { + fmt::print("{} errors found.\n", error_count); + } + return error_count > 0 ? 1 : 0; + } + + int rebuild(lmdb::txn& transaction, lmdb::dbi& db, bool rebuild_keys) { + Validator validator(transaction, db, true, rebuild_keys); + + if (validator.validate_and_rebuild() == 0) { + fmt::print("Successfully rebuilt{} packages.\n", + rebuild_keys ? " all package keys" : ""); + transaction.commit(); + return 0; + } else { + fmt::print(stderr, "Failed to rebuild packages.\n"); + return 1; + } + } +} // namespace handlers + +class DatabaseCli { +public: + int run(int argc, char** argv) { + CLI::App app {"BXT Database CLI Tool"}; + app.require_subcommand(1); + + std::string prefix; + auto list = app.add_subcommand("list", "List packages"); + list->add_option("prefix", prefix, "Package name prefix to filter by"); + + std::string get_key; + auto get = app.add_subcommand("get", "Get package details"); + get->add_option("key", get_key, "Package key to retrieve")->required(); + + std::string del_key; + auto del = app.add_subcommand("del", "Delete package"); + del->add_option("key", del_key, "Package key to delete")->required(); + + auto validate = app.add_subcommand("validate", "Validate database records"); + + bool rebuild_keys = false; + auto rebuild = app.add_subcommand("rebuild", "Rebuild database records"); + rebuild->add_flag("--keys", rebuild_keys, "Rebuild package keys"); + + CLI11_PARSE(app, argc, argv); + + auto lmdbenv = lmdb::env::create(); + lmdbenv.set_mapsize(LmdbMapSize); + lmdbenv.set_max_dbs(LmdbMaxDbs); + std::error_code ec; + + if (!std::filesystem::exists("./bxtd.lmdb", ec) || ec) { + fmt::print(stderr, "bxtd.lmdb not found\n"); + return 1; + } + + lmdbenv.open("./bxtd.lmdb"); + auto transaction = lmdb::txn::begin(lmdbenv); + auto db = lmdb::dbi::open(transaction, "bxt::Box"); + + if (list->parsed()) { + return handlers::list(transaction, db, prefix); + } else if (get->parsed()) { + return handlers::get(transaction, db, get_key); + } else if (del->parsed()) { + return handlers::delete_(transaction, db, del_key); + } else if (validate->parsed()) { + return handlers::validate(transaction, db); + } else if (rebuild->parsed()) { + return handlers::rebuild(transaction, db, rebuild_keys); + } + + return 0; + } +}; + +} // namespace bxt::cli + +int main(int argc, char** argv) { + return bxt::cli::DatabaseCli {}.run(argc, argv); +} diff --git a/db-cli/validation.h b/db-cli/validation.h new file mode 100644 index 00000000..2e99ed94 --- /dev/null +++ b/db-cli/validation.h @@ -0,0 +1,194 @@ + +/* === This file is part of bxt === + * + * SPDX-FileCopyrightText: 2024 Artem Grinev + * SPDX-License-Identifier: AGPL-3.0-or-later + * + */ +#pragma once + +// bxt +#include +#include +#include + +// External +#include +#include +#include +#include +#include + +// STL +#include +#include +#include + +class Validator { +public: + Validator(lmdb::txn& transaction, lmdb::dbi& db, bool rebuild_descfile, bool rebuild_keys) + : m_transaction(transaction) + , m_db(db) + , m_rebuild_descfile(rebuild_descfile) + , m_rebuild_keys(rebuild_keys) { + } + + int validate_and_rebuild() { + auto cursor = lmdb::cursor::open(m_transaction, m_db); + std::string_view key, value; + + while (cursor.get(key, value, MDB_NEXT)) { + validate_record(cursor, key, value); + } + + return m_error_count; + } + + int error_count() const { + return m_error_count; + } + + lmdb::dbi& db() const { + return m_db; + } + + lmdb::txn& transaction() const { + return m_transaction; + } + +protected: + std::string read_file_content(std::filesystem::path const& filepath) { + std::ifstream file(filepath); + if (!file.is_open()) { + throw std::ios_base::failure("Unable to open file: " + filepath.string()); + } + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); + } + + template + void handle_error(fmt::format_string fmt, TArgs&&... args) { + ++m_error_count; + fmt::print(stderr, fg(fmt::terminal_color::red), fmt, std::forward(args)...); + } + + bool validate_description(std::string const& record_id, + std::string const& location, + bxt::Persistence::Box::PackageRecord::Description& description) { + namespace fs = std::filesystem; + + if (!fs::exists(description.filepath)) { + handle_error("{} ({}): File not found: {}\n", record_id, location, + description.filepath); + return m_rebuild_descfile; + } + + if (description.signature_path && !fs::exists(*description.signature_path)) { + handle_error("{} ({}): Signature file not found: {}\n", record_id, location, + *description.signature_path); + return m_rebuild_descfile; + } + + std::string signature; + if (description.signature_path) { + try { + signature = read_file_content(*description.signature_path); + } catch (std::exception const& e) { + handle_error("{} ({}): Error reading signature file: {}\n", record_id, location, + e.what()); + return false; + } + } + + auto desc_result = + bxt::Utilities::AlpmDb::Desc::parse_package(description.filepath, signature); + if (!desc_result) { + handle_error("{} ({}): Failed to parse desc-file: {}\n", record_id, location, + description.filepath.string()); + return m_rebuild_descfile; + } + + if (desc_result->desc != description.descfile.desc) { + fmt::print(stderr, fg(fmt::terminal_color::yellow), "{} ({}): Desc-file mismatch\n", + record_id, location); + + fs::path report_dir = "dbcli-report"; + fs::path package_dir = report_dir / record_id / location; + fs::create_directories(package_dir); + + std::ofstream db_file(package_dir / "db"); + db_file << description.descfile.desc; + + std::ofstream pkg_file(package_dir / "pkg"); + pkg_file << desc_result->desc; + + fmt::print(fg(fmt::terminal_color::yellow), "{} ({}): Desc files saved in {}\n", + record_id, location, package_dir.string()); + + if (!m_rebuild_descfile) { + return false; + } + + description.descfile = std::move(*desc_result); + } + + fmt::print(fg(fmt::terminal_color::green), "{} ({}): Valid\n", record_id, location); + return true; + } + + void validate_record(lmdb::cursor& cursor, std::string_view key, std::string_view value) { + fmt::print("Checking record: {}\n", key); + + auto record = Serializer::deserialize(std::string(value)); + if (!record) { + handle_error("{}: Failed to deserialize record: {}\n", key, record.error().what()); + return; + } + + bool needs_update = false; + + for (auto& [location, description] : record->descriptions) { + if (!validate_description(record->id.to_string(), bxt::to_string(location), + description)) { + continue; + } + needs_update = m_rebuild_descfile; + } + + if (needs_update) { + auto serialized = Serializer::serialize(*record); + if (!serialized) { + handle_error("{}: Failed to serialize record: {}\n", record->id.to_string(), + serialized.error().what()); + return; + } + + try { + cursor.del(); + + if (m_rebuild_keys) { + auto new_key = record->id.to_string(); + m_db.put(m_transaction, new_key, *serialized); + fmt::print(fg(fmt::terminal_color::green), "{}: Updated with reworked key\n", + new_key); + } else { + m_db.put(m_transaction, std::string(key), *serialized); + fmt::print(fg(fmt::terminal_color::green), + "{}: Updated without reworking key\n", key); + } + } catch (std::exception const& e) { + handle_error("{}: Failed to update record: {}", record->id.to_string(), e.what()); + } + } + } + +private: + using Serializer = bxt::Utilities::LMDB::CerealSerializer; + + lmdb::txn& m_transaction; + lmdb::dbi& m_db; + bool m_rebuild_descfile = false; + bool m_rebuild_keys = false; + int m_error_count = 0; +}; diff --git a/dbcli/dbcli.cpp b/dbcli/dbcli.cpp deleted file mode 100644 index fb0f24e4..00000000 --- a/dbcli/dbcli.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* === This file is part of bxt === - * - * SPDX-FileCopyrightText: 2024 Artem Grinev - * SPDX-License-Identifier: AGPL-3.0-or-later - * - */ -#include "persistence/box/record/PackageRecord.h" -#include "utilities/lmdb/CerealSerializer.h" -#include "utilities/to_string.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using Serializer = bxt::Utilities::LMDB::CerealSerializer; - -int validate_and_rebuild(lmdb::txn& transaction, lmdb::dbi& db, bool rebuild_descfile = false) { - auto cursor = lmdb::cursor::open(transaction, db); - std::string_view key, value; - - int error_count = 0; - - while (cursor.get(key, value, MDB_NEXT)) { - fmt::print("Checking record: {}\n", key); - auto record = Serializer::deserialize(std::string(value)); - if (!record) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{}: Failed to deserialize record: {}\n", record->id.to_string(), - record.error().what()); - if (rebuild_descfile) { - return 1; - } - error_count++; - continue; - } - - bool needs_update = false; - - for (auto& [location, description] : record->descriptions) { - if (!std::filesystem::exists(description.filepath)) { - fmt::print(stderr, fg(fmt::terminal_color::red), "{} ({}): File not found: {}\n", - record->id.to_string(), bxt::to_string(location), - description.signature_path->string()); - if (rebuild_descfile) { - return 1; - } - error_count++; - continue; - } - - if (description.signature_path - && !std::filesystem::exists(*description.signature_path)) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{} ({}): Signature file not found: {}\n", record->id.to_string(), - bxt::to_string(location), description.signature_path->string()); - if (rebuild_descfile) { - return 1; - } - error_count++; - continue; - } - - std::string signature; - - if (description.signature_path) { - try { - std::ifstream file(*description.signature_path); - if (file.is_open()) { - std::stringstream buffer; - buffer << file.rdbuf(); - signature = buffer.str(); - file.close(); - } else { - std::cerr << "Unable to open signature file: " - << *description.signature_path << std::endl; - } - } catch (std::exception const& e) { - std::cerr << "Error reading signature file: " << e.what() << std::endl; - } - } - - auto desc_result = - bxt::Utilities::AlpmDb::Desc::parse_package(description.filepath, signature); - - if (!desc_result) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{} ({}): Failed to parse desc-file for: {}\n", record->id.to_string(), - bxt::to_string(location), description.filepath.string()); - if (rebuild_descfile) { - return 1; - } - error_count++; - continue; - } - - if (desc_result->desc != description.descfile.desc) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{} ({}): Desc-file mismatch for: {}\n", record->id.to_string(), - bxt::to_string(location), description.filepath.string()); - - // Create directory for saving desc files - std::filesystem::path report_dir = "dbcli-report"; - std::filesystem::path package_dir = - report_dir / record->id.to_string() / bxt::to_string(location); - std::filesystem::create_directories(package_dir); - - // Save db desc file - std::ofstream db_file(package_dir / "db"); - if (db_file.is_open()) { - db_file << description.descfile.desc; - db_file.close(); - } else { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{} ({}): Failed to save db desc file\n", record->id.to_string(), - bxt::to_string(location)); - } - - // Save pkg desc file - std::ofstream pkg_file(package_dir / "pkg"); - if (pkg_file.is_open()) { - pkg_file << desc_result->desc; - pkg_file.close(); - } else { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{} ({}): Failed to save pkg desc file\n", record->id.to_string(), - bxt::to_string(location)); - } - - fmt::print(fg(fmt::terminal_color::yellow), "{} ({}): Desc files saved in {}\n", - record->id.to_string(), bxt::to_string(location), package_dir.string()); - - needs_update = true; - - if (!rebuild_descfile) { - error_count++; - } - - } else { - fmt::print(fg(fmt::terminal_color::green), "{} ({}): Valid\n", - record->id.to_string(), bxt::to_string(location)); - } - if (rebuild_descfile) { - fmt::print("{} ({}): Rebuilding desc-file for: {}\n", record->id.to_string(), - bxt::to_string(location), record->id.to_string()); - description.descfile = std::move(*desc_result); - needs_update = true; - } - } - - if (needs_update) { - auto serialized = Serializer::serialize(*record); - if (!serialized) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{}: Failed to serialize record: {}\n", record->id.to_string(), - serialized.error().what()); - return 1; - } - - try { - auto new_key = record->id.to_string(); - - cursor.del(); - - db.put(transaction, new_key, *serialized); - - fmt::print(fg(fmt::terminal_color::green), "{}: Updated\n", new_key); - - } catch (std::exception const& e) { - fmt::print(stderr, fg(fmt::terminal_color::red), - "{}: Failed to update record: {}\n", record->id.to_string(), e.what()); - return 1; - } - } - } - return error_count; -} - -int main(int argc, char** argv) { - auto lmdbenv = lmdb::env::create(); - - lmdbenv.set_mapsize(50UL * 1024UL * 1024UL * 1024UL); - lmdbenv.set_max_dbs(128); - std::error_code ec; - - if (!std::filesystem::exists("./bxtd.lmdb", ec) || ec) { - fmt::print(stderr, "bxtd.lmdb not found\n"); - return 1; - } - - lmdbenv.open("./bxtd.lmdb"); - - auto transaction = lmdb::txn::begin(lmdbenv); - auto db = lmdb::dbi::open(transaction, "bxt::Box"); - - if (argc < 2) { - fmt::print("Usage: {} [options]\n", argv[0]); - return 1; - } - - std::string command = argv[1]; - if (command == "list") { - auto cursor = lmdb::cursor::open(transaction, db); - if (argc >= 3) { - std::string_view key = argv[2]; - if (!cursor.get(key, MDB_SET_RANGE) || !key.starts_with(argv[2])) { - fmt::print(stderr, "No packages found with prefix {}\n", key); - return 1; - } - do { - fmt::print("{}\n", key); - } while (cursor.get(key, MDB_NEXT) && key.starts_with(argv[2])); - } else { - std::string_view key; - while (cursor.get(key, MDB_NEXT)) { - fmt::print("{}\n", key); - } - } - } else if (command == "get") { - if (argc != 3) { - fmt::print(stderr, "Usage: {} get \n", argv[0]); - return 1; - } - - std::string key = argv[2]; - std::string_view data; - auto result = db.get(transaction, key, data); - if (!result) { - fmt::print(stderr, "Failed to retrieve value or value not found.\n"); - return 1; - } - - auto const package = Serializer::deserialize(std::string(data)); - if (!package.has_value()) { - fmt::print(stderr, "Failed to deserialize package.\n"); - return 1; - } - - fmt::print("{}\nIs any arch: {}\nDescriptions:\n", package->id.to_string(), - package->is_any_architecture); - for (auto const& [key, value] : package->descriptions) { - fmt::print("==={}===\nFilepath: {}\nSignature path: " - "{}\nDescfile:\n\n{}\n", - bxt::to_string(key), value.filepath.string(), value.signature_path->string(), - value.descfile.desc); - } - } else if (command == "del") { - if (argc != 3) { - fmt::print(stderr, "Usage: {} del \n", argv[0]); - return 1; - } - - std::string_view key = argv[2]; - auto result = db.del(transaction, key); - if (result) { - fmt::print("Value deleted successfully.\n", argv[0]); - } else { - fmt::print(stderr, "Failed to delete value or value not found.\n"); - } - } else if (command == "validate") { - if (argc == 2) { - auto error_count = validate_and_rebuild(transaction, db, false); - - if (error_count == 0) { - fmt::print("No errors found.\n"); - } else { - fmt::print("{} errors found.\n", error_count); - } - } else { - fmt::print(stderr, "Usage: {} validate\n", argv[0]); - return 1; - } - - } else if (command == "rebuild") { - if (argc == 2) { - if (validate_and_rebuild(transaction, db, true) == 0) { - fmt::print("Successfully rebuilt all packages.\n"); - transaction.commit(); - } else { - fmt::print(stderr, "Failed to rebuild packages.\n"); - } - } else { - fmt::print(stderr, "Usage: {} rebuild \n", argv[0]); - return 1; - } - - } else { - fmt::print(stderr, "Unknown command: {}\n", command); - return 1; - } - - return 0; -}