1use crate::api::machine::{
9 reset_request::WipeMode as ProtoWipeMode, Reset as ProtoReset,
10 ResetPartitionSpec as ProtoPartitionSpec, ResetRequest as ProtoRequest,
11 ResetResponse as ProtoResponse,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum WipeMode {
17 #[default]
19 All,
20 SystemDisk,
22 UserDisks,
24}
25
26impl From<WipeMode> for i32 {
27 fn from(mode: WipeMode) -> Self {
28 match mode {
29 WipeMode::All => ProtoWipeMode::All as i32,
30 WipeMode::SystemDisk => ProtoWipeMode::SystemDisk as i32,
31 WipeMode::UserDisks => ProtoWipeMode::UserDisks as i32,
32 }
33 }
34}
35
36impl From<i32> for WipeMode {
37 fn from(value: i32) -> Self {
38 match value {
39 0 => WipeMode::All,
40 1 => WipeMode::SystemDisk,
41 2 => WipeMode::UserDisks,
42 _ => WipeMode::All,
43 }
44 }
45}
46
47impl std::fmt::Display for WipeMode {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 WipeMode::All => write!(f, "all"),
51 WipeMode::SystemDisk => write!(f, "system-disk"),
52 WipeMode::UserDisks => write!(f, "user-disks"),
53 }
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ResetPartitionSpec {
60 pub label: String,
62 pub wipe: bool,
64}
65
66impl ResetPartitionSpec {
67 #[must_use]
69 pub fn new(label: impl Into<String>, wipe: bool) -> Self {
70 Self {
71 label: label.into(),
72 wipe,
73 }
74 }
75
76 #[must_use]
78 pub fn wipe(label: impl Into<String>) -> Self {
79 Self::new(label, true)
80 }
81}
82
83impl From<ResetPartitionSpec> for ProtoPartitionSpec {
84 fn from(spec: ResetPartitionSpec) -> Self {
85 ProtoPartitionSpec {
86 label: spec.label,
87 wipe: spec.wipe,
88 }
89 }
90}
91
92#[derive(Debug, Clone, Default)]
110pub struct ResetRequest {
111 pub graceful: bool,
113 pub reboot: bool,
115 pub system_partitions_to_wipe: Vec<ResetPartitionSpec>,
117 pub user_disks_to_wipe: Vec<String>,
119 pub mode: WipeMode,
121}
122
123impl ResetRequest {
124 #[must_use]
126 pub fn builder() -> ResetRequestBuilder {
127 ResetRequestBuilder::default()
128 }
129
130 #[must_use]
137 pub fn graceful() -> Self {
138 Self {
139 graceful: true,
140 reboot: true,
141 mode: WipeMode::All,
142 ..Default::default()
143 }
144 }
145
146 #[must_use]
153 pub fn force() -> Self {
154 Self {
155 graceful: false,
156 reboot: true,
157 mode: WipeMode::All,
158 ..Default::default()
159 }
160 }
161
162 #[must_use]
164 pub fn halt() -> Self {
165 Self {
166 graceful: true,
167 reboot: false,
168 mode: WipeMode::All,
169 ..Default::default()
170 }
171 }
172}
173
174impl From<ResetRequest> for ProtoRequest {
175 fn from(req: ResetRequest) -> Self {
176 ProtoRequest {
177 graceful: req.graceful,
178 reboot: req.reboot,
179 system_partitions_to_wipe: req
180 .system_partitions_to_wipe
181 .into_iter()
182 .map(Into::into)
183 .collect(),
184 user_disks_to_wipe: req.user_disks_to_wipe,
185 mode: req.mode.into(),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Default)]
192pub struct ResetRequestBuilder {
193 graceful: bool,
194 reboot: bool,
195 system_partitions_to_wipe: Vec<ResetPartitionSpec>,
196 user_disks_to_wipe: Vec<String>,
197 mode: WipeMode,
198}
199
200impl ResetRequestBuilder {
201 #[must_use]
203 pub fn graceful(mut self, graceful: bool) -> Self {
204 self.graceful = graceful;
205 self
206 }
207
208 #[must_use]
210 pub fn reboot(mut self, reboot: bool) -> Self {
211 self.reboot = reboot;
212 self
213 }
214
215 #[must_use]
217 pub fn wipe_mode(mut self, mode: WipeMode) -> Self {
218 self.mode = mode;
219 self
220 }
221
222 #[must_use]
224 pub fn wipe_partition(mut self, spec: ResetPartitionSpec) -> Self {
225 self.system_partitions_to_wipe.push(spec);
226 self
227 }
228
229 #[must_use]
231 pub fn wipe_user_disk(mut self, disk: impl Into<String>) -> Self {
232 self.user_disks_to_wipe.push(disk.into());
233 self
234 }
235
236 #[must_use]
238 pub fn build(self) -> ResetRequest {
239 ResetRequest {
240 graceful: self.graceful,
241 reboot: self.reboot,
242 system_partitions_to_wipe: self.system_partitions_to_wipe,
243 user_disks_to_wipe: self.user_disks_to_wipe,
244 mode: self.mode,
245 }
246 }
247}
248
249#[derive(Debug, Clone)]
251pub struct ResetResult {
252 pub node: Option<String>,
254 pub actor_id: String,
256}
257
258impl From<ProtoReset> for ResetResult {
259 fn from(proto: ProtoReset) -> Self {
260 Self {
261 node: proto.metadata.map(|m| m.hostname),
262 actor_id: proto.actor_id,
263 }
264 }
265}
266
267#[derive(Debug, Clone)]
269pub struct ResetResponse {
270 pub results: Vec<ResetResult>,
272}
273
274impl From<ProtoResponse> for ResetResponse {
275 fn from(proto: ProtoResponse) -> Self {
276 Self {
277 results: proto.messages.into_iter().map(Into::into).collect(),
278 }
279 }
280}
281
282impl ResetResponse {
283 #[must_use]
285 pub fn is_success(&self) -> bool {
286 !self.results.is_empty()
287 }
288
289 #[must_use]
291 pub fn first(&self) -> Option<&ResetResult> {
292 self.results.first()
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_wipe_mode_conversion() {
302 assert_eq!(i32::from(WipeMode::All), 0);
303 assert_eq!(i32::from(WipeMode::SystemDisk), 1);
304 assert_eq!(i32::from(WipeMode::UserDisks), 2);
305
306 assert_eq!(WipeMode::from(0), WipeMode::All);
307 assert_eq!(WipeMode::from(1), WipeMode::SystemDisk);
308 assert_eq!(WipeMode::from(2), WipeMode::UserDisks);
309 }
310
311 #[test]
312 fn test_wipe_mode_display() {
313 assert_eq!(WipeMode::All.to_string(), "all");
314 assert_eq!(WipeMode::SystemDisk.to_string(), "system-disk");
315 assert_eq!(WipeMode::UserDisks.to_string(), "user-disks");
316 }
317
318 #[test]
319 fn test_reset_request_graceful() {
320 let request = ResetRequest::graceful();
321 assert!(request.graceful);
322 assert!(request.reboot);
323 assert_eq!(request.mode, WipeMode::All);
324 }
325
326 #[test]
327 fn test_reset_request_force() {
328 let request = ResetRequest::force();
329 assert!(!request.graceful);
330 assert!(request.reboot);
331 assert_eq!(request.mode, WipeMode::All);
332 }
333
334 #[test]
335 fn test_reset_request_halt() {
336 let request = ResetRequest::halt();
337 assert!(request.graceful);
338 assert!(!request.reboot);
339 }
340
341 #[test]
342 fn test_reset_request_builder() {
343 let request = ResetRequest::builder()
344 .graceful(true)
345 .reboot(false)
346 .wipe_mode(WipeMode::SystemDisk)
347 .wipe_partition(ResetPartitionSpec::wipe("STATE"))
348 .wipe_user_disk("/dev/sdb")
349 .build();
350
351 assert!(request.graceful);
352 assert!(!request.reboot);
353 assert_eq!(request.mode, WipeMode::SystemDisk);
354 assert_eq!(request.system_partitions_to_wipe.len(), 1);
355 assert_eq!(request.user_disks_to_wipe.len(), 1);
356 }
357
358 #[test]
359 fn test_proto_conversion() {
360 let request = ResetRequest::builder()
361 .graceful(true)
362 .reboot(true)
363 .wipe_mode(WipeMode::UserDisks)
364 .build();
365
366 let proto: ProtoRequest = request.into();
367 assert!(proto.graceful);
368 assert!(proto.reboot);
369 assert_eq!(proto.mode, ProtoWipeMode::UserDisks as i32);
370 }
371
372 #[test]
373 fn test_partition_spec() {
374 let spec = ResetPartitionSpec::wipe("STATE");
375 assert_eq!(spec.label, "STATE");
376 assert!(spec.wipe);
377
378 let spec2 = ResetPartitionSpec::new("EPHEMERAL", false);
379 assert_eq!(spec2.label, "EPHEMERAL");
380 assert!(!spec2.wipe);
381 }
382
383 #[test]
384 fn test_reset_response_is_success() {
385 let response = ResetResponse {
386 results: vec![ResetResult {
387 node: Some("node1".to_string()),
388 actor_id: "actor-123".to_string(),
389 }],
390 };
391 assert!(response.is_success());
392
393 let empty = ResetResponse { results: vec![] };
394 assert!(!empty.is_success());
395 }
396}