docs: add missing #[must_use], # Errors, # Panics across public api
This commit is contained in:
parent
748536203b
commit
908cadb151
10 changed files with 157 additions and 0 deletions
|
|
@ -19,6 +19,11 @@ const RETRY_BACKOFFS_MS: &[u64] = &[2_000, 4_000, 8_000, 16_000, 30_000];
|
|||
/// the retry count. Use this from non-tool callers (the harness serve
|
||||
/// loop, web UI, CLI subcommands) where we just want the socket-restart
|
||||
/// resilience without surfacing the bookkeeping.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the socket is unreachable after all retries, or if
|
||||
/// serialization / deserialization of the request or response fails.
|
||||
pub async fn request<Req, Resp>(socket: &Path, req: &Req) -> Result<Resp>
|
||||
where
|
||||
Req: Serialize + ?Sized,
|
||||
|
|
@ -33,6 +38,16 @@ where
|
|||
/// retries happened — that way claude knows the prior socket flake
|
||||
/// wasn't a content error and shouldn't trigger an LLM-level retry of
|
||||
/// its own.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if all retries are exhausted, or on a fatal protocol
|
||||
/// error (serialization / deserialization failure).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `RETRY_BACKOFFS_MS.len()` does not fit in a `u32`, which
|
||||
/// cannot happen with the current compile-time constant.
|
||||
pub async fn request_retried<Req, Resp>(socket: &Path, req: &Req) -> Result<(Resp, u32)>
|
||||
where
|
||||
Req: Serialize + ?Sized,
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ pub struct TokenUsage {
|
|||
|
||||
impl TokenUsage {
|
||||
/// Total context consumed this turn (input + cache reads + cache writes).
|
||||
#[must_use]
|
||||
pub fn context_tokens(&self) -> u64 {
|
||||
self.input_tokens + self.cache_read_input_tokens + self.cache_creation_input_tokens
|
||||
}
|
||||
|
|
@ -230,6 +231,7 @@ impl TokenUsage {
|
|||
/// **cumulative** sum across every inference in the turn — useful as a
|
||||
/// cost signal, but NOT the current context size (a tool-heavy turn
|
||||
/// sums per-call cached prompts and easily exceeds the model window).
|
||||
#[must_use]
|
||||
pub fn from_stream_event(v: &serde_json::Value) -> Option<Self> {
|
||||
if v.get("type").and_then(|t| t.as_str()) != Some("result") {
|
||||
return None;
|
||||
|
|
@ -241,6 +243,7 @@ impl TokenUsage {
|
|||
/// `.message.usage` block. Each turn fires one of these for every
|
||||
/// model call; tracking the LAST one over the turn gives the actual
|
||||
/// conversation context size — the number to watch for compaction.
|
||||
#[must_use]
|
||||
pub fn from_assistant_event(v: &serde_json::Value) -> Option<Self> {
|
||||
if v.get("type").and_then(|t| t.as_str()) != Some("assistant") {
|
||||
return None;
|
||||
|
|
@ -443,12 +446,17 @@ impl Bus {
|
|||
|
||||
/// Take + clear the one-shot. Returns true iff the caller should
|
||||
/// run claude without `--continue` for this turn.
|
||||
#[must_use]
|
||||
pub fn take_skip_continue(&self) -> bool {
|
||||
self.skip_continue_once.swap(false, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Currently-selected claude model name. Read on every turn so a
|
||||
/// `/model <name>` flip takes effect on the next turn.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn model(&self) -> String {
|
||||
self.model.lock().unwrap().clone()
|
||||
|
|
@ -459,6 +467,10 @@ impl Bus {
|
|||
/// state dir (`hyperhive-model`) so the override survives harness
|
||||
/// restart and container rebuild (gone on `--purge`, matching
|
||||
/// every other piece of agent state).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn set_model(&self, name: impl Into<String>) {
|
||||
let value: String = name.into();
|
||||
self.model.lock().unwrap().clone_from(&value);
|
||||
|
|
@ -472,6 +484,10 @@ impl Bus {
|
|||
/// emitting a SSE event. Used by the bin entrypoints to backfill
|
||||
/// from the most recent `turn_stats` row so the per-agent web UI's
|
||||
/// ctx + cost badges paint real numbers on cold load.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an internal lock is poisoned.
|
||||
pub fn seed_usage(&self, ctx: Option<TokenUsage>, cost: Option<TokenUsage>) {
|
||||
if ctx.is_some() {
|
||||
*self.last_ctx_usage.lock().unwrap() = ctx;
|
||||
|
|
@ -485,6 +501,10 @@ impl Bus {
|
|||
/// usage (current context size); `cost` is the cumulative across
|
||||
/// every inference in the turn (cost signal). One SSE event fires
|
||||
/// per turn carrying both.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an internal lock is poisoned.
|
||||
pub fn record_turn_usage(&self, ctx: TokenUsage, cost: TokenUsage) {
|
||||
*self.last_ctx_usage.lock().unwrap() = Some(ctx);
|
||||
*self.last_cost_usage.lock().unwrap() = Some(cost);
|
||||
|
|
@ -503,6 +523,10 @@ impl Bus {
|
|||
/// per-turn counter for each one we find. Called by the stdout
|
||||
/// pump on every parsed line. Cheap when the line isn't an
|
||||
/// assistant message — the field-check short-circuits.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn observe_stream(&self, v: &serde_json::Value) {
|
||||
if v.get("type").and_then(|t| t.as_str()) != Some("assistant") {
|
||||
return;
|
||||
|
|
@ -531,6 +555,10 @@ impl Bus {
|
|||
/// Snapshot + clear the per-turn tool-call counter. The harness
|
||||
/// calls this between turns to fold the breakdown into a
|
||||
/// `turn_stats` row, then start the next turn with an empty map.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn take_tool_calls(&self) -> std::collections::HashMap<String, u64> {
|
||||
std::mem::take(&mut *self.tool_calls.lock().unwrap())
|
||||
|
|
@ -538,6 +566,10 @@ impl Bus {
|
|||
|
||||
/// Last context-size snapshot (last inference of the most recent
|
||||
/// turn), or `None` if no turn has completed yet.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn last_ctx_usage(&self) -> Option<TokenUsage> {
|
||||
*self.last_ctx_usage.lock().unwrap()
|
||||
|
|
@ -545,6 +577,10 @@ impl Bus {
|
|||
|
||||
/// Last cumulative cost snapshot (sum across the most recent turn's
|
||||
/// inferences), or `None` if no turn has completed yet.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn last_cost_usage(&self) -> Option<TokenUsage> {
|
||||
*self.last_cost_usage.lock().unwrap()
|
||||
|
|
@ -552,6 +588,10 @@ impl Bus {
|
|||
|
||||
/// Update the harness's authoritative turn-loop state. Records
|
||||
/// the transition time so `state_snapshot` can return a since-age.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn set_state(&self, next: TurnState) {
|
||||
let since;
|
||||
{
|
||||
|
|
@ -598,6 +638,10 @@ impl Bus {
|
|||
}
|
||||
|
||||
/// Current state + since-when (unix seconds). Snapshot copy, no lock held.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn state_snapshot(&self) -> (TurnState, i64) {
|
||||
*self.state.lock().unwrap()
|
||||
|
|
@ -617,6 +661,7 @@ impl Bus {
|
|||
let _ = self.tx.send(envelope);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn subscribe(&self) -> broadcast::Receiver<BusEvent> {
|
||||
self.tx.subscribe()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@ impl LoginSession {
|
|||
/// `HYPERHIVE_LOGIN_CMD` (single string, shell-split into argv); by
|
||||
/// default we run `claude auth login`. Failing to spawn returns an error
|
||||
/// before any state is registered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if spawning the login command fails, or if the child's
|
||||
/// stdio handles cannot be acquired.
|
||||
pub fn start() -> Result<Self> {
|
||||
let (cmd, args) = resolve_command();
|
||||
tracing::info!(%cmd, ?args, "spawning login session");
|
||||
|
|
@ -82,6 +87,11 @@ impl LoginSession {
|
|||
/// Write `code` (plus a newline) to the child's stdin. Returns an error
|
||||
/// if the stdin has already been closed (e.g. after the child exited or
|
||||
/// after a prior submission consumed it).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the login stdin is already closed, or if writing
|
||||
/// to or flushing the stdin pipe fails.
|
||||
pub async fn submit_code(&self, code: &str) -> Result<()> {
|
||||
let mut guard = self.stdin.lock().await;
|
||||
let stdin = guard.as_mut().context("login stdin already closed")?;
|
||||
|
|
@ -100,18 +110,34 @@ impl LoginSession {
|
|||
let _ = self.stdin.lock().await.take();
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn output(&self) -> String {
|
||||
self.state.lock().unwrap().output.clone()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn url(&self) -> Option<String> {
|
||||
self.state.lock().unwrap().url.clone()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn finished(&self) -> bool {
|
||||
self.state.lock().unwrap().finished
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn exit_note(&self) -> Option<String> {
|
||||
self.state.lock().unwrap().exit_note.clone()
|
||||
}
|
||||
|
|
@ -119,6 +145,10 @@ impl LoginSession {
|
|||
/// Best-effort: poll the child once and update `finished`/`exit_note`.
|
||||
/// Called by the web UI on each render so the state stays fresh without
|
||||
/// running a dedicated reaper task.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an internal lock is poisoned.
|
||||
pub fn poll(&self) {
|
||||
let mut child = self.child.lock().unwrap();
|
||||
match child.try_wait() {
|
||||
|
|
@ -137,6 +167,10 @@ impl LoginSession {
|
|||
}
|
||||
|
||||
/// Kill the child if it's still running. Idempotent.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn kill(&self) {
|
||||
if let Err(e) = self.child.lock().unwrap().start_kill() {
|
||||
tracing::warn!(error = ?e, "kill login child");
|
||||
|
|
@ -217,6 +251,10 @@ fn extract_url(line: &str) -> Option<String> {
|
|||
|
||||
/// Helper used by the web UI to gate "is there a session running right now"
|
||||
/// without holding both this module's mutex and the `AppState`'s at once.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn drop_if_finished(slot: &Mutex<Option<Arc<LoginSession>>>) {
|
||||
let mut guard = slot.lock().unwrap();
|
||||
if let Some(s) = guard.as_ref() {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
|
|||
/// Format helper for "send-like" tools (anything that expects an `Ok`).
|
||||
/// `tool` and `ok_msg` only appear in the result string; they don't change
|
||||
/// behavior.
|
||||
#[must_use]
|
||||
pub fn format_ack(resp: Result<SocketReply, anyhow::Error>, tool: &str, ok_msg: String) -> String {
|
||||
match resp {
|
||||
Ok(SocketReply::Ok) => ok_msg,
|
||||
|
|
@ -131,6 +132,7 @@ pub fn format_ack(resp: Result<SocketReply, anyhow::Error>, tool: &str, ok_msg:
|
|||
/// and `---` separators between bodies so the model can tell where
|
||||
/// one ends and the next begins; per-message redelivery banners
|
||||
/// included.
|
||||
#[must_use]
|
||||
pub fn format_recv(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||
use std::fmt::Write as _;
|
||||
let messages = match resp {
|
||||
|
|
@ -171,6 +173,7 @@ pub const REDELIVERY_HINT: &str =
|
|||
/// of pending approvals + questions + reminders. Empty list collapses
|
||||
/// to a clear marker so claude doesn't go hunting for a payload that
|
||||
/// isn't there.
|
||||
#[must_use]
|
||||
pub fn format_loose_ends(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||
use std::fmt::Write as _;
|
||||
let loose_ends = match resp {
|
||||
|
|
@ -259,6 +262,7 @@ fn loose_end_kind_label(kind: hive_sh4re::CancelLooseEndKind) -> &'static str {
|
|||
/// Format helper for `whoami`: renders the identity block as a short
|
||||
/// human-readable string. Skips fields that are `None` so the output
|
||||
/// doesn't carry dead placeholders.
|
||||
#[must_use]
|
||||
pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||
match resp {
|
||||
Ok(SocketReply::Whoami {
|
||||
|
|
@ -294,6 +298,7 @@ where
|
|||
/// from "c0re flickered and the harness rode it out" — without the
|
||||
/// hint, a tool result that took 30s to come back looks identical to a
|
||||
/// content failure and the model would burn a turn retrying it.
|
||||
#[must_use]
|
||||
pub fn annotate_retries(mut s: String, retries: u32) -> String {
|
||||
if retries > 0 {
|
||||
use std::fmt::Write as _;
|
||||
|
|
@ -660,6 +665,11 @@ impl AgentServer {
|
|||
impl ServerHandler for AgentServer {}
|
||||
|
||||
/// Run the agent MCP server over stdio. Returns when the client disconnects.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the MCP server fails to initialize or the transport
|
||||
/// encounters a fatal error.
|
||||
pub async fn serve_agent_stdio(socket: PathBuf) -> Result<()> {
|
||||
let server = AgentServer::new(socket);
|
||||
let service = server.serve(stdio()).await?;
|
||||
|
|
@ -668,6 +678,11 @@ pub async fn serve_agent_stdio(socket: PathBuf) -> Result<()> {
|
|||
}
|
||||
|
||||
/// Run the manager MCP server over stdio. Same idea, different tool surface.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the MCP server fails to initialize or the transport
|
||||
/// encounters a fatal error.
|
||||
pub async fn serve_manager_stdio(socket: PathBuf) -> Result<()> {
|
||||
let server = ManagerServer::new(socket);
|
||||
let service = server.serve(stdio()).await?;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::turn_stats::TurnStatRow;
|
|||
/// system prompt; this is just the wake signal body. `unread` is the inbox
|
||||
/// depth after this message was popped. `redelivered` prepends a "may already
|
||||
/// be handled" banner.
|
||||
#[must_use]
|
||||
pub fn format_wake_prompt(from: &str, body: &str, unread: u64, redelivered: bool) -> String {
|
||||
let banner = if redelivered { REDELIVERY_HINT } else { "" };
|
||||
let pending = if unread == 0 {
|
||||
|
|
@ -26,6 +27,7 @@ pub fn format_wake_prompt(from: &str, body: &str, unread: u64, redelivered: bool
|
|||
}
|
||||
|
||||
/// Current time as a Unix timestamp (seconds). Returns 0 on any error.
|
||||
#[must_use]
|
||||
pub fn now_unix() -> i64 {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
|
|
@ -37,6 +39,7 @@ pub fn now_unix() -> i64 {
|
|||
/// Assemble a `TurnStatRow` from the harness's per-turn state. Used by both
|
||||
/// the agent and manager serve loops — the shape is identical, only the
|
||||
/// post-turn count fetch helpers differ (and those stay in each binary).
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_row(
|
||||
started_at: i64,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub enum Window {
|
|||
}
|
||||
|
||||
impl Window {
|
||||
#[must_use]
|
||||
pub fn parse(s: &str) -> Self {
|
||||
match s {
|
||||
"1h" => Self::Hour,
|
||||
|
|
@ -51,6 +52,7 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn span_secs(self) -> i64 {
|
||||
match self {
|
||||
Self::Hour => 3600,
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ pub struct TurnFiles {
|
|||
impl TurnFiles {
|
||||
/// Write all three files into the per-agent runtime dir alongside
|
||||
/// `socket`. Idempotent — overwrites whatever was there.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if any of the config files cannot be written to disk.
|
||||
pub async fn prepare(socket: &Path, label: &str, flavor: mcp::Flavor) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mcp_config: write_mcp_config(socket).await?,
|
||||
|
|
@ -112,6 +116,10 @@ impl TurnFiles {
|
|||
/// as `--socket <path>`); `binary_subcommand` is e.g. `"mcp"` for sub-agents
|
||||
/// or `"mcp"` for the manager (both binaries name their MCP subcommand the
|
||||
/// same — the differentiator is which binary `/proc/self/exe` resolves to).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the config file cannot be written.
|
||||
pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
|
||||
let parent = socket.parent().unwrap_or_else(|| Path::new("/run/hive"));
|
||||
tokio::fs::create_dir_all(parent).await.ok();
|
||||
|
|
@ -128,6 +136,10 @@ pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
|
|||
/// Drop the static `--settings` JSON next to the MCP config so we can
|
||||
/// pass a path (`--settings <file>`) instead of an ever-growing inline
|
||||
/// blob — the CLI argv has a finite length budget.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the settings file cannot be written.
|
||||
pub async fn write_settings(socket: &Path) -> Result<PathBuf> {
|
||||
let parent = socket.parent().unwrap_or_else(|| Path::new("/run/hive"));
|
||||
tokio::fs::create_dir_all(parent).await.ok();
|
||||
|
|
@ -142,6 +154,10 @@ pub async fn write_settings(socket: &Path) -> Result<PathBuf> {
|
|||
/// `--system-prompt-file`, replacing claude's default system prompt with
|
||||
/// the role + tools instructions. Per-turn prompts become much smaller
|
||||
/// (just the wake message body).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the system prompt file cannot be written.
|
||||
pub async fn write_system_prompt(
|
||||
socket: &Path,
|
||||
label: &str,
|
||||
|
|
@ -399,6 +415,10 @@ pub fn emit_turn_end(bus: &Bus, outcome: &TurnOutcome) {
|
|||
/// Block until the bound `~/.claude/` dir contains a session, polling
|
||||
/// `claude_dir` on a `poll_ms` interval (min 2s). Flips `state` to
|
||||
/// `Online` when login lands; caller resumes its serve loop.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal login-state lock is poisoned.
|
||||
pub async fn wait_for_login(
|
||||
claude_dir: &Path,
|
||||
state: Arc<Mutex<LoginState>>,
|
||||
|
|
@ -442,6 +462,11 @@ pub async fn run_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutcome
|
|||
/// surface, same system prompt, same allowed-tools — so the post-
|
||||
/// compact state matches a normal turn's. Only the prompt over stdin
|
||||
/// differs (`/compact` vs the wake-up payload).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the `claude --print /compact` invocation fails
|
||||
/// (non-zero exit or I/O error).
|
||||
pub async fn compact_session(files: &TurnFiles, bus: &Bus) -> Result<()> {
|
||||
bus.emit(LiveEvent::Note {
|
||||
text: "context overflow — running /compact on the persistent session".into(),
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ impl TurnStats {
|
|||
|
||||
/// Insert a row. Best-effort — logs + swallows errors so a sqlite
|
||||
/// hiccup (locked db, full disk) doesn't crash the harness.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
pub fn record(&self, row: &TurnStatRow) {
|
||||
let conn = self.inner.lock().unwrap();
|
||||
let res = conn.execute(
|
||||
|
|
@ -209,6 +213,9 @@ impl TurnStats {
|
|||
/// have last-inference zeros — those rows yield `ctx = None` so the
|
||||
/// badge stays empty until the next real turn rather than showing a
|
||||
/// misleading 0.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn last_usage(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ impl AppState {
|
|||
/// `post_compact`) the allowed-tools surface claude sees.
|
||||
pub type Flavor = mcp::Flavor;
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the TCP listener cannot bind to the given port.
|
||||
pub async fn serve(
|
||||
label: String,
|
||||
port: u16,
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ pub struct ReminderStats {
|
|||
}
|
||||
|
||||
impl HostResponse {
|
||||
#[must_use]
|
||||
pub fn success() -> Self {
|
||||
Self {
|
||||
ok: true,
|
||||
|
|
@ -142,6 +143,7 @@ impl HostResponse {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn error(message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
ok: false,
|
||||
|
|
@ -151,6 +153,7 @@ impl HostResponse {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn list(agents: Vec<String>) -> Self {
|
||||
Self {
|
||||
ok: true,
|
||||
|
|
@ -160,6 +163,7 @@ impl HostResponse {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pending(approvals: Vec<Approval>) -> Self {
|
||||
Self {
|
||||
ok: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue