forge_core/daemon/
traits.rs1use std::future::Future;
2use std::pin::Pin;
3use std::str::FromStr;
4use std::time::Duration;
5
6use crate::error::Result;
7
8use super::context::DaemonContext;
9
10pub trait ForgeDaemon: crate::__sealed::Sealed + Send + Sync + 'static {
12 fn info() -> DaemonInfo;
13
14 fn execute(ctx: &DaemonContext) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
15}
16
17#[derive(Debug, Clone)]
19pub struct DaemonInfo {
20 pub name: &'static str,
21 pub leader_elected: bool,
22 pub restart_on_panic: bool,
23 pub restart_delay: Duration,
24 pub startup_delay: Duration,
25 pub http_timeout: Option<Duration>,
26 pub max_restarts: Option<u32>,
28}
29
30impl Default for DaemonInfo {
31 fn default() -> Self {
32 Self {
33 name: "",
34 leader_elected: true,
35 restart_on_panic: true,
36 restart_delay: Duration::from_secs(5),
37 startup_delay: Duration::from_secs(0),
38 http_timeout: None,
39 max_restarts: None,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[non_exhaustive]
46pub enum DaemonStatus {
47 Pending,
48 Acquiring,
49 Running,
50 Stopped,
51 Failed,
52 Restarting,
53}
54
55impl DaemonStatus {
56 pub fn as_str(&self) -> &'static str {
57 match self {
58 Self::Pending => "pending",
59 Self::Acquiring => "acquiring",
60 Self::Running => "running",
61 Self::Stopped => "stopped",
62 Self::Failed => "failed",
63 Self::Restarting => "restarting",
64 }
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct ParseDaemonStatusError(pub String);
70
71impl std::fmt::Display for ParseDaemonStatusError {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 write!(f, "unknown daemon status: {:?}", self.0)
74 }
75}
76
77impl std::error::Error for ParseDaemonStatusError {}
78
79impl FromStr for DaemonStatus {
80 type Err = ParseDaemonStatusError;
81
82 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
83 match s {
84 "pending" => Ok(Self::Pending),
85 "acquiring" => Ok(Self::Acquiring),
86 "running" => Ok(Self::Running),
87 "stopped" => Ok(Self::Stopped),
88 "failed" => Ok(Self::Failed),
89 "restarting" => Ok(Self::Restarting),
90 _ => Err(ParseDaemonStatusError(s.to_string())),
91 }
92 }
93}
94
95#[cfg(test)]
96#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_default_daemon_info() {
102 let info = DaemonInfo::default();
103 assert!(info.leader_elected);
104 assert!(info.restart_on_panic);
105 assert_eq!(info.restart_delay, Duration::from_secs(5));
106 assert_eq!(info.startup_delay, Duration::from_secs(0));
107 assert_eq!(info.http_timeout, None);
108 assert!(info.max_restarts.is_none());
109 }
110
111 #[test]
112 fn test_status_conversion() {
113 assert_eq!(DaemonStatus::Running.as_str(), "running");
114 assert_eq!("running".parse::<DaemonStatus>(), Ok(DaemonStatus::Running));
115 assert_eq!(DaemonStatus::Failed.as_str(), "failed");
116 assert_eq!("failed".parse::<DaemonStatus>(), Ok(DaemonStatus::Failed));
117 }
118}