14#include <condition_variable>
44 using ResultOf = std::invoke_result_t<Fn&, SqliteConnection&>;
65 Enqueue(MakeJob(std::move(name), std::move(
fn), {}));
76 template <
class Fn,
class OnValue>
77 requires(!std::is_void_v<ResultOf<Fn>> && std::invocable<OnValue&, ResultOf<Fn>> &&
78 !std::invocable<OnValue&, Result<ResultOf<Fn>>>)
101 auto promise = std::make_shared<std::promise<Result<ResultOf<Fn>>>>();
102 std::future<Result<ResultOf<Fn>>>
answer =
promise->get_future();
103 Enqueue(MakeJob(std::move(name), std::move(
fn),
120 bool IsConnected()
const {
return _connected.load(std::memory_order_relaxed); }
130 std::move_only_function<
void(
Error)> Fail;
145 static_assert(std::same_as<std::invoke_result_t<Fn&, PostgresConnection&>,
ResultOf<Fn>> &&
146 std::same_as<std::invoke_result_t<Fn&, MariaDbConnection&>,
ResultOf<Fn>>,
147 "a database job must return the same type for every driver");
151 .Name = std::move(name),
153 .Fail = [](
Error) {},
157 auto shared = std::make_shared<decltype(deliver)>(std::move(
deliver));
159 .Name = std::move(name),
162 if constexpr (std::is_void_v<ResultOf<Fn>>)
182 if constexpr (std::same_as<std::remove_cvref_t<
decltype(
open)>, std::monostate>)
184 throw std::logic_error(
"no database connection");
195 static bool IsSet(
const auto&
callback)
197 if constexpr (
requires {
static_cast<bool>(
callback); })
207 void Enqueue(Job
job);
210 std::optional<Job> TakeJob();
211 void RunJob(Job&
job);
215 void DropConnection();
217 Scheduler& _scheduler;
218 DatabaseConfig _config;
221 std::mutex _queueMutex;
222 std::condition_variable _queueCv;
223 std::deque<Job> _queue;
224 State _state = State::Stopped;
225 std::chrono::steady_clock::time_point _stopDeadline{};
227 GameThreadQueue _completions;
229 Subscription _onFrame;
232 std::atomic<bool> _connected{
false};
236 std::chrono::steady_clock::time_point _retryAt{};
Async database access over Postgres, MariaDB and SQLite.
Result< ResultOf< Fn > > Run(std::string name, Fn fn)
Database(const Database &)=delete
ResultOf< Fn > RunOr(std::string name, Fn fn, ResultOf< Fn > fallback={})
bool Connect(const DatabaseConfig &config)
Database(Scheduler &scheduler)
void Disconnect(std::chrono::milliseconds stopDeadline=std::chrono::seconds(5))
std::invoke_result_t< Fn &, SqliteConnection & > ResultOf
void DispatchCompletions()
void RunAsync(std::string name, Fn fn, OnValue onValue)
void RunAsync(std::string name, Fn fn, std::move_only_function< void(Result< ResultOf< Fn > >)> onDone={})
Database & operator=(const Database &)=delete
void Push(std::move_only_function< void()> completion)
One-shot delays and repeating timers, run on the game thread.
void Error(std::format_string< Args... > fmt, Args &&... args)
constexpr std::string_view Name(E value) noexcept
static std::string ReadFile(const std::filesystem::path &path)
MigrationResult RunMigrations(Database &db, std::string_view dir, const MigrationOptions &options={})
std::expected< T, Error > Result
std::variant< std::monostate, PostgresConnection, MariaDbConnection, SqliteConnection > AnyConnection
Connection parameters for every supported backend.
One failure: a code to branch on, text for the log, and an optional translation key.