nixfleet_proto/
host_rollout_state.rs1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct HostRolloutStateParseError {
12 pub got: String,
13}
14
15impl std::fmt::Display for HostRolloutStateParseError {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "unknown host_rollout_state: {:?}", self.got)
18 }
19}
20
21impl std::error::Error for HostRolloutStateParseError {}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub enum HostRolloutState {
28 Pending,
29 Activating,
30 Deferred,
31 Soaking,
32 Converged,
33 Failed,
34 Reverted,
35}
36
37impl HostRolloutState {
38 pub fn as_db_str(&self) -> &'static str {
41 match self {
42 HostRolloutState::Pending => "Pending",
43 HostRolloutState::Activating => "Activating",
44 HostRolloutState::Deferred => "Deferred",
45 HostRolloutState::Soaking => "Soaking",
46 HostRolloutState::Converged => "Converged",
47 HostRolloutState::Failed => "Failed",
48 HostRolloutState::Reverted => "Reverted",
49 }
50 }
51
52 pub fn from_db_str(s: &str) -> Result<Self, HostRolloutStateParseError> {
53 match s {
54 "Pending" => Ok(HostRolloutState::Pending),
55 "Activating" => Ok(HostRolloutState::Activating),
56 "Deferred" => Ok(HostRolloutState::Deferred),
57 "Soaking" => Ok(HostRolloutState::Soaking),
58 "Converged" => Ok(HostRolloutState::Converged),
59 "Failed" => Ok(HostRolloutState::Failed),
60 "Reverted" => Ok(HostRolloutState::Reverted),
61 other => Err(HostRolloutStateParseError {
62 got: other.to_string(),
63 }),
64 }
65 }
66
67 pub fn is_terminal_for_ordering(&self) -> bool {
75 matches!(self, Self::Converged | Self::Deferred)
76 }
77
78 pub fn is_in_flight(&self) -> bool {
83 matches!(self, Self::Pending | Self::Activating | Self::Soaking)
84 }
85
86 pub fn is_failed(&self) -> bool {
88 matches!(self, Self::Failed | Self::Reverted)
89 }
90}
91
92#[cfg(feature = "rusqlite")]
93mod rusqlite_impls {
94 use super::*;
95 use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
96
97 impl ToSql for HostRolloutState {
98 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
99 Ok(ToSqlOutput::Borrowed(self.as_db_str().into()))
100 }
101 }
102
103 impl FromSql for HostRolloutState {
104 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
105 let s = value.as_str()?;
106 Self::from_db_str(s).map_err(|e| FromSqlError::Other(Box::new(e)))
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn round_trip_known_values() {
117 for v in [
118 HostRolloutState::Pending,
119 HostRolloutState::Activating,
120 HostRolloutState::Deferred,
121 HostRolloutState::Soaking,
122 HostRolloutState::Converged,
123 HostRolloutState::Failed,
124 HostRolloutState::Reverted,
125 ] {
126 assert_eq!(HostRolloutState::from_db_str(v.as_db_str()).unwrap(), v);
127 }
128 }
129
130 #[test]
131 fn legacy_variants_no_longer_parse() {
132 for legacy in ["Queued", "Dispatched", "ConfirmWindow", "Healthy", "Soaked"] {
133 assert!(
134 HostRolloutState::from_db_str(legacy).is_err(),
135 "v0.1 variant {legacy} must not parse against v0.2 wire shape",
136 );
137 }
138 }
139
140 #[test]
141 fn unknown_strings_error() {
142 assert!(HostRolloutState::from_db_str("").is_err());
143 assert!(HostRolloutState::from_db_str("pending").is_err());
144 assert!(HostRolloutState::from_db_str("Pendng").is_err());
145 }
146}