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