156 lines
5.9 KiB
C++
156 lines
5.9 KiB
C++
module;
|
|
|
|
#include <sqlite3.h>
|
|
|
|
export module cargoxx.linkdb;
|
|
|
|
import std;
|
|
import cargoxx.util;
|
|
|
|
export namespace cargoxx::linkdb {
|
|
|
|
struct Recipe {
|
|
std::string nixpkgs_attr;
|
|
std::string find_package; // post-substitution
|
|
std::vector<std::string> targets; // post-substitution
|
|
std::string source; // 'curated' | 'manual' | etc.
|
|
|
|
// When set, the dep is consumed via PkgConfig instead of a normal
|
|
// find_package. Codegen emits
|
|
// find_package(PkgConfig REQUIRED)
|
|
// pkg_check_modules(<UPPER> REQUIRED IMPORTED_TARGET <pkg_config_module>)
|
|
// and the recipe's `targets` list holds the synthesized
|
|
// PkgConfig::<UPPER> entries. `find_package` for these recipes is
|
|
// the literal string "PkgConfig REQUIRED" — kept consistent so
|
|
// overlay rows don't need a sentinel.
|
|
std::optional<std::string> pkg_config_module;
|
|
|
|
bool operator==(const Recipe&) const = default;
|
|
};
|
|
|
|
} // namespace cargoxx::linkdb
|
|
|
|
namespace cargoxx::linkdb::detail {
|
|
|
|
struct OverlayRow {
|
|
std::string version_range;
|
|
std::string nixpkgs_attr;
|
|
std::string find_package;
|
|
std::vector<std::string> targets;
|
|
std::string source;
|
|
std::int64_t verified_at = 0;
|
|
std::optional<std::string> pkg_config_module;
|
|
};
|
|
|
|
// RAII wrapper for an open sqlite3 connection used by the overlay database.
|
|
// Move-only would be redundant — the holder (Database::overlay_) is a
|
|
// std::unique_ptr<OverlayState>, so we delete copy and move outright.
|
|
class OverlayState {
|
|
public:
|
|
OverlayState() = default;
|
|
explicit OverlayState(sqlite3* handle) noexcept : db_(handle) {}
|
|
~OverlayState() {
|
|
if (db_) {
|
|
sqlite3_close(db_);
|
|
}
|
|
}
|
|
|
|
OverlayState(const OverlayState&) = delete;
|
|
OverlayState& operator=(const OverlayState&) = delete;
|
|
OverlayState(OverlayState&&) = delete;
|
|
OverlayState& operator=(OverlayState&&) = delete;
|
|
|
|
[[nodiscard]] auto handle() const noexcept -> sqlite3* { return db_; }
|
|
|
|
private:
|
|
sqlite3* db_ = nullptr;
|
|
};
|
|
|
|
auto overlay_open(const std::filesystem::path& path)
|
|
-> cargoxx::util::Result<std::unique_ptr<OverlayState>>;
|
|
|
|
auto overlay_insert_manual(OverlayState& state, const std::string& package,
|
|
const std::string& version_range,
|
|
const cargoxx::linkdb::Recipe& r)
|
|
-> cargoxx::util::Result<void>;
|
|
|
|
// Insert a row from a non-manual probe. `verified_at = 0` flags the row as
|
|
// provisional; the verify-link step bumps it to the current epoch on
|
|
// success or deletes the row on failure.
|
|
auto overlay_insert_provisional(OverlayState& state, const std::string& package,
|
|
const std::string& version_range,
|
|
const cargoxx::linkdb::Recipe& r,
|
|
const std::string& source)
|
|
-> cargoxx::util::Result<void>;
|
|
|
|
auto overlay_confirm_provisional(OverlayState& state, const std::string& package,
|
|
const std::string& version_range,
|
|
const std::string& source)
|
|
-> cargoxx::util::Result<void>;
|
|
|
|
auto overlay_delete_recipe(OverlayState& state, const std::string& package,
|
|
const std::string& version_range,
|
|
const std::string& source)
|
|
-> cargoxx::util::Result<void>;
|
|
|
|
// Drops every overlay row for `package` whose source is not "manual".
|
|
// Used by `cargoxx add` to invalidate stale auto-discovered recipes
|
|
// when the resolver/scanner logic has changed under the user — they
|
|
// should never have to know `~/.cache/cargoxx/linkdb.sqlite` exists.
|
|
auto overlay_evict_auto(OverlayState& state, const std::string& package)
|
|
-> cargoxx::util::Result<void>;
|
|
|
|
auto overlay_query(OverlayState& state, const std::string& package)
|
|
-> cargoxx::util::Result<std::vector<OverlayRow>>;
|
|
|
|
auto overlay_is_fresh(const OverlayRow& row, std::int64_t now_epoch_seconds) -> bool;
|
|
|
|
} // namespace cargoxx::linkdb::detail
|
|
|
|
export namespace cargoxx::linkdb {
|
|
|
|
class Database {
|
|
public:
|
|
static auto open(std::optional<std::filesystem::path> overlay_path = std::nullopt)
|
|
-> util::Result<Database>;
|
|
|
|
auto resolve(const std::string& package, const std::string& version,
|
|
const std::vector<std::string>& components = {})
|
|
-> util::Result<Recipe>;
|
|
|
|
auto add_manual(const std::string& package, const std::string& version_range,
|
|
const Recipe& r) -> util::Result<void>;
|
|
|
|
// Provisional-recipe lifecycle, used by the resolver's verify-link step.
|
|
// `source` should be one of "conan", "vcpkg", "nix-probe", or any other
|
|
// probe identifier (NOT "manual"/"curated", which have their own
|
|
// contracts).
|
|
auto insert_provisional(const std::string& package,
|
|
const std::string& version_range, const Recipe& r,
|
|
const std::string& source) -> util::Result<void>;
|
|
auto confirm_provisional(const std::string& package,
|
|
const std::string& version_range,
|
|
const std::string& source) -> util::Result<void>;
|
|
auto abort_provisional(const std::string& package,
|
|
const std::string& version_range,
|
|
const std::string& source) -> util::Result<void>;
|
|
|
|
// Evict every non-manual overlay row for `package`. Called by
|
|
// `cargoxx add` so users never have to manually drop stale
|
|
// auto-discovered recipes when cargoxx's resolver/scanner logic
|
|
// changes.
|
|
auto evict_auto_recipes(const std::string& package) -> util::Result<void>;
|
|
|
|
private:
|
|
Database() = default;
|
|
std::unique_ptr<detail::OverlayState> overlay_;
|
|
};
|
|
|
|
// Returns the default overlay-database path:
|
|
// $XDG_CACHE_HOME/cargoxx/linkdb.sqlite (if XDG_CACHE_HOME is set)
|
|
// $HOME/.cache/cargoxx/linkdb.sqlite (else if HOME is set)
|
|
// <cwd>/.cargoxx-linkdb.sqlite (final fallback)
|
|
auto default_overlay_path() -> std::filesystem::path;
|
|
|
|
} // namespace cargoxx::linkdb
|