Skip to main content

pitchfork_cli/
daemon_status.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, strum::Display, strum::EnumIs)]
4#[strum(serialize_all = "snake_case")]
5#[serde(rename_all = "snake_case")]
6#[derive(Default)]
7pub enum DaemonStatus {
8    Failed(String),
9    Waiting,
10    Running,
11    Stopping,
12    /// Exit code of the process, or -1 if unknown.
13    Errored(i32),
14    #[default]
15    Stopped,
16}
17
18impl DaemonStatus {
19    pub fn style(&self) -> String {
20        let s = self.to_string();
21        match self {
22            DaemonStatus::Failed(_) => console::style(s).red().to_string(),
23            DaemonStatus::Waiting => console::style(s).yellow().to_string(),
24            DaemonStatus::Running => console::style(s).green().to_string(),
25            DaemonStatus::Stopping => console::style(s).yellow().to_string(),
26            DaemonStatus::Stopped => console::style(s).dim().to_string(),
27            DaemonStatus::Errored(_) => console::style(s).red().to_string(),
28        }
29    }
30
31    pub fn error_message(&self) -> Option<String> {
32        match self {
33            DaemonStatus::Failed(msg) => Some(msg.clone()),
34            DaemonStatus::Errored(code) if *code != -1 => Some(format!("exit code {code}")),
35            DaemonStatus::Errored(_) => Some("unknown exit code".to_string()),
36            _ => None,
37        }
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    fn all_variants() -> Vec<(&'static str, DaemonStatus)> {
46        vec![
47            ("running", DaemonStatus::Running),
48            ("stopped", DaemonStatus::Stopped),
49            ("waiting", DaemonStatus::Waiting),
50            ("stopping", DaemonStatus::Stopping),
51            ("failed", DaemonStatus::Failed("some error".to_string())),
52            ("errored", DaemonStatus::Errored(1)),
53            ("errored_unknown", DaemonStatus::Errored(-1)),
54        ]
55    }
56
57    #[test]
58    fn test_daemon_status_json_roundtrip() {
59        for (name, status) in all_variants() {
60            let json_str = serde_json::to_string(&status)
61                .unwrap_or_else(|_| panic!("Failed to serialize {name}"));
62            let result: Result<DaemonStatus, _> = serde_json::from_str(&json_str);
63            assert!(
64                result.is_ok(),
65                "Failed to deserialize {name}: {:?}",
66                result.err()
67            );
68        }
69    }
70
71    #[test]
72    fn test_daemon_status_toml_roundtrip() {
73        #[derive(Serialize, Deserialize, Debug)]
74        struct Wrapper {
75            status: DaemonStatus,
76        }
77
78        for (name, status) in all_variants() {
79            let w = Wrapper { status };
80            let toml_str =
81                toml::to_string(&w).unwrap_or_else(|e| panic!("Failed to serialize {name}: {e}"));
82            let result: Result<Wrapper, _> = toml::from_str(&toml_str);
83            assert!(
84                result.is_ok(),
85                "Failed to deserialize {name}: {:?}\nTOML was: {toml_str:?}",
86                result.err()
87            );
88        }
89    }
90}