gatewayd: approvals answered from Mattermost; typing paused; the notice
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::Path;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use proto::{
|
||||
Envelope, ErrorCode, Message, PROTOCOL_VERSION, Turn, TurnEvent, WireError, read_frame,
|
||||
@@ -40,10 +41,36 @@ pub enum Outcome {
|
||||
|
||||
pub fn approval_text(approval: u64) -> String {
|
||||
format!(
|
||||
"waiting for approval {approval}: approve or deny it with `bxctl` (Mattermost approvals arrive in M4b)"
|
||||
"waiting for approval {approval}: brokerd has not sent it here; answer it with bxctl approvals"
|
||||
)
|
||||
}
|
||||
|
||||
/// How long a turn waits for `brokerd` to bring its approval here before posting the notice.
|
||||
pub const NOTICE_WAIT: Duration = Duration::from_secs(10);
|
||||
|
||||
/// Whether an approval was brought here (and so is posted already), and how long to wait for it.
|
||||
pub struct Notice<'a> {
|
||||
pub posted: &'a dyn Fn(u64) -> bool,
|
||||
pub wait: Duration,
|
||||
}
|
||||
|
||||
impl Notice<'_> {
|
||||
fn arrives(&self, id: u64) -> bool {
|
||||
let until = Instant::now() + self.wait;
|
||||
loop {
|
||||
if (self.posted)(id) {
|
||||
return true;
|
||||
}
|
||||
if Instant::now() >= until {
|
||||
return false;
|
||||
}
|
||||
std::thread::sleep(
|
||||
Duration::from_millis(50).min(until.saturating_duration_since(Instant::now())),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// "Error: <code>: <detail>"; the detail carries `loopd`'s runbook pointer when there is one.
|
||||
pub fn error_text(error: &WireError) -> String {
|
||||
// "Error: <code>: <detail>", where <code> is the snake_case name serde gives the ErrorCode
|
||||
@@ -173,14 +200,23 @@ fn post(poster: &dyn Poster, thread: &Thread, text: String, log: &dyn Fn(&str))
|
||||
}
|
||||
|
||||
/// Run a batch's turn and post what comes of it in its thread. A post that fails is logged.
|
||||
pub fn deliver(poster: &dyn Poster, socket: &Path, batch: &Batch, log: &dyn Fn(&str)) {
|
||||
// Post in the batch thread. An ApprovalPending event posts `approval_text(approval)` at once.
|
||||
// Then: Answer -> every part of `split_answer`, in order; Refused -> `error_text`;
|
||||
pub fn deliver(
|
||||
poster: &dyn Poster,
|
||||
socket: &Path,
|
||||
batch: &Batch,
|
||||
log: &dyn Fn(&str),
|
||||
notice: &Notice,
|
||||
) {
|
||||
// Post in the batch thread. An ApprovalPending event waits up to `notice.wait` for `brokerd` to
|
||||
// bring the approval through `gateway.sock`; only when it does not, it posts the notice. Then:
|
||||
// Answer -> every part of `split_answer`, in order; Refused -> `error_text`;
|
||||
// LoopDown(why) -> log "gatewayd: <session>: <why>" and post LOOP_DOWN. A post that fails is
|
||||
// logged, exactly "gatewayd: cannot post in <channel> (thread <root>): <error>", and the rest
|
||||
// goes on.
|
||||
let outcome = run_turn(socket, batch, &mut |event| {
|
||||
if let TurnEvent::ApprovalPending { approval, .. } = event {
|
||||
if let TurnEvent::ApprovalPending { approval, .. } = event
|
||||
&& !notice.arrives(*approval)
|
||||
{
|
||||
post(poster, &batch.thread, approval_text(*approval), log);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user