xbot/irc_coroutine.hpp
2025-01-23 12:46:52 -08:00

98 lines
2.4 KiB
C++

#pragma once
#include "connection.hpp"
#include <coroutine>
#include <vector>
struct irc_promise;
struct irc_coroutine : std::coroutine_handle<irc_promise> {
using promise_type = irc_promise;
auto start(Connection &connection) -> void;
auto is_running() -> bool;
auto exception() -> std::exception_ptr;
};
struct irc_promise
{
// Pointer to the connection while running. Cleared on termination.
std::shared_ptr<Connection> connection_;
// Pointer to exception that terminated this coroutine if there is one.
std::exception_ptr exception_;
irc_coroutine get_return_object()
{
return {irc_coroutine::from_promise(*this)};
}
// Suspend waiting for start() to initialize connection_
std::suspend_always initial_suspend() noexcept { return {}; }
// Suspend so that is_running() and exception() work
std::suspend_always final_suspend() noexcept { return {}; }
// Normal termination
void return_void() {
connection_.reset();
}
// Abnormal termination - remember the exception
void unhandled_exception() {
connection_.reset();
exception_ = std::current_exception();
}
};
/*
struct wait_ircmsg {
using result_type = std::pair<IrcCommand, const IrcMsg &>
std::vector<IrcCommand> want_cmds_;
};
struct wait_timeout {
struct result_type {};
std::vector<IrcCommand> want_cmds_;
};
*/
class wait_command {
std::vector<IrcCommand> want_cmds_;
IrcCommand resultCmd;
const IrcMsg *resultMsg;
boost::signals2::scoped_connection ircmsg_connection_;
boost::signals2::scoped_connection disconnect_connection_;
void unsubscribe() {
ircmsg_connection_.disconnect();
disconnect_connection_.disconnect();
}
public:
wait_command(std::initializer_list<IrcCommand> want_cmds)
: want_cmds_(want_cmds)
, resultMsg{}
{}
/// The coroutine always needs to wait for a message. It will never
/// be ready immediately.
bool await_ready() noexcept { return false; }
/// Install event handles in the connection that will resume this coroutine.
void await_suspend(std::coroutine_handle<irc_promise> handle);
auto await_resume() -> std::pair<IrcCommand, const IrcMsg &> {
if (resultMsg) {
return std::make_pair(resultCmd, std::cref(*resultMsg));
} else {
throw std::runtime_error{"connection terminated"};
}
}
};