fix #265: resolve all remaining clippy warnings (cast, too_many_lines, doc nits)
This commit is contained in:
parent
30d82148e0
commit
484cea62c7
12 changed files with 95 additions and 86 deletions
|
|
@ -149,7 +149,7 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments, clippy::similar_names)]
|
#[allow(clippy::too_many_arguments, clippy::similar_names, clippy::too_many_lines)]
|
||||||
async fn serve(
|
async fn serve(
|
||||||
socket: &Path,
|
socket: &Path,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
|
|
@ -307,14 +307,14 @@ async fn serve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
// Per-turn user prompt: the role/tools/etc. is in the system prompt
|
||||||
/// (`prompts/agent.md` → `claude --system-prompt-file`); this is just the
|
// (`prompts/agent.md` → `claude --system-prompt-file`); this is just the
|
||||||
/// wake signal claude reacts to. `unread` is the count of *other*
|
// wake signal claude reacts to. `unread` is the count of *other*
|
||||||
/// messages in the inbox right after this one was popped.
|
// messages in the inbox right after this one was popped.
|
||||||
/// `redelivered` flags messages that were popped in a prior harness
|
// `redelivered` flags messages that were popped in a prior harness
|
||||||
/// session, never acked, and resurfaced after a restart — a banner
|
// session, never acked, and resurfaced after a restart — a banner
|
||||||
/// at the top of the wake prompt warns that any side-effects of
|
// at the top of the wake prompt warns that any side-effects of
|
||||||
/// previous handling may already have happened.
|
// previous handling may already have happened.
|
||||||
|
|
||||||
/// Best-effort: tell the broker every message we popped during the
|
/// Best-effort: tell the broker every message we popped during the
|
||||||
/// turn is now fully handled (turn-end-OK). Swallows transport
|
/// turn is now fully handled (turn-end-OK). Swallows transport
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)] // linear startup sequence; splitting adds indirection without clarity
|
||||||
async fn serve(
|
async fn serve(
|
||||||
socket: &Path,
|
socket: &Path,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
//! are silently marked read — the agent already knows it opened them.
|
//! are silently marked read — the agent already knows it opened them.
|
||||||
//! - Comment notifications where the comment author matches this agent's own
|
//! - Comment notifications where the comment author matches this agent's own
|
||||||
//! forge login are silently marked read.
|
//! forge login are silently marked read.
|
||||||
|
//!
|
||||||
//! Own login is fetched once at startup via `GET /user` and cached for the
|
//! Own login is fetched once at startup via `GET /user` and cached for the
|
||||||
//! lifetime of the polling loop.
|
//! lifetime of the polling loop.
|
||||||
//!
|
//!
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
//! generic `[comment on PR #N repo]` so agents can action it immediately.
|
//! generic `[comment on PR #N repo]` so agents can action it immediately.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::fmt::Write as _;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -209,6 +211,7 @@ fn review_state_label(state: &str) -> Option<&str> {
|
||||||
///
|
///
|
||||||
/// Number is extracted from `html_url` last path segment before any `#`.
|
/// Number is extracted from `html_url` last path segment before any `#`.
|
||||||
/// Repo slug (`owner/name`) is always included — agents may watch multiple repos.
|
/// Repo slug (`owner/name`) is always included — agents may watch multiple repos.
|
||||||
|
#[allow(clippy::too_many_lines)] // multiple notification types handled in one place by design
|
||||||
async fn format_notification(
|
async fn format_notification(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
token: &str,
|
token: &str,
|
||||||
|
|
@ -329,9 +332,9 @@ async fn format_notification(
|
||||||
let kind = format!("PR {review_label}{num}{repo}");
|
let kind = format!("PR {review_label}{num}{repo}");
|
||||||
let mut out = format!("[{kind}] {title}\nurl: {url}");
|
let mut out = format!("[{kind}] {title}\nurl: {url}");
|
||||||
if body_text.is_empty() {
|
if body_text.is_empty() {
|
||||||
out.push_str(&format!("\n\nreviewer: {author}"));
|
write!(out, "\n\nreviewer: {author}").ok();
|
||||||
} else {
|
} else {
|
||||||
out.push_str(&format!("\n\n{author}: {}", truncate(body_text, BODY_TRUNCATE)));
|
write!(out, "\n\n{author}: {}", truncate(body_text, BODY_TRUNCATE)).ok();
|
||||||
}
|
}
|
||||||
out.push_str(&meta_suffix);
|
out.push_str(&meta_suffix);
|
||||||
Some(out)
|
Some(out)
|
||||||
|
|
@ -449,15 +452,12 @@ async fn poll_once(
|
||||||
debug!(count = notifications.len(), "forge_notify: delivering notifications");
|
debug!(count = notifications.len(), "forge_notify: delivering notifications");
|
||||||
|
|
||||||
for notif in ¬ifications {
|
for notif in ¬ifications {
|
||||||
let id = match notif["id"].as_u64() {
|
let Some(id) = notif["id"].as_u64() else { continue };
|
||||||
Some(n) => n,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let body_opt = format_notification(client, token, notif, own_login).await;
|
let body_opt = format_notification(client, token, notif, own_login).await;
|
||||||
|
|
||||||
// None means self-echo — mark read silently, no delivery.
|
// None means self-echo — mark read silently, no delivery.
|
||||||
let body = if let Some(b) = body_opt { b } else {
|
let Some(body) = body_opt else {
|
||||||
mark_read(client, forge_url, token, id).await;
|
mark_read(client, forge_url, token, id).await;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ pub fn rate_limit_sleep_secs() -> u64 {
|
||||||
/// 1. `HIVE_AUTO_RESET_WATERMARK_TOKENS` env var (explicit override).
|
/// 1. `HIVE_AUTO_RESET_WATERMARK_TOKENS` env var (explicit override).
|
||||||
/// 2. 50% of the model's context window (derived from `bus.model()` +
|
/// 2. 50% of the model's context window (derived from `bus.model()` +
|
||||||
/// `events::context_window_tokens`).
|
/// `events::context_window_tokens`).
|
||||||
|
///
|
||||||
/// `0` disables auto-reset entirely.
|
/// `0` disables auto-reset entirely.
|
||||||
fn auto_reset_watermark_tokens(bus: &Bus) -> u64 {
|
fn auto_reset_watermark_tokens(bus: &Bus) -> u64 {
|
||||||
if let Some(v) = std::env::var("HIVE_AUTO_RESET_WATERMARK_TOKENS")
|
if let Some(v) = std::env::var("HIVE_AUTO_RESET_WATERMARK_TOKENS")
|
||||||
|
|
@ -223,6 +224,7 @@ fn cache_ttl_secs() -> u64 {
|
||||||
/// 1. `HIVE_COMPACT_WATERMARK_TOKENS` env var (explicit override).
|
/// 1. `HIVE_COMPACT_WATERMARK_TOKENS` env var (explicit override).
|
||||||
/// 2. 75% of the model's context window (derived from `bus.model()` +
|
/// 2. 75% of the model's context window (derived from `bus.model()` +
|
||||||
/// `events::context_window_tokens`).
|
/// `events::context_window_tokens`).
|
||||||
|
///
|
||||||
/// `0` disables proactive compaction (reactive path still applies).
|
/// `0` disables proactive compaction (reactive path still applies).
|
||||||
fn compact_watermark_tokens(bus: &Bus) -> u64 {
|
fn compact_watermark_tokens(bus: &Bus) -> u64 {
|
||||||
if let Some(v) = std::env::var("HIVE_COMPACT_WATERMARK_TOKENS")
|
if let Some(v) = std::env::var("HIVE_COMPACT_WATERMARK_TOKENS")
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,9 @@ async fn api_stats(
|
||||||
// Pass the window span to the reminder-stats RPC so the broker
|
// Pass the window span to the reminder-stats RPC so the broker
|
||||||
// filters its counts to the same time range as the chart data.
|
// filters its counts to the same time range as the chart data.
|
||||||
let window_secs = window.span_secs();
|
let window_secs = window.span_secs();
|
||||||
snapshot.reminder_stats = fetch_reminder_stats(&state.socket, state.flavor(), window_secs as u64).await;
|
#[allow(clippy::cast_sign_loss)] // window span is always a positive duration
|
||||||
|
let window_secs_u = window_secs.max(0) as u64;
|
||||||
|
snapshot.reminder_stats = fetch_reminder_stats(&state.socket, state.flavor(), window_secs_u).await;
|
||||||
axum::Json(snapshot)
|
axum::Json(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use crate::lifecycle::{self, MANAGER_NAME};
|
||||||
///
|
///
|
||||||
/// In all cases an `ApprovalResolved` helper event lands in the manager's
|
/// In all cases an `ApprovalResolved` helper event lands in the manager's
|
||||||
/// inbox when the work resolves.
|
/// inbox when the work resolves.
|
||||||
|
#[allow(clippy::too_many_lines)] // approval dispatch covers several independent approval kinds
|
||||||
pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
|
pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
|
||||||
let approval = coord.approvals.mark_approved(id)?;
|
let approval = coord.approvals.mark_approved(id)?;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
|
|
@ -268,6 +269,7 @@ fn finish_approval(
|
||||||
/// and reset the working tree back to the last known-good main. main
|
/// and reset the working tree back to the last known-good main. main
|
||||||
/// never advances on a failed build, so a crash-and-recover doesn't
|
/// never advances on a failed build, so a crash-and-recover doesn't
|
||||||
/// leave the agent pointing at a tree it can't evaluate.
|
/// leave the agent pointing at a tree it can't evaluate.
|
||||||
|
#[allow(clippy::too_many_lines)] // sequential build/tag/notify pipeline; splitting would obscure the flow
|
||||||
async fn run_apply_commit(
|
async fn run_apply_commit(
|
||||||
coord: &Arc<Coordinator>,
|
coord: &Arc<Coordinator>,
|
||||||
approval: &hive_sh4re::Approval,
|
approval: &hive_sh4re::Approval,
|
||||||
|
|
|
||||||
|
|
@ -609,6 +609,7 @@ impl Broker {
|
||||||
/// in the last `since_secs` seconds (0 = all reminders).
|
/// in the last `since_secs` seconds (0 = all reminders).
|
||||||
pub fn reminder_rollup_for(&self, agent: &str, since_secs: u64) -> Result<hive_sh4re::ReminderStats> {
|
pub fn reminder_rollup_for(&self, agent: &str, since_secs: u64) -> Result<hive_sh4re::ReminderStats> {
|
||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
|
#[allow(clippy::cast_possible_wrap)] // unix epoch secs fit in i64 until year 292B
|
||||||
let cutoff_time = if since_secs > 0 {
|
let cutoff_time = if since_secs > 0 {
|
||||||
let now = std::time::SystemTime::now()
|
let now = std::time::SystemTime::now()
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,8 @@ fn is_rate_limited(name: &str) -> bool {
|
||||||
/// silently yields `None` so a missing/corrupt file never blocks
|
/// silently yields `None` so a missing/corrupt file never blocks
|
||||||
/// `build_all`.
|
/// `build_all`.
|
||||||
///
|
///
|
||||||
/// Context tokens = `last_input_tokens + last_cache_read_input_tokens
|
/// Context tokens are the sum of `last_input_tokens`, `last_cache_read_input_tokens`,
|
||||||
/// + last_cache_creation_input_tokens`, mirroring
|
/// and `last_cache_creation_input_tokens`, mirroring
|
||||||
/// `hive_ag3nt::events::TokenUsage::context_tokens`.
|
/// `hive_ag3nt::events::TokenUsage::context_tokens`.
|
||||||
fn read_last_ctx_tokens(name: &str) -> Option<u64> {
|
fn read_last_ctx_tokens(name: &str) -> Option<u64> {
|
||||||
let path = Coordinator::agent_notes_dir(name).join("hyperhive-turn-stats.sqlite");
|
let path = Coordinator::agent_notes_dir(name).join("hyperhive-turn-stats.sqlite");
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const KEEP_SECS: i64 = 7 * 24 * 3600;
|
||||||
/// Background loop: sweep every existing agent state dir hourly, run
|
/// Background loop: sweep every existing agent state dir hourly, run
|
||||||
/// the vacuum SQL against its events.sqlite if present. Errors are
|
/// the vacuum SQL against its events.sqlite if present. Errors are
|
||||||
/// logged but don't tear the loop down.
|
/// logged but don't tear the loop down.
|
||||||
pub fn spawn(coord: Arc<Coordinator>) {
|
pub fn spawn(coord: &Arc<Coordinator>) {
|
||||||
let mut shutdown = coord.shutdown_rx();
|
let mut shutdown = coord.shutdown_rx();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
||||||
|
|
@ -767,69 +767,6 @@ async fn systemd_daemon_reload() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Regression test: setup_proposed must seed both agent.nix and flake.nix
|
|
||||||
/// in the initial commit. Before commit 5b5a93e flake.nix was missing from
|
|
||||||
/// the scaffold, requiring manual creation (seen with the damocles agent).
|
|
||||||
#[tokio::test]
|
|
||||||
async fn setup_proposed_seeds_flake_nix() {
|
|
||||||
let dir = tempfile::tempdir().expect("tempdir");
|
|
||||||
let proposed = dir.path().join("proposed");
|
|
||||||
setup_proposed(&proposed, "test-agent")
|
|
||||||
.await
|
|
||||||
.expect("setup_proposed");
|
|
||||||
|
|
||||||
// Both files must exist on disk.
|
|
||||||
assert!(proposed.join("agent.nix").exists(), "agent.nix missing");
|
|
||||||
assert!(proposed.join("flake.nix").exists(), "flake.nix missing");
|
|
||||||
|
|
||||||
// flake.nix must export nixosModules.default (the meta-flake contract).
|
|
||||||
let flake = std::fs::read_to_string(proposed.join("flake.nix")).unwrap();
|
|
||||||
assert!(
|
|
||||||
flake.contains("nixosModules.default"),
|
|
||||||
"flake.nix does not export nixosModules.default"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Both files must be tracked in the initial git commit.
|
|
||||||
let out = git_command()
|
|
||||||
.current_dir(&proposed)
|
|
||||||
.args(["show", "--name-only", "--format=", "HEAD"])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.expect("git show");
|
|
||||||
let tracked = String::from_utf8_lossy(&out.stdout);
|
|
||||||
assert!(tracked.contains("agent.nix"), "agent.nix not committed");
|
|
||||||
assert!(tracked.contains("flake.nix"), "flake.nix not committed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// setup_proposed is idempotent: calling it on an existing repo is a
|
|
||||||
/// no-op (the fresh guard skips all writes).
|
|
||||||
#[tokio::test]
|
|
||||||
async fn setup_proposed_idempotent() {
|
|
||||||
let dir = tempfile::tempdir().expect("tempdir");
|
|
||||||
let proposed = dir.path().join("proposed");
|
|
||||||
setup_proposed(&proposed, "test-agent")
|
|
||||||
.await
|
|
||||||
.expect("first call");
|
|
||||||
// Second call must not error even though .git already exists.
|
|
||||||
setup_proposed(&proposed, "test-agent")
|
|
||||||
.await
|
|
||||||
.expect("second call");
|
|
||||||
// Still one commit.
|
|
||||||
let out = git_command()
|
|
||||||
.current_dir(&proposed)
|
|
||||||
.args(["rev-list", "--count", "HEAD"])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.expect("git rev-list");
|
|
||||||
let count = String::from_utf8_lossy(&out.stdout).trim().to_owned();
|
|
||||||
assert_eq!(count, "1", "expected exactly one commit after idempotent call");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Idempotently rewrite the lines in `/etc/nixos-containers/<container>.conf`
|
/// Idempotently rewrite the lines in `/etc/nixos-containers/<container>.conf`
|
||||||
/// that hive-c0re owns: `PRIVATE_NETWORK` (forced 0 so the agent's web UI port
|
/// that hive-c0re owns: `PRIVATE_NETWORK` (forced 0 so the agent's web UI port
|
||||||
/// is reachable on the host) and `EXTRA_NSPAWN_FLAGS` (the runtime-dir bind).
|
/// is reachable on the host) and `EXTRA_NSPAWN_FLAGS` (the runtime-dir bind).
|
||||||
|
|
@ -1097,3 +1034,66 @@ async fn container_journal_tail(args: &[&str]) -> String {
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Regression test: `setup_proposed` must seed both agent.nix and flake.nix
|
||||||
|
/// in the initial commit. Before commit 5b5a93e flake.nix was missing from
|
||||||
|
/// the scaffold, requiring manual creation (seen with the damocles agent).
|
||||||
|
#[tokio::test]
|
||||||
|
async fn setup_proposed_seeds_flake_nix() {
|
||||||
|
let dir = tempfile::tempdir().expect("tempdir");
|
||||||
|
let proposed = dir.path().join("proposed");
|
||||||
|
setup_proposed(&proposed, "test-agent")
|
||||||
|
.await
|
||||||
|
.expect("setup_proposed");
|
||||||
|
|
||||||
|
// Both files must exist on disk.
|
||||||
|
assert!(proposed.join("agent.nix").exists(), "agent.nix missing");
|
||||||
|
assert!(proposed.join("flake.nix").exists(), "flake.nix missing");
|
||||||
|
|
||||||
|
// flake.nix must export nixosModules.default (the meta-flake contract).
|
||||||
|
let flake = std::fs::read_to_string(proposed.join("flake.nix")).unwrap();
|
||||||
|
assert!(
|
||||||
|
flake.contains("nixosModules.default"),
|
||||||
|
"flake.nix does not export nixosModules.default"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Both files must be tracked in the initial git commit.
|
||||||
|
let out = git_command()
|
||||||
|
.current_dir(&proposed)
|
||||||
|
.args(["show", "--name-only", "--format=", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.expect("git show");
|
||||||
|
let tracked = String::from_utf8_lossy(&out.stdout);
|
||||||
|
assert!(tracked.contains("agent.nix"), "agent.nix not committed");
|
||||||
|
assert!(tracked.contains("flake.nix"), "flake.nix not committed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `setup_proposed` is idempotent: calling it on an existing repo is a
|
||||||
|
/// no-op (the fresh guard skips all writes).
|
||||||
|
#[tokio::test]
|
||||||
|
async fn setup_proposed_idempotent() {
|
||||||
|
let dir = tempfile::tempdir().expect("tempdir");
|
||||||
|
let proposed = dir.path().join("proposed");
|
||||||
|
setup_proposed(&proposed, "test-agent")
|
||||||
|
.await
|
||||||
|
.expect("first call");
|
||||||
|
// Second call must not error even though .git already exists.
|
||||||
|
setup_proposed(&proposed, "test-agent")
|
||||||
|
.await
|
||||||
|
.expect("second call");
|
||||||
|
// Still one commit.
|
||||||
|
let out = git_command()
|
||||||
|
.current_dir(&proposed)
|
||||||
|
.args(["rev-list", "--count", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.expect("git rev-list");
|
||||||
|
let count = String::from_utf8_lossy(&out.stdout).trim().to_owned();
|
||||||
|
assert_eq!(count, "1", "expected exactly one commit after idempotent call");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ enum Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
#[allow(clippy::too_many_lines)] // top-level dispatch; hard to split without losing readability
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
|
|
@ -186,10 +187,10 @@ async fn main() -> Result<()> {
|
||||||
});
|
});
|
||||||
// Per-agent events.sqlite vacuum: host-side so the harness
|
// Per-agent events.sqlite vacuum: host-side so the harness
|
||||||
// doesn't need any retention wiring of its own.
|
// doesn't need any retention wiring of its own.
|
||||||
events_vacuum::spawn(coord.clone());
|
events_vacuum::spawn(&coord);
|
||||||
// Per-agent turn-stats.sqlite vacuum: same pattern, 90-day
|
// Per-agent turn-stats.sqlite vacuum: same pattern, 90-day
|
||||||
// retention so trend analysis has enough history.
|
// retention so trend analysis has enough history.
|
||||||
stats_vacuum::spawn(coord.clone());
|
stats_vacuum::spawn(&coord);
|
||||||
// Container crash watcher: emits HelperEvent::ContainerCrash
|
// Container crash watcher: emits HelperEvent::ContainerCrash
|
||||||
// when a previously-running container goes away without an
|
// when a previously-running container goes away without an
|
||||||
// operator-initiated transient state.
|
// operator-initiated transient state.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ const KEEP_SECS: i64 = 90 * 24 * 3600;
|
||||||
/// Background loop: sweep every existing agent state dir hourly, run
|
/// Background loop: sweep every existing agent state dir hourly, run
|
||||||
/// the vacuum SQL against its turn-stats.sqlite if present. Errors
|
/// the vacuum SQL against its turn-stats.sqlite if present. Errors
|
||||||
/// are logged but don't tear the loop down.
|
/// are logged but don't tear the loop down.
|
||||||
pub fn spawn(coord: Arc<Coordinator>) {
|
pub fn spawn(coord: &Arc<Coordinator>) {
|
||||||
let mut shutdown = coord.shutdown_rx();
|
let mut shutdown = coord.shutdown_rx();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue