1use int_enum::IntEnum;
7use serde_repr::*;
8use strum_macros::EnumIter;
9use strum_macros::EnumString;
10
11pub const SYSTEMD_SERVICE_SUFFIX: &str = ".service";
12pub const SYSTEMD_TIMER_SUFFIX: &str = ".timer";
13
14#[allow(non_camel_case_types)]
16#[derive(
17 Serialize_repr,
18 Deserialize_repr,
19 Clone,
20 Copy,
21 Debug,
22 Default,
23 Eq,
24 PartialEq,
25 EnumIter,
26 EnumString,
27 IntEnum,
28 strum_macros::Display,
29)]
30#[repr(u8)]
31pub enum SystemdUnitActiveState {
32 #[default]
33 unknown = 0,
34 active = 1,
35 reloading = 2,
36 inactive = 3,
37 failed = 4,
38 activating = 5,
39 deactivating = 6,
40}
41
42#[allow(non_camel_case_types)]
44#[derive(
45 Serialize_repr,
46 Deserialize_repr,
47 Clone,
48 Copy,
49 Debug,
50 Default,
51 Eq,
52 PartialEq,
53 EnumIter,
54 EnumString,
55 IntEnum,
56 strum_macros::Display,
57)]
58#[repr(u8)]
59pub enum SystemdUnitLoadState {
60 #[default]
61 unknown = 0,
62 loaded = 1,
63 error = 2,
64 masked = 3,
65 not_found = 4,
66}
67
68pub fn is_unit_unhealthy(
72 active_state: SystemdUnitActiveState,
73 load_state: SystemdUnitLoadState,
74) -> bool {
75 is_unit_unhealthy_for_service(active_state, load_state, false, false)
76}
77
78pub fn is_unit_unhealthy_for_service(
80 active_state: SystemdUnitActiveState,
81 load_state: SystemdUnitLoadState,
82 is_oneshot_service: bool,
83 ignore_inactive_oneshot_services: bool,
84) -> bool {
85 match load_state {
86 SystemdUnitLoadState::loaded => {
88 if ignore_inactive_oneshot_services
89 && is_oneshot_service
90 && matches!(active_state, SystemdUnitActiveState::inactive)
91 {
92 return false;
93 }
94 !matches!(active_state, SystemdUnitActiveState::active)
95 }
96 SystemdUnitLoadState::masked => false,
99 _ => true,
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use std::str::FromStr;
108 use strum::IntoEnumIterator;
109
110 #[test]
111 fn test_is_unit_unhealthy() {
112 assert!(!is_unit_unhealthy(
114 SystemdUnitActiveState::active,
115 SystemdUnitLoadState::loaded
116 ));
117 assert!(is_unit_unhealthy(
119 SystemdUnitActiveState::activating,
120 SystemdUnitLoadState::loaded
121 ));
122 assert!(!is_unit_unhealthy(
124 SystemdUnitActiveState::activating,
125 SystemdUnitLoadState::masked
126 ));
127 assert!(is_unit_unhealthy(
129 SystemdUnitActiveState::deactivating,
130 SystemdUnitLoadState::not_found
131 ));
132 assert!(is_unit_unhealthy(
133 SystemdUnitActiveState::active,
135 SystemdUnitLoadState::error,
136 ));
137 }
138
139 #[test]
140 fn test_is_unit_unhealthy_for_oneshot_service() {
141 assert!(!is_unit_unhealthy_for_service(
142 SystemdUnitActiveState::inactive,
143 SystemdUnitLoadState::loaded,
144 true,
145 true
146 ));
147 assert!(is_unit_unhealthy_for_service(
148 SystemdUnitActiveState::inactive,
149 SystemdUnitLoadState::loaded,
150 true,
151 false
152 ));
153 assert!(is_unit_unhealthy_for_service(
154 SystemdUnitActiveState::failed,
155 SystemdUnitLoadState::loaded,
156 true,
157 true
158 ));
159 assert!(is_unit_unhealthy_for_service(
160 SystemdUnitActiveState::inactive,
161 SystemdUnitLoadState::loaded,
162 false,
163 true
164 ));
165 }
166
167 #[test]
168 fn test_iterators() {
169 assert!(SystemdUnitActiveState::iter().collect::<Vec<_>>().len() > 0);
170 assert!(SystemdUnitLoadState::iter().collect::<Vec<_>>().len() > 0);
171 }
172
173 #[test]
174 fn test_active_state_from_str() {
175 assert_eq!(
176 SystemdUnitActiveState::from_str("active").unwrap(),
177 SystemdUnitActiveState::active
178 );
179 assert_eq!(
180 SystemdUnitActiveState::from_str("reloading").unwrap(),
181 SystemdUnitActiveState::reloading
182 );
183 assert_eq!(
184 SystemdUnitActiveState::from_str("inactive").unwrap(),
185 SystemdUnitActiveState::inactive
186 );
187 assert_eq!(
188 SystemdUnitActiveState::from_str("failed").unwrap(),
189 SystemdUnitActiveState::failed
190 );
191 assert_eq!(
192 SystemdUnitActiveState::from_str("activating").unwrap(),
193 SystemdUnitActiveState::activating
194 );
195 assert_eq!(
196 SystemdUnitActiveState::from_str("deactivating").unwrap(),
197 SystemdUnitActiveState::deactivating
198 );
199 assert_eq!(
200 SystemdUnitActiveState::from_str("unknown").unwrap(),
201 SystemdUnitActiveState::unknown
202 );
203 }
204
205 #[test]
206 fn test_active_state_from_str_invalid() {
207 assert!(SystemdUnitActiveState::from_str("invalid").is_err());
208 assert!(SystemdUnitActiveState::from_str("").is_err());
209 assert!(SystemdUnitActiveState::from_str("ACTIVE").is_err());
210 }
211
212 #[test]
213 fn test_load_state_from_str() {
214 assert_eq!(
215 SystemdUnitLoadState::from_str("loaded").unwrap(),
216 SystemdUnitLoadState::loaded
217 );
218 assert_eq!(
219 SystemdUnitLoadState::from_str("error").unwrap(),
220 SystemdUnitLoadState::error
221 );
222 assert_eq!(
223 SystemdUnitLoadState::from_str("masked").unwrap(),
224 SystemdUnitLoadState::masked
225 );
226 assert_eq!(
227 SystemdUnitLoadState::from_str("not_found").unwrap(),
228 SystemdUnitLoadState::not_found
229 );
230 assert_eq!(
231 SystemdUnitLoadState::from_str("unknown").unwrap(),
232 SystemdUnitLoadState::unknown
233 );
234 }
235
236 #[test]
237 fn test_load_state_from_str_invalid() {
238 assert!(SystemdUnitLoadState::from_str("invalid").is_err());
239 assert!(SystemdUnitLoadState::from_str("").is_err());
240 assert!(SystemdUnitLoadState::from_str("LOADED").is_err());
241 }
242
243 #[test]
244 fn test_active_state_display() {
245 assert_eq!(format!("{}", SystemdUnitActiveState::active), "active");
246 assert_eq!(
247 format!("{}", SystemdUnitActiveState::reloading),
248 "reloading"
249 );
250 assert_eq!(format!("{}", SystemdUnitActiveState::inactive), "inactive");
251 assert_eq!(format!("{}", SystemdUnitActiveState::failed), "failed");
252 assert_eq!(
253 format!("{}", SystemdUnitActiveState::activating),
254 "activating"
255 );
256 assert_eq!(
257 format!("{}", SystemdUnitActiveState::deactivating),
258 "deactivating"
259 );
260 assert_eq!(format!("{}", SystemdUnitActiveState::unknown), "unknown");
261 }
262
263 #[test]
264 fn test_load_state_display() {
265 assert_eq!(format!("{}", SystemdUnitLoadState::loaded), "loaded");
266 assert_eq!(format!("{}", SystemdUnitLoadState::error), "error");
267 assert_eq!(format!("{}", SystemdUnitLoadState::masked), "masked");
268 assert_eq!(format!("{}", SystemdUnitLoadState::not_found), "not_found");
269 assert_eq!(format!("{}", SystemdUnitLoadState::unknown), "unknown");
270 }
271
272 #[test]
273 fn test_active_state_default() {
274 let state: SystemdUnitActiveState = Default::default();
275 assert_eq!(state, SystemdUnitActiveState::unknown);
276 }
277
278 #[test]
279 fn test_load_state_default() {
280 let state: SystemdUnitLoadState = Default::default();
281 assert_eq!(state, SystemdUnitLoadState::unknown);
282 }
283
284 #[test]
285 fn test_active_state_clone() {
286 let state = SystemdUnitActiveState::active;
287 let cloned = state.clone();
288 assert_eq!(state, cloned);
289 }
290
291 #[test]
292 fn test_load_state_clone() {
293 let state = SystemdUnitLoadState::loaded;
294 let cloned = state.clone();
295 assert_eq!(state, cloned);
296 }
297
298 #[test]
299 fn test_active_state_debug() {
300 let state = SystemdUnitActiveState::active;
301 let debug_str = format!("{:?}", state);
302 assert!(debug_str.contains("active"));
303 }
304
305 #[test]
306 fn test_load_state_debug() {
307 let state = SystemdUnitLoadState::loaded;
308 let debug_str = format!("{:?}", state);
309 assert!(debug_str.contains("loaded"));
310 }
311
312 #[test]
313 fn test_active_state_equality() {
314 assert_eq!(
315 SystemdUnitActiveState::active,
316 SystemdUnitActiveState::active
317 );
318 assert_ne!(
319 SystemdUnitActiveState::active,
320 SystemdUnitActiveState::inactive
321 );
322 }
323
324 #[test]
325 fn test_load_state_equality() {
326 assert_eq!(SystemdUnitLoadState::loaded, SystemdUnitLoadState::loaded);
327 assert_ne!(SystemdUnitLoadState::loaded, SystemdUnitLoadState::masked);
328 }
329
330 #[test]
331 fn test_active_state_int_enum() {
332 assert_eq!(SystemdUnitActiveState::unknown as u8, 0);
333 assert_eq!(SystemdUnitActiveState::active as u8, 1);
334 assert_eq!(SystemdUnitActiveState::reloading as u8, 2);
335 assert_eq!(SystemdUnitActiveState::inactive as u8, 3);
336 assert_eq!(SystemdUnitActiveState::failed as u8, 4);
337 assert_eq!(SystemdUnitActiveState::activating as u8, 5);
338 assert_eq!(SystemdUnitActiveState::deactivating as u8, 6);
339 }
340
341 #[test]
342 fn test_load_state_int_enum() {
343 assert_eq!(SystemdUnitLoadState::unknown as u8, 0);
344 assert_eq!(SystemdUnitLoadState::loaded as u8, 1);
345 assert_eq!(SystemdUnitLoadState::error as u8, 2);
346 assert_eq!(SystemdUnitLoadState::masked as u8, 3);
347 assert_eq!(SystemdUnitLoadState::not_found as u8, 4);
348 }
349
350 #[test]
351 fn test_active_state_from_int() {
352 assert_eq!(
353 SystemdUnitActiveState::try_from(0).unwrap(),
354 SystemdUnitActiveState::unknown
355 );
356 assert_eq!(
357 SystemdUnitActiveState::try_from(1).unwrap(),
358 SystemdUnitActiveState::active
359 );
360 assert_eq!(
361 SystemdUnitActiveState::try_from(2).unwrap(),
362 SystemdUnitActiveState::reloading
363 );
364 assert_eq!(
365 SystemdUnitActiveState::try_from(3).unwrap(),
366 SystemdUnitActiveState::inactive
367 );
368 assert_eq!(
369 SystemdUnitActiveState::try_from(4).unwrap(),
370 SystemdUnitActiveState::failed
371 );
372 assert_eq!(
373 SystemdUnitActiveState::try_from(5).unwrap(),
374 SystemdUnitActiveState::activating
375 );
376 assert_eq!(
377 SystemdUnitActiveState::try_from(6).unwrap(),
378 SystemdUnitActiveState::deactivating
379 );
380 }
381
382 #[test]
383 fn test_load_state_from_int() {
384 assert_eq!(
385 SystemdUnitLoadState::try_from(0).unwrap(),
386 SystemdUnitLoadState::unknown
387 );
388 assert_eq!(
389 SystemdUnitLoadState::try_from(1).unwrap(),
390 SystemdUnitLoadState::loaded
391 );
392 assert_eq!(
393 SystemdUnitLoadState::try_from(2).unwrap(),
394 SystemdUnitLoadState::error
395 );
396 assert_eq!(
397 SystemdUnitLoadState::try_from(3).unwrap(),
398 SystemdUnitLoadState::masked
399 );
400 assert_eq!(
401 SystemdUnitLoadState::try_from(4).unwrap(),
402 SystemdUnitLoadState::not_found
403 );
404 }
405
406 #[test]
407 fn test_active_state_from_int_invalid() {
408 assert!(SystemdUnitActiveState::try_from(255).is_err());
409 assert!(SystemdUnitActiveState::try_from(100).is_err());
410 }
411
412 #[test]
413 fn test_load_state_from_int_invalid() {
414 assert!(SystemdUnitLoadState::try_from(255).is_err());
415 assert!(SystemdUnitLoadState::try_from(100).is_err());
416 }
417
418 #[test]
419 fn test_is_unit_unhealthy_all_combinations() {
420 assert!(!is_unit_unhealthy(
422 SystemdUnitActiveState::active,
423 SystemdUnitLoadState::loaded
424 ));
425 assert!(is_unit_unhealthy(
426 SystemdUnitActiveState::reloading,
427 SystemdUnitLoadState::loaded
428 ));
429 assert!(is_unit_unhealthy(
430 SystemdUnitActiveState::inactive,
431 SystemdUnitLoadState::loaded
432 ));
433 assert!(is_unit_unhealthy(
434 SystemdUnitActiveState::failed,
435 SystemdUnitLoadState::loaded
436 ));
437 assert!(is_unit_unhealthy(
438 SystemdUnitActiveState::activating,
439 SystemdUnitLoadState::loaded
440 ));
441 assert!(is_unit_unhealthy(
442 SystemdUnitActiveState::deactivating,
443 SystemdUnitLoadState::loaded
444 ));
445 assert!(is_unit_unhealthy(
446 SystemdUnitActiveState::unknown,
447 SystemdUnitLoadState::loaded
448 ));
449
450 assert!(!is_unit_unhealthy(
452 SystemdUnitActiveState::active,
453 SystemdUnitLoadState::masked
454 ));
455 assert!(!is_unit_unhealthy(
456 SystemdUnitActiveState::reloading,
457 SystemdUnitLoadState::masked
458 ));
459 assert!(!is_unit_unhealthy(
460 SystemdUnitActiveState::inactive,
461 SystemdUnitLoadState::masked
462 ));
463 assert!(!is_unit_unhealthy(
464 SystemdUnitActiveState::failed,
465 SystemdUnitLoadState::masked
466 ));
467 assert!(!is_unit_unhealthy(
468 SystemdUnitActiveState::activating,
469 SystemdUnitLoadState::masked
470 ));
471 assert!(!is_unit_unhealthy(
472 SystemdUnitActiveState::deactivating,
473 SystemdUnitLoadState::masked
474 ));
475 assert!(!is_unit_unhealthy(
476 SystemdUnitActiveState::unknown,
477 SystemdUnitLoadState::masked
478 ));
479
480 assert!(is_unit_unhealthy(
482 SystemdUnitActiveState::active,
483 SystemdUnitLoadState::error
484 ));
485 assert!(is_unit_unhealthy(
486 SystemdUnitActiveState::reloading,
487 SystemdUnitLoadState::error
488 ));
489 assert!(is_unit_unhealthy(
490 SystemdUnitActiveState::inactive,
491 SystemdUnitLoadState::error
492 ));
493 assert!(is_unit_unhealthy(
494 SystemdUnitActiveState::failed,
495 SystemdUnitLoadState::error
496 ));
497 assert!(is_unit_unhealthy(
498 SystemdUnitActiveState::activating,
499 SystemdUnitLoadState::error
500 ));
501 assert!(is_unit_unhealthy(
502 SystemdUnitActiveState::deactivating,
503 SystemdUnitLoadState::error
504 ));
505 assert!(is_unit_unhealthy(
506 SystemdUnitActiveState::unknown,
507 SystemdUnitLoadState::error
508 ));
509
510 assert!(is_unit_unhealthy(
512 SystemdUnitActiveState::active,
513 SystemdUnitLoadState::not_found
514 ));
515 assert!(is_unit_unhealthy(
516 SystemdUnitActiveState::reloading,
517 SystemdUnitLoadState::not_found
518 ));
519 assert!(is_unit_unhealthy(
520 SystemdUnitActiveState::inactive,
521 SystemdUnitLoadState::not_found
522 ));
523 assert!(is_unit_unhealthy(
524 SystemdUnitActiveState::failed,
525 SystemdUnitLoadState::not_found
526 ));
527 assert!(is_unit_unhealthy(
528 SystemdUnitActiveState::activating,
529 SystemdUnitLoadState::not_found
530 ));
531 assert!(is_unit_unhealthy(
532 SystemdUnitActiveState::deactivating,
533 SystemdUnitLoadState::not_found
534 ));
535 assert!(is_unit_unhealthy(
536 SystemdUnitActiveState::unknown,
537 SystemdUnitLoadState::not_found
538 ));
539
540 assert!(is_unit_unhealthy(
542 SystemdUnitActiveState::active,
543 SystemdUnitLoadState::unknown
544 ));
545 assert!(is_unit_unhealthy(
546 SystemdUnitActiveState::reloading,
547 SystemdUnitLoadState::unknown
548 ));
549 assert!(is_unit_unhealthy(
550 SystemdUnitActiveState::inactive,
551 SystemdUnitLoadState::unknown
552 ));
553 assert!(is_unit_unhealthy(
554 SystemdUnitActiveState::failed,
555 SystemdUnitLoadState::unknown
556 ));
557 assert!(is_unit_unhealthy(
558 SystemdUnitActiveState::activating,
559 SystemdUnitLoadState::unknown
560 ));
561 assert!(is_unit_unhealthy(
562 SystemdUnitActiveState::deactivating,
563 SystemdUnitLoadState::unknown
564 ));
565 assert!(is_unit_unhealthy(
566 SystemdUnitActiveState::unknown,
567 SystemdUnitLoadState::unknown
568 ));
569 }
570
571 #[test]
572 fn test_active_state_serialization() {
573 let state = SystemdUnitActiveState::active;
574 let serialized = serde_json::to_string(&state).unwrap();
575 assert_eq!(serialized, "1");
576
577 let deserialized: SystemdUnitActiveState = serde_json::from_str(&serialized).unwrap();
578 assert_eq!(deserialized, state);
579 }
580
581 #[test]
582 fn test_load_state_serialization() {
583 let state = SystemdUnitLoadState::loaded;
584 let serialized = serde_json::to_string(&state).unwrap();
585 assert_eq!(serialized, "1");
586
587 let deserialized: SystemdUnitLoadState = serde_json::from_str(&serialized).unwrap();
588 assert_eq!(deserialized, state);
589 }
590
591 #[test]
592 fn test_active_state_all_variants_serialization() {
593 for state in SystemdUnitActiveState::iter() {
594 let serialized = serde_json::to_string(&state).unwrap();
595 let deserialized: SystemdUnitActiveState = serde_json::from_str(&serialized).unwrap();
596 assert_eq!(deserialized, state);
597 }
598 }
599
600 #[test]
601 fn test_load_state_all_variants_serialization() {
602 for state in SystemdUnitLoadState::iter() {
603 let serialized = serde_json::to_string(&state).unwrap();
604 let deserialized: SystemdUnitLoadState = serde_json::from_str(&serialized).unwrap();
605 assert_eq!(deserialized, state);
606 }
607 }
608}