restart_manager/
shutdown.rs1use crate::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
7pub struct ShutdownOptions {
8 force_if_hung: bool,
9 require_restart_registration: bool,
10}
11
12impl ShutdownOptions {
13 #[must_use]
15 pub const fn new() -> Self {
16 Self {
17 force_if_hung: false,
18 require_restart_registration: false,
19 }
20 }
21
22 #[must_use]
26 pub const fn with_force_if_hung(mut self, enabled: bool) -> Self {
27 self.force_if_hung = enabled;
28 self
29 }
30
31 #[must_use]
33 pub const fn with_require_restart_registration(mut self, enabled: bool) -> Self {
34 self.require_restart_registration = enabled;
35 self
36 }
37
38 #[must_use]
40 pub const fn force_if_hung(self) -> bool {
41 self.force_if_hung
42 }
43
44 #[must_use]
46 pub const fn require_restart_registration(self) -> bool {
47 self.require_restart_registration
48 }
49
50 pub(crate) const fn native_flags(self) -> u32 {
51 let mut flags = 0;
52 if self.force_if_hung {
53 flags |= 0x01;
54 }
55 if self.require_restart_registration {
56 flags |= 0x10;
57 }
58 flags
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum OperationOutcome {
65 Succeeded,
67 Failed(Error),
69}
70
71impl OperationOutcome {
72 pub(crate) fn from_result(result: Result<()>) -> Self {
73 match result {
74 Ok(()) => Self::Succeeded,
75 Err(error) => Self::Failed(error),
76 }
77 }
78
79 #[must_use]
81 pub const fn is_success(&self) -> bool {
82 matches!(self, Self::Succeeded)
83 }
84
85 #[must_use]
87 pub const fn error(&self) -> Option<&Error> {
88 match self {
89 Self::Succeeded => None,
90 Self::Failed(error) => Some(error),
91 }
92 }
93
94 pub fn into_result(self) -> Result<()> {
96 match self {
97 Self::Succeeded => Ok(()),
98 Self::Failed(error) => Err(error),
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct RecoveryOutcome {
106 pub(crate) shutdown: OperationOutcome,
107 pub(crate) restart: Option<OperationOutcome>,
108}
109
110impl RecoveryOutcome {
111 #[must_use]
113 pub const fn shutdown_outcome(&self) -> &OperationOutcome {
114 &self.shutdown
115 }
116
117 #[must_use]
119 pub const fn restart_outcome(&self) -> Option<&OperationOutcome> {
120 self.restart.as_ref()
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
126pub struct Progress {
127 percent_complete: u8,
128}
129
130impl Progress {
131 pub(crate) fn try_from_native(percent_complete: u32) -> Option<Self> {
132 u8::try_from(percent_complete)
133 .ok()
134 .filter(|percent| *percent <= 100)
135 .map(|percent_complete| Self { percent_complete })
136 }
137
138 #[must_use]
140 pub const fn percent_complete(self) -> u8 {
141 self.percent_complete
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn progress_is_validated_and_ordered() {
151 assert_eq!(
152 Progress::try_from_native(42).unwrap().percent_complete(),
153 42
154 );
155 assert!(Progress::try_from_native(101).is_none());
156 assert!(Progress::try_from_native(10) < Progress::try_from_native(20));
157 }
158
159 #[test]
160 fn shutdown_flags_are_composable() {
161 let options = ShutdownOptions::new()
162 .with_force_if_hung(true)
163 .with_require_restart_registration(true);
164 assert_eq!(options.native_flags(), 0x11);
165 assert!(options.force_if_hung());
166 assert!(options.require_restart_registration());
167 }
168
169 #[test]
170 fn operation_outcomes_preserve_success_and_failure() {
171 let success = OperationOutcome::from_result(Ok(()));
172 assert!(success.is_success());
173 assert!(success.error().is_none());
174 success.into_result().unwrap();
175
176 let error = Error::new(
177 crate::ErrorKind::Cancelled,
178 Some(1223),
179 "cancelled for test",
180 );
181 let failure = OperationOutcome::from_result(Err(error.clone()));
182 assert!(!failure.is_success());
183 assert_eq!(failure.error(), Some(&error));
184 assert_eq!(failure.into_result().unwrap_err(), error);
185 }
186}