Skip to main content

verbs/
daemon_plan.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Pure mount-daemon status/stop message assembly (no endpoint I/O).
3//!
4//! Owns human lines and status tokens for `heddle daemon status|stop` that
5//! can be decided from RPC/list facts alone. Endpoint load, RPC, PID wait,
6//! and RecoveryAdvice stay CLI-owned.
7
8/// JSON/status token when the daemon answered Health.
9pub fn daemon_running_status_token() -> &'static str {
10    "running"
11}
12
13/// JSON/status token when no live endpoint answered.
14pub fn daemon_not_running_status_token() -> &'static str {
15    "not_running"
16}
17
18/// JSON status after a successful stop.
19pub fn daemon_stopped_status_token() -> &'static str {
20    "stopped"
21}
22
23/// Human line when status finds a live daemon.
24pub fn daemon_status_running_line(
25    ok: bool,
26    version: u32,
27    uptime_s: u64,
28    mount_count: usize,
29    materialized_count: usize,
30) -> String {
31    format!(
32        "daemon: ok={ok} version={version} uptime_s={uptime_s} mount_count={mount_count} materialized_count={materialized_count}"
33    )
34}
35
36/// Human line when status finds no live endpoint.
37pub fn daemon_status_not_running_line(endpoint_path: &str, materialized_count: usize) -> String {
38    format!(
39        "daemon: not running (no live endpoint at {endpoint_path}) materialized_count={materialized_count}"
40    )
41}
42
43/// Human line after stop when the daemon was not running.
44pub fn daemon_stop_not_running_line() -> &'static str {
45    "daemon: not running"
46}
47
48/// Human line after a successful stop.
49pub fn daemon_stop_stopped_line() -> &'static str {
50    "daemon: stopped"
51}
52
53/// Header for the materialized-thread inventory section.
54pub fn daemon_materialized_threads_header() -> &'static str {
55    "materialized threads:"
56}
57
58/// One indented materialized-thread inventory line.
59pub fn daemon_materialized_thread_line(
60    thread: &str,
61    state: &str,
62    files: usize,
63    tree_short: &str,
64) -> String {
65    format!("  {thread} (state={state}, files={files}, tree={tree_short})")
66}
67
68/// First 12 characters of a tree / object hash string for status display.
69pub fn daemon_short_tree(tree: &str) -> &str {
70    &tree[..tree.len().min(12)]
71}
72
73/// Stable recovery-advice kind tokens for daemon response failures.
74pub fn daemon_health_failed_kind() -> &'static str {
75    "daemon_health_failed"
76}
77
78/// Kind when health/stop received an unexpected response variant.
79pub fn daemon_unexpected_response_kind() -> &'static str {
80    "daemon_unexpected_response"
81}
82
83/// Kind when shutdown was refused by the daemon.
84pub fn daemon_shutdown_refused_kind() -> &'static str {
85    "daemon_shutdown_refused"
86}
87
88/// Status token for stop JSON from whether a live daemon was contacted.
89pub fn daemon_stop_status_token(was_running: bool) -> &'static str {
90    if was_running {
91        daemon_stopped_status_token()
92    } else {
93        daemon_not_running_status_token()
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn status_tokens_and_lines() {
103        assert_eq!(daemon_running_status_token(), "running");
104        assert_eq!(daemon_not_running_status_token(), "not_running");
105        assert_eq!(daemon_stopped_status_token(), "stopped");
106        assert_eq!(daemon_stop_status_token(true), "stopped");
107        assert_eq!(daemon_stop_status_token(false), "not_running");
108
109        let running = daemon_status_running_line(true, 1, 42, 2, 3);
110        assert!(running.contains("ok=true"));
111        assert!(running.contains("uptime_s=42"));
112        assert!(running.contains("materialized_count=3"));
113
114        let idle = daemon_status_not_running_line("/tmp/ep.json", 1);
115        assert!(idle.contains("not running"));
116        assert!(idle.contains("/tmp/ep.json"));
117        assert_eq!(daemon_stop_not_running_line(), "daemon: not running");
118        assert_eq!(daemon_stop_stopped_line(), "daemon: stopped");
119    }
120
121    #[test]
122    fn materialized_line_and_kinds() {
123        assert_eq!(
124            daemon_materialized_thread_line("feat", "abc", 10, "deadbeefcafe"),
125            "  feat (state=abc, files=10, tree=deadbeefcafe)"
126        );
127        assert_eq!(daemon_short_tree("0123456789abcdef"), "0123456789ab");
128        assert_eq!(daemon_health_failed_kind(), "daemon_health_failed");
129        assert_eq!(
130            daemon_unexpected_response_kind(),
131            "daemon_unexpected_response"
132        );
133        assert_eq!(daemon_shutdown_refused_kind(), "daemon_shutdown_refused");
134        assert_eq!(
135            daemon_materialized_threads_header(),
136            "materialized threads:"
137        );
138    }
139}