pitchfork_cli/
daemon_status.rs1use 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 Restarting,
20 Errored(i32),
22 Completed,
34 #[default]
35 Stopped,
36}
37
38impl DaemonStatus {
39 pub fn style(&self) -> String {
40 let s = self.to_string();
41 match self {
42 DaemonStatus::Failed(_) => console::style(s).red().to_string(),
43 DaemonStatus::Waiting => console::style(s).yellow().to_string(),
44 DaemonStatus::Running => console::style(s).green().to_string(),
45 DaemonStatus::Stopping => console::style(s).yellow().to_string(),
46 DaemonStatus::Restarting => console::style(s).yellow().to_string(),
47 DaemonStatus::Stopped => console::style(s).dim().to_string(),
48 DaemonStatus::Completed => console::style(s).green().dim().to_string(),
49 DaemonStatus::Errored(_) => console::style(s).red().to_string(),
50 }
51 }
52
53 pub fn is_stopping_or_restarting(&self) -> bool {
56 self.is_stopping() || self.is_restarting()
57 }
58
59 pub fn error_message(&self) -> Option<String> {
60 match self {
61 DaemonStatus::Failed(msg) => Some(msg.clone()),
62 DaemonStatus::Errored(code) if *code != -1 => Some(format!("exit code {code}")),
63 DaemonStatus::Errored(_) => Some("unknown exit code".to_string()),
64 _ => None,
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 fn all_variants() -> Vec<(&'static str, DaemonStatus)> {
74 vec![
75 ("running", DaemonStatus::Running),
76 ("stopped", DaemonStatus::Stopped),
77 ("waiting", DaemonStatus::Waiting),
78 ("stopping", DaemonStatus::Stopping),
79 ("restarting", DaemonStatus::Restarting),
80 ("failed", DaemonStatus::Failed("some error".to_string())),
81 ("errored", DaemonStatus::Errored(1)),
82 ("errored_unknown", DaemonStatus::Errored(-1)),
83 ("completed", DaemonStatus::Completed),
84 ]
85 }
86
87 #[test]
88 fn test_completed_serializes_as_completed() {
89 assert_eq!(DaemonStatus::Completed.to_string(), "completed");
92 assert_eq!(
93 serde_json::to_string(&DaemonStatus::Completed).unwrap(),
94 "\"completed\""
95 );
96 }
97
98 #[test]
99 fn test_restarting_serializes_as_restarting() {
100 assert_eq!(DaemonStatus::Restarting.to_string(), "restarting");
101 assert_eq!(
102 serde_json::to_string(&DaemonStatus::Restarting).unwrap(),
103 "\"restarting\""
104 );
105 assert!(DaemonStatus::Restarting.error_message().is_none());
106 assert!(DaemonStatus::Restarting.is_stopping_or_restarting());
107 assert!(!DaemonStatus::Running.is_stopping_or_restarting());
108 }
109
110 #[test]
111 fn test_completed_has_no_error_message() {
112 assert!(DaemonStatus::Completed.error_message().is_none());
113 }
114
115 #[test]
116 fn test_daemon_status_json_roundtrip() {
117 for (name, status) in all_variants() {
118 let json_str = serde_json::to_string(&status)
119 .unwrap_or_else(|_| panic!("Failed to serialize {name}"));
120 let result: Result<DaemonStatus, _> = serde_json::from_str(&json_str);
121 assert!(
122 result.is_ok(),
123 "Failed to deserialize {name}: {:?}",
124 result.err()
125 );
126 }
127 }
128
129 #[test]
130 fn test_daemon_status_toml_roundtrip() {
131 #[derive(Serialize, Deserialize, Debug)]
132 struct Wrapper {
133 status: DaemonStatus,
134 }
135
136 for (name, status) in all_variants() {
137 let w = Wrapper { status };
138 let toml_str =
139 toml::to_string(&w).unwrap_or_else(|e| panic!("Failed to serialize {name}: {e}"));
140 let result: Result<Wrapper, _> = toml::from_str(&toml_str);
141 assert!(
142 result.is_ok(),
143 "Failed to deserialize {name}: {:?}\nTOML was: {toml_str:?}",
144 result.err()
145 );
146 }
147 }
148}