From 92b32d06fbf9103ede58909524e6756acb3b63e3 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 5 Jun 2026 14:28:05 +0200 Subject: [PATCH] fix(clippy): fix all clippy warnings in hive-ag3nt, hive-forge, hive-matrix-mcp, hive-sh4re Fixes all clippy -D warnings errors in the crates iris owns: hive-sh4re: - doc_lazy_continuation: add blank /// separator in priv_proto.rs - doc_markdown: backtick PRIVATE_NETWORK=0 / PRIVATE_NETWORK=1 hive-matrix-mcp: - map_unwrap_or: map().unwrap_or_else() -> map_or_else() in paths.rs - collapsible_if: if-let chains in wake.rs - doc_markdown: backtick M_UNKNOWN_TOKEN in main.rs - cast_possible_truncation: usize/u64 -> u32::try_from in handlers.rs - map_unwrap_or: map_or_else() in handlers.rs - manual_let_else: match Ok(r) => r, Err => return -> let Ok in handlers.rs - unused_async: remove async from list_invites; update socket.rs call site hive-forge: - doc_markdown: backtick REQUEST_CHANGES / APPROVED / COMMENT in pr_reviews.rs - unnecessary_wraps: list_reviews_text returns () not Result<()> - doc_markdown: backtick start_page / last_page in comments.rs - cast_possible_truncation: PAGE_SIZE u64 -> usize; remove as usize casts hive-ag3nt: - collapsible_if: if-let chains in events.rs and mcp.rs - single_match_else: match -> if let in events.rs and mcp.rs - items_after_statements: hoist STATUS_MAX_CHARS const in mcp.rs - map_unwrap_or: map_or_else() in mcp.rs and mcp_loose_ends.rs - cast_possible_truncation: usize -> u32::try_from in mcp.rs - doc_markdown: backtick snake_case in mcp.rs, needs_update/deployed_sha in web_ui.rs, HISTORY_CAPACITY in web_ui.rs - identical_match_arms: combine manage_root_agent | query_agent_state - redundant_closure: |s| s.to_string() -> ToString::to_string in web_ui.rs - duration_suboptimal_units: from_secs(3600) -> from_hours(1) in turn.rs Remaining failures in hive-c0re (39), hive-priv (8), hive-bash-mcp (11) are owned by damocles. --- hive-ag3nt/src/events.rs | 85 ++++++++++++++---------------- hive-ag3nt/src/mcp.rs | 78 +++++++++++++-------------- hive-ag3nt/src/mcp_loose_ends.rs | 3 +- hive-ag3nt/src/turn.rs | 2 +- hive-ag3nt/src/web_ui.rs | 8 +-- hive-forge/src/verbs/comments.rs | 8 +-- hive-forge/src/verbs/pr_reviews.rs | 12 ++--- hive-matrix-mcp/src/handlers.rs | 26 ++++----- hive-matrix-mcp/src/main.rs | 2 +- hive-matrix-mcp/src/paths.rs | 3 +- hive-matrix-mcp/src/socket.rs | 2 +- hive-matrix-mcp/src/wake.rs | 28 +++++----- hive-sh4re/src/priv_proto.rs | 4 +- 13 files changed, 128 insertions(+), 133 deletions(-) diff --git a/hive-ag3nt/src/events.rs b/hive-ag3nt/src/events.rs index ff7fafd9..db2afb70 100644 --- a/hive-ag3nt/src/events.rs +++ b/hive-ag3nt/src/events.rs @@ -80,18 +80,18 @@ fn harness_json_path() -> PathBuf { fn read_harness_state() -> (bool, bool) { // Try the new consolidated file first. - if let Ok(raw) = std::fs::read_to_string(harness_json_path()) { - if let Ok(v) = serde_json::from_str::(&raw) { - let rate_limited = v - .get("rate_limited") - .and_then(|x| x.as_bool()) - .unwrap_or(false); - let needs_login = v - .get("needs_login") - .and_then(|x| x.as_bool()) - .unwrap_or(false); - return (rate_limited, needs_login); - } + if let Ok(raw) = std::fs::read_to_string(harness_json_path()) + && let Ok(v) = serde_json::from_str::(&raw) + { + let rate_limited = v + .get("rate_limited") + .and_then(serde_json::Value::as_bool) + .unwrap_or(false); + let needs_login = v + .get("needs_login") + .and_then(serde_json::Value::as_bool) + .unwrap_or(false); + return (rate_limited, needs_login); } // Fall back to legacy sentinel files written by older harness builds. let state_dir = crate::paths::state_dir(); @@ -265,38 +265,35 @@ impl EventStore { let conn = self.conn.lock().unwrap(); // Fetch one extra row so we can tell whether more exist. let fetch = limit_i.saturating_add(1); - let rows: Vec<(i64, LiveEvent)> = match before_id { - Some(bid) => { - let mut stmt = conn.prepare( - "SELECT id, payload_json FROM events - WHERE id < ?1 - ORDER BY id DESC - LIMIT ?2", - )?; - stmt.query_map(params![bid, fetch], |row| { - let id: i64 = row.get(0)?; - let s: String = row.get(1)?; - Ok(serde_json::from_str::(&s).ok().map(|e| (id, e))) - })? - .flatten() - .flatten() - .collect() - } - None => { - let mut stmt = conn.prepare( - "SELECT id, payload_json FROM events - ORDER BY id DESC - LIMIT ?1", - )?; - stmt.query_map(params![fetch], |row| { - let id: i64 = row.get(0)?; - let s: String = row.get(1)?; - Ok(serde_json::from_str::(&s).ok().map(|e| (id, e))) - })? - .flatten() - .flatten() - .collect() - } + let rows: Vec<(i64, LiveEvent)> = if let Some(bid) = before_id { + let mut stmt = conn.prepare( + "SELECT id, payload_json FROM events + WHERE id < ?1 + ORDER BY id DESC + LIMIT ?2", + )?; + stmt.query_map(params![bid, fetch], |row| { + let id: i64 = row.get(0)?; + let s: String = row.get(1)?; + Ok(serde_json::from_str::(&s).ok().map(|e| (id, e))) + })? + .flatten() + .flatten() + .collect() + } else { + let mut stmt = conn.prepare( + "SELECT id, payload_json FROM events + ORDER BY id DESC + LIMIT ?1", + )?; + stmt.query_map(params![fetch], |row| { + let id: i64 = row.get(0)?; + let s: String = row.get(1)?; + Ok(serde_json::from_str::(&s).ok().map(|e| (id, e))) + })? + .flatten() + .flatten() + .collect() }; let has_more = rows.len() > limit; let mut rows: Vec<(i64, LiveEvent)> = rows.into_iter().take(limit).collect(); diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index f6ee89db..278452df 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -111,6 +111,9 @@ impl From for SocketReply { /// the file is never written with text the server would later reject (which /// would leave a stale invalid entry on disk). fn write_status_file(text: &str) -> Result<(), String> { + // 200 chars mirrors STATUS_MAX_CHARS in hive-c0re/src/limits.rs. + // Keep in sync if that constant changes. + const STATUS_MAX_CHARS: usize = 200; let trimmed = text.trim(); if !trimmed.is_empty() { if trimmed.contains('\n') || trimmed.contains('\r') { @@ -120,9 +123,6 @@ fn write_status_file(text: &str) -> Result<(), String> { .to_owned(), ); } - // 200 chars mirrors STATUS_MAX_CHARS in hive-c0re/src/limits.rs. - // Keep in sync if that constant changes. - const STATUS_MAX_CHARS: usize = 200; let len = trimmed.chars().count(); if len > STATUS_MAX_CHARS { return Err(format!( @@ -310,9 +310,10 @@ struct MatrixRoomUnread { async fn matrix_unread_summary() -> Option> { use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::UnixStream; - let socket = std::env::var_os("HIVE_MATRIX_SOCKET") - .map(std::path::PathBuf::from) - .unwrap_or_else(|| std::path::PathBuf::from("/run/hive-matrix/socket")); + let socket = std::env::var_os("HIVE_MATRIX_SOCKET").map_or_else( + || std::path::PathBuf::from("/run/hive-matrix/socket"), + std::path::PathBuf::from, + ); if !socket.exists() { return None; } @@ -339,11 +340,11 @@ fn format_matrix_summary(rooms: &[MatrixRoomUnread]) -> String { } let mut out = String::new(); for r in rooms { - if r.count == 1 { - if let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) { - let _ = writeln!(out, "- {}: {sender}: {body}", r.label); - continue; - } + if r.count == 1 + && let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) + { + let _ = writeln!(out, "- {}: {sender}: {body}", r.label); + continue; } let _ = writeln!(out, "- {}: {} unread", r.label, r.count); } @@ -775,19 +776,19 @@ impl AgentServer { }; // Prepend matrix unread entry for self-queries only (can't // reach another agent's matrix daemon from here). - if is_self_query { - if let Some(unread_rooms) = matrix_unread_summary().await { - let total = unread_rooms.len() as u32; - if total > 0 { - let summary = format_matrix_summary(&unread_rooms); - loose_ends.insert( - 0, - hive_sh4re::LooseEnd::UnreadMatrix { - rooms: total, - summary, - }, - ); - } + if is_self_query + && let Some(unread_rooms) = matrix_unread_summary().await + { + let total = u32::try_from(unread_rooms.len()).unwrap_or(u32::MAX); + if total > 0 { + let summary = format_matrix_summary(&unread_rooms); + loose_ends.insert( + 0, + hive_sh4re::LooseEnd::UnreadMatrix { + rooms: total, + summary, + }, + ); } } let mut out = annotate_retries(render_loose_ends(&loose_ends), retries); @@ -1754,14 +1755,14 @@ pub const SERVER_NAME: &str = "hyperhive"; pub const ALLOWED_BUILTIN_TOOLS: &[&str] = &["Edit", "Glob", "Grep", "Read", "Write"]; /// Env var written by the meta renderer with a comma-separated list of -/// `hive_sh4re::ToolGroup` snake_case names (e.g. `"messaging,inbox,meta"`). +/// `hive_sh4re::ToolGroup` `snake_case` names (e.g. `"messaging,inbox,meta"`). /// When present, the harness expands the groups into per-tool allow entries /// instead of using the hardcoded flavor default. See `docs/conventions.md::Tool groups`. const TOOL_GROUPS_ENV: &str = "HIVE_TOOL_GROUPS"; /// `HIVE_CAPABILITIES` env var injected by `meta::render_flake` when the /// operator grants capabilities to this agent. Comma-separated -/// `hive_sh4re::Capability` snake_case names. Absent = no extra capabilities. +/// `hive_sh4re::Capability` `snake_case` names. Absent = no extra capabilities. const CAPABILITIES_ENV: &str = "HIVE_CAPABILITIES"; /// Returns the MCP tool names (without `mcp__hyperhive__` prefix) that are @@ -1778,14 +1779,12 @@ fn allowed_capability_tools() -> Vec { let t = token.trim().to_ascii_lowercase(); match t.as_str() { "read_host_journal" => tools.push("get_host_journal".to_owned()), - // manage_root_agent doesn't expose new MCP tools (it gates - // existing lifecycle tools via the topology enforcement). - "manage_root_agent" => {} - // query_agent_state doesn't expose new MCP tools; it unlocks - // the `agent` field in get_loose_ends / count_pending_reminders - // / reminder_rollup on the agent socket (c0re enforces the cap - // server-side; the harness honours it by passing the field). - "query_agent_state" => {} + // manage_root_agent / query_agent_state don't expose new MCP + // tools: manage_root_agent gates existing lifecycle tools via + // topology enforcement; query_agent_state unlocks the `agent` + // field in get_loose_ends / count_pending_reminders / + // reminder_rollup (c0re enforces the cap server-side). + "manage_root_agent" | "query_agent_state" => {} unknown => { tracing::warn!(capability = %unknown, "unrecognised capability in HIVE_CAPABILITIES — skipped"); } @@ -1810,13 +1809,12 @@ fn effective_tool_groups() -> Vec { for token in raw.split(',') { let t = token.trim().to_ascii_lowercase(); // Parse via serde_json (the canonical deserialization path). - match serde_json::from_value::(serde_json::Value::String(t.clone())) + if let Ok(g) = + serde_json::from_value::(serde_json::Value::String(t.clone())) { - Ok(g) => groups.push(g), - Err(_) => tracing::warn!( - token = %t, - "{TOOL_GROUPS_ENV}: unknown tool group, skipping" - ), + groups.push(g); + } else { + tracing::warn!(token = %t, "{TOOL_GROUPS_ENV}: unknown tool group, skipping"); } } if groups.is_empty() { diff --git a/hive-ag3nt/src/mcp_loose_ends.rs b/hive-ag3nt/src/mcp_loose_ends.rs index 222a6392..c61f4260 100644 --- a/hive-ag3nt/src/mcp_loose_ends.rs +++ b/hive-ag3nt/src/mcp_loose_ends.rs @@ -21,8 +21,7 @@ fn loose_ends_dir() -> PathBuf { let state_path = PathBuf::from(&state); state_path .parent() - .map(|p| p.join("harness")) - .unwrap_or_else(|| PathBuf::from(state)) + .map_or_else(|| PathBuf::from(state), |p| p.join("harness")) }; base.join("mcp-loose-ends") } diff --git a/hive-ag3nt/src/turn.rs b/hive-ag3nt/src/turn.rs index d74027c2..d418e990 100644 --- a/hive-ag3nt/src/turn.rs +++ b/hive-ag3nt/src/turn.rs @@ -905,7 +905,7 @@ mod tests { fs::write(dir.path().join("credentials.json"), b"{}").unwrap(); let snapshot = DirSnapshot { file_count: 1, - newest_mtime: Some(SystemTime::now() + Duration::from_secs(3600)), + newest_mtime: Some(SystemTime::now() + Duration::from_hours(1)), }; assert!(!session_refreshed(snapshot, snapshot_dir(dir.path()))); } diff --git a/hive-ag3nt/src/web_ui.rs b/hive-ag3nt/src/web_ui.rs index 6d3e62b0..25ed295e 100644 --- a/hive-ag3nt/src/web_ui.rs +++ b/hive-ag3nt/src/web_ui.rs @@ -579,7 +579,7 @@ async fn api_state(State(state): State) -> axum::Json { /// fetches this once per running agent to get fresh, agent-authoritative /// values instead of relying on hive-c0re's periodic file-reads. /// -/// Structural fields (running, needs_update, deployed_sha, parent, …) +/// Structural fields (running, `needs_update`, `deployed_sha`, parent, …) /// continue to come from hive-c0re's `/api/state`; this endpoint covers /// only the fields the agent itself is the source of truth for. #[derive(serde::Serialize)] @@ -808,7 +808,7 @@ struct HistoryParams { /// Cursor: only return events with sqlite row id < `before`. /// Omit for the initial (most-recent) page. before: Option, - /// Page size (default 100, capped at HISTORY_CAPACITY). + /// Page size (default 100, capped at `HISTORY_CAPACITY`). limit: Option, } @@ -1094,7 +1094,7 @@ fn available_models() -> Vec { const DEFAULT: &[&str] = &["haiku", "sonnet", "opus"]; let raw = match std::env::var("HIVE_AVAILABLE_MODELS") { Ok(v) if !v.trim().is_empty() => v, - _ => return DEFAULT.iter().map(|s| s.to_string()).collect(), + _ => return DEFAULT.iter().map(ToString::to_string).collect(), }; let models: Vec = raw .split(',') @@ -1102,7 +1102,7 @@ fn available_models() -> Vec { .filter(|s| !s.is_empty()) .collect(); if models.is_empty() { - DEFAULT.iter().map(|s| s.to_string()).collect() + DEFAULT.iter().map(ToString::to_string).collect() } else { models } diff --git a/hive-forge/src/verbs/comments.rs b/hive-forge/src/verbs/comments.rs index 73c1a0bc..a294260f 100644 --- a/hive-forge/src/verbs/comments.rs +++ b/hive-forge/src/verbs/comments.rs @@ -26,7 +26,7 @@ use crate::verbs::print_json; /// Forgejo's per-page comment cap. The API caps `limit` at 50 even /// if a higher value is requested; pin it explicitly so the math /// downstream doesn't depend on a hidden default. -const PAGE_SIZE: u64 = 50; +const PAGE_SIZE: usize = 50; #[derive(ClapArgs)] pub struct Args { @@ -112,7 +112,7 @@ fn fetch_tail(client: &Client, repo: &str, number: u64, n: usize) -> Result (usize, usize) { let n = n.min(total); - let page_size = PAGE_SIZE as usize; + let page_size = PAGE_SIZE; let start_idx = total - n; let start_page = (start_idx / page_size) + 1; let last_page = (total - 1) / page_size + 1; diff --git a/hive-forge/src/verbs/pr_reviews.rs b/hive-forge/src/verbs/pr_reviews.rs index 6ee76b02..a30aa9be 100644 --- a/hive-forge/src/verbs/pr_reviews.rs +++ b/hive-forge/src/verbs/pr_reviews.rs @@ -17,7 +17,7 @@ pub struct Args { #[arg(long, conflicts_with_all = ["request_changes", "comment"])] approve: bool, - /// Request changes on the PR (submit a REQUEST_CHANGES review). + /// Request changes on the PR (submit a `REQUEST_CHANGES` review). #[arg(long, conflicts_with_all = ["approve", "comment"])] request_changes: bool, @@ -52,7 +52,7 @@ pub fn run(client: &Client, args: Args) -> Result<()> { } } -/// Submit a review event (APPROVED / REQUEST_CHANGES / COMMENT) and print +/// Submit a review event (`APPROVED` / `REQUEST_CHANGES` / `COMMENT`) and print /// a compact summary of the created review. fn submit_review(client: &Client, number: u64, event: &str, body: Option) -> Result<()> { let repo = client.repo(); @@ -89,7 +89,8 @@ fn list_reviews(client: &Client, number: u64) -> Result<()> { if client.json_mode() { list_reviews_json(client, repo, number, &reviews) } else { - list_reviews_text(client, repo, number, &reviews) + list_reviews_text(client, repo, number, &reviews); + Ok(()) } } @@ -129,10 +130,10 @@ fn list_reviews_json(client: &Client, repo: &str, number: u64, reviews: &[Value] /// Human-readable output: Markdown-style heading per review, inline /// comments as `[path:line] body` (line omitted for PR-level comments). -fn list_reviews_text(client: &Client, repo: &str, number: u64, reviews: &[Value]) -> Result<()> { +fn list_reviews_text(client: &Client, repo: &str, number: u64, reviews: &[Value]) { if reviews.is_empty() { println!("(no reviews)"); - return Ok(()); + return; } for r in reviews { let id = r.get("id").and_then(Value::as_u64).unwrap_or(0); @@ -160,5 +161,4 @@ fn list_reviews_text(client: &Client, repo: &str, number: u64, reviews: &[Value] } println!(); } - Ok(()) } diff --git a/hive-matrix-mcp/src/handlers.rs b/hive-matrix-mcp/src/handlers.rs index 353c9053..219dd097 100644 --- a/hive-matrix-mcp/src/handlers.rs +++ b/hive-matrix-mcp/src/handlers.rs @@ -275,7 +275,7 @@ pub async fn list_room_members(client: &Client, room_ref: &str) -> DaemonRespons DaemonResponse::ok(&list) } -pub async fn list_invites(client: &Client) -> DaemonResponse { +pub fn list_invites(client: &Client) -> DaemonResponse { let invites: Vec = client .invited_rooms() .into_iter() @@ -422,11 +422,14 @@ pub async fn read_room(client: &Client, room_ref: &str, limit: Option) -> /// [`collect_unread`] issues for count==1 rooms. #[must_use] pub fn unread_count(client: &Client) -> DaemonResponse { - let rooms = client - .joined_rooms() - .into_iter() - .filter(|r| r.unread_notification_counts().notification_count > 0) - .count() as u32; + let rooms = u32::try_from( + client + .joined_rooms() + .into_iter() + .filter(|r| r.unread_notification_counts().notification_count > 0) + .count(), + ) + .unwrap_or(u32::MAX); DaemonResponse::ok(&serde_json::json!({ "rooms": rooms })) } @@ -447,14 +450,14 @@ pub async fn collect_unread(client: &Client) -> Vec use crate::protocol::RoomUnread; let mut result = Vec::new(); for room in client.joined_rooms() { - let count = room.unread_notification_counts().notification_count as u32; + let count = u32::try_from(room.unread_notification_counts().notification_count) + .unwrap_or(u32::MAX); if count == 0 { continue; } let label = room .canonical_alias() - .map(|a| a.to_string()) - .unwrap_or_else(|| room.room_id().to_string()); + .map_or_else(|| room.room_id().to_string(), |a| a.to_string()); let (last_body, last_sender) = if count == 1 { fetch_last_message(client, &room).await } else { @@ -482,9 +485,8 @@ async fn fetch_last_message( let mut req = get_message_events::v3::Request::new(room.room_id().to_owned(), Direction::Backward); req.limit = matrix_sdk::ruma::UInt::from(1u32); - let resp = match client.send(req).await { - Ok(r) => r, - Err(_) => return (None, None), + let Ok(resp) = client.send(req).await else { + return (None, None); }; for raw in &resp.chunk { if let Ok(ev) = raw.deserialize() { diff --git a/hive-matrix-mcp/src/main.rs b/hive-matrix-mcp/src/main.rs index 24b551e8..ff0f749e 100644 --- a/hive-matrix-mcp/src/main.rs +++ b/hive-matrix-mcp/src/main.rs @@ -15,7 +15,7 @@ //! synced with hive-c0re's token-provisioning timing. //! //! Stale-token recovery: handled in `client::build_and_restore` — see -//! that module for the M_UNKNOWN_TOKEN detection + cleanup flow. +//! that module for the `M_UNKNOWN_TOKEN` detection + cleanup flow. use anyhow::{Context, Result}; use matrix_sdk::config::SyncSettings; diff --git a/hive-matrix-mcp/src/paths.rs b/hive-matrix-mcp/src/paths.rs index a40c4704..9838aff5 100644 --- a/hive-matrix-mcp/src/paths.rs +++ b/hive-matrix-mcp/src/paths.rs @@ -83,8 +83,7 @@ pub fn mcp_loose_ends_dir() -> PathBuf { let state_path = PathBuf::from(&state); state_path .parent() - .map(|p| p.join("harness")) - .unwrap_or_else(|| PathBuf::from(state)) + .map_or_else(|| PathBuf::from(state), |p| p.join("harness")) }; base.join("mcp-loose-ends") } diff --git a/hive-matrix-mcp/src/socket.rs b/hive-matrix-mcp/src/socket.rs index f3b450b2..168ceba9 100644 --- a/hive-matrix-mcp/src/socket.rs +++ b/hive-matrix-mcp/src/socket.rs @@ -79,7 +79,7 @@ async fn dispatch(req: DaemonRequest, client: &Client) -> DaemonResponse { handlers::mark_read(client, &room, &event_id).await } DaemonRequest::ListRooms => handlers::list_rooms(client).await, - DaemonRequest::ListInvites => handlers::list_invites(client).await, + DaemonRequest::ListInvites => handlers::list_invites(client), DaemonRequest::JoinRoom { room } => handlers::join_room(client, &room).await, DaemonRequest::InviteUser { room, user_id } => { handlers::invite_user(client, &room, &user_id).await diff --git a/hive-matrix-mcp/src/wake.rs b/hive-matrix-mcp/src/wake.rs index 3bc66975..1c4bfff8 100644 --- a/hive-matrix-mcp/src/wake.rs +++ b/hive-matrix-mcp/src/wake.rs @@ -58,8 +58,8 @@ pub async fn send_wake(socket: &Path, body: impl AsRef) -> Result<()> { // Drain the response line so the server doesn't get ECONNRESET on // its write-back. We don't act on the response — best-effort wake. let mut reader = tokio::io::BufReader::new(read); - let mut _resp = String::new(); - let _ = reader.read_line(&mut _resp).await; + let mut resp = String::new(); + let _ = reader.read_line(&mut resp).await; Ok(()) } @@ -86,13 +86,13 @@ pub fn format_unread_summary(rooms: &[crate::protocol::RoomUnread]) -> String { // Terse path: exactly one room, exactly one unread with body. if rooms.len() == 1 { let r = &rooms[0]; - if r.count == 1 { - if let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) { - return format!( - "[matrix] {sender} in {label}: {body} — use read_room to view, mark_read to clear", - label = r.label - ); - } + if r.count == 1 + && let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) + { + return format!( + "[matrix] {sender} in {label}: {body} — use read_room to view, mark_read to clear", + label = r.label + ); } return format!( "[matrix] {} unread in {} — use read_room to view, mark_read to clear", @@ -102,11 +102,11 @@ pub fn format_unread_summary(rooms: &[crate::protocol::RoomUnread]) -> String { // Multi-room path. let mut out = String::from("[matrix] unread messages:"); for r in rooms { - if r.count == 1 { - if let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) { - let _ = write!(out, "\n- {}: {sender}: {body}", r.label); - continue; - } + if r.count == 1 + && let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) + { + let _ = write!(out, "\n- {}: {sender}: {body}", r.label); + continue; } let _ = write!(out, "\n- {}: {} unread", r.label, r.count); } diff --git a/hive-sh4re/src/priv_proto.rs b/hive-sh4re/src/priv_proto.rs index acf8961d..631225e8 100644 --- a/hive-sh4re/src/priv_proto.rs +++ b/hive-sh4re/src/priv_proto.rs @@ -165,8 +165,8 @@ pub enum PrivRequest { WriteNspawnFlags { container: String, binds: Vec, - /// `None` = host netns (PRIVATE_NETWORK=0). `Some` = private netns with - /// veth on the specified bridge (PRIVATE_NETWORK=1). + /// `None` = host netns (`PRIVATE_NETWORK=0`). `Some` = private netns with + /// veth on the specified bridge (`PRIVATE_NETWORK=1`). #[serde(default)] isolation: Option, },