1use crate::errors::SystemdErrors;
2use crate::sysdbus::{INTERFACE_SYSTEMD_MANAGER, INTERFACE_SYSTEMD_UNIT};
3use enumflags2::{_internal::RawBitFlags, bitflags};
4use gettextrs::pgettext;
5use glib::{self, EnumValue, value::ToValue};
6use std::{cell::RefCell, fmt::Display, str::FromStr};
7use strum::{EnumIter, IntoEnumIterator};
8use tracing::{info, warn};
9use zvariant::OwnedValue;
10
11const MASKED: &str = "masked";
12const ENABLED: &str = "enabled";
13const LINKED: &str = "linked";
14
15#[derive(
16 Clone, Copy, Debug, PartialEq, Eq, EnumIter, Default, Hash, glib::Enum, Ord, PartialOrd,
17)]
18#[enum_type(name = "Preset")]
19pub enum Preset {
20 #[default]
21 UnSet,
22 Enabled,
23 Disabled,
24 Ignore,
25}
26
27impl Preset {
28 pub fn as_str(&self) -> &'static str {
29 match self {
30 Preset::UnSet => "",
31 Preset::Ignore => "ignored",
32 Preset::Disabled => "disabled",
33 Preset::Enabled => ENABLED,
34 }
35 }
36
37 pub fn as_str_op(&self) -> Option<&'static str> {
38 match self {
39 Preset::UnSet => None,
40 Preset::Ignore => Some("ignored"),
41 Preset::Disabled => Some("disabled"),
42 Preset::Enabled => Some(ENABLED),
43 }
44 }
45
46 pub fn label(&self) -> &'static str {
47 match self {
48 Preset::UnSet => "<i>not set</i>",
49 _ => self.as_str(),
50 }
51 }
52
53 pub fn tooltip_info(&self) -> Option<&str> {
54 None
55 }
56}
57
58impl From<&str> for Preset {
59 fn from(value: &str) -> Self {
60 match value {
61 ENABLED => Preset::Enabled,
62 "disabled" => Preset::Disabled,
63 "ignored" => Preset::Ignore,
64 _ => Preset::UnSet,
65 }
66 }
67}
68
69impl From<String> for Preset {
70 fn from(value: String) -> Self {
71 value.as_str().into()
72 }
73}
74
75#[derive(
76 Clone, Copy, Debug, PartialEq, Eq, EnumIter, Default, Hash, glib::Enum, PartialOrd, Ord,
77)]
78#[enum_type(name = "EnablementStatus")]
79pub enum UnitFileStatus {
80 #[default]
81 Unknown,
82 Alias,
83 Bad,
84 Disabled,
85 Enabled,
86 EnabledRuntime,
87 Generated,
88 Indirect,
89 Linked,
90 LinkedRuntime,
91 Masked,
92 MaskedRuntime,
93 Static,
94 Transient,
95}
96
97impl UnitFileStatus {
98 pub fn from_strr(enablement_status: &str) -> UnitFileStatus {
101 if enablement_status.is_empty() {
102 info!("Empty Enablement Status: \"{enablement_status}\"");
103 return UnitFileStatus::Unknown;
104 }
105
106 let c = enablement_status.chars().next().unwrap();
107
108 match c {
109 'a' => UnitFileStatus::Alias,
110 's' => UnitFileStatus::Static,
111 'd' => UnitFileStatus::Disabled,
112 'e' => {
113 if enablement_status.len() == ENABLED.len() {
114 UnitFileStatus::Enabled
115 } else {
116 UnitFileStatus::EnabledRuntime
117 }
118 }
119 'i' => UnitFileStatus::Indirect,
120 'l' => {
121 if enablement_status.len() == LINKED.len() {
122 UnitFileStatus::Linked
123 } else {
124 UnitFileStatus::LinkedRuntime
125 }
126 }
127 'm' => {
128 if enablement_status.len() == MASKED.len() {
129 UnitFileStatus::Masked
130 } else {
131 UnitFileStatus::MaskedRuntime
132 }
133 }
134 'b' => UnitFileStatus::Bad,
135 'g' => UnitFileStatus::Generated,
136 't' => UnitFileStatus::Transient,
137 _ => {
138 warn!("Unknown State: {enablement_status}");
139 UnitFileStatus::Unknown
140 }
141 }
142 }
143
144 pub fn as_str(&self) -> &'static str {
145 match self {
146 UnitFileStatus::Alias => "alias",
147 UnitFileStatus::Bad => "bad",
148 UnitFileStatus::Disabled => "disabled",
149 UnitFileStatus::Enabled => ENABLED,
150 UnitFileStatus::Indirect => "indirect",
151 UnitFileStatus::Linked => LINKED,
152 UnitFileStatus::Masked => MASKED,
153 UnitFileStatus::Static => "static",
154 UnitFileStatus::Generated => "generated",
155 UnitFileStatus::Transient => "transient",
156 UnitFileStatus::EnabledRuntime => "enabled-runtime",
157 UnitFileStatus::LinkedRuntime => "linked-runtime",
158 UnitFileStatus::MaskedRuntime => "masked-runtime",
159 UnitFileStatus::Unknown => "",
160 }
161 }
162
163 pub fn label(&self) -> &'static str {
164 match self {
165 UnitFileStatus::Unknown => "<i>unset</i>",
166 _ => self.as_str(),
167 }
168 }
169
170 pub fn tooltip_info(&self) -> Option<String> {
171 match self {
172 UnitFileStatus::Alias => Some(pgettext(
174 "list",
175 "The name is an alias (symlink to another unit file).",
176 )),
177 UnitFileStatus::Bad => Some(pgettext(
179 "list",
180 "The unit file is invalid or another error occurred.",
181 )),
182 UnitFileStatus::Disabled => Some(pgettext(
184 "list",
185 "The unit file is not enabled, but contains an [Install] section with installation instructions.",
186 )),
187 UnitFileStatus::Enabled => Some(pgettext(
189 "list",
190 "Enabled via <span fgcolor='#62a0ea'>.wants/</span>, <span fgcolor='#62a0ea'>.requires/</span> or <u>Alias=</u> symlinks (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span>, or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>).",
191 )),
192 UnitFileStatus::Generated => Some(pgettext(
194 "list",
195 "The unit file was generated dynamically via a generator tool. See <b>man systemd.generator(7)</b>. Generated unit files may not be enabled, they are enabled implicitly by their generator.",
196 )),
197 UnitFileStatus::Indirect => Some(pgettext(
199 "list",
200 "The unit file itself is not enabled, but it has a non-empty <u>Also=</u> setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <u>Also=</u>. For template unit files, an instance different than the one specified in <u>DefaultInstance=</u> is enabled.",
201 )),
202 UnitFileStatus::Linked => Some(pgettext(
204 "list",
205 "Made available through one or more symlinks to the unit file (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>), even though the unit file might reside outside of the unit file search path.",
206 )),
207 UnitFileStatus::Masked => Some(pgettext(
209 "list",
210 "Completely disabled, so that any start operation on it fails (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/systemd/</span>).",
211 )),
212 UnitFileStatus::Static => Some(pgettext(
214 "list",
215 "The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section.",
216 )),
217 UnitFileStatus::Transient => Some(pgettext(
219 "list",
220 "The unit file has been created dynamically with the runtime API. Transient units may not be enabled.",
221 )),
222
223 UnitFileStatus::Unknown => None,
224 UnitFileStatus::EnabledRuntime => Some(pgettext(
226 "list",
227 "Enabled via <span fgcolor='#62a0ea'>.wants/</span>, <span fgcolor='#62a0ea'>.requires/</span> or <u>Alias=</u> symlinks (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span>, or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>).",
228 )),
229 UnitFileStatus::LinkedRuntime => Some(pgettext(
231 "list",
232 "Made available through one or more symlinks to the unit file (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>), even though the unit file might reside outside of the unit file search path.",
233 )),
234 UnitFileStatus::MaskedRuntime => Some(pgettext(
236 "list",
237 "Completely disabled, so that any start operation on it fails (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/systemd/</span>).",
238 )),
239 }
240 }
241
242 pub fn is_runtime(&self) -> bool {
243 matches!(
244 self,
245 UnitFileStatus::LinkedRuntime
246 | UnitFileStatus::MaskedRuntime
247 | UnitFileStatus::EnabledRuntime
248 )
249 }
250
251 pub fn has_status(&self) -> bool {
252 !matches!(self, UnitFileStatus::Unknown)
253 }
254}
255
256impl Display for UnitFileStatus {
257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
258 write!(f, "{}", self.as_str())
259 }
260}
261
262impl From<Option<String>> for UnitFileStatus {
263 fn from(value: Option<String>) -> Self {
264 if let Some(str_val) = value {
265 return UnitFileStatus::from_str(&str_val).expect("always Status");
266 }
267 UnitFileStatus::Unknown
268 }
269}
270
271impl From<String> for UnitFileStatus {
272 fn from(value: String) -> Self {
273 UnitFileStatus::from_str(&value).expect("always Status")
274 }
275}
276
277impl From<&str> for UnitFileStatus {
278 fn from(value: &str) -> Self {
279 UnitFileStatus::from_str(value).expect("always Status")
280 }
281}
282
283impl FromStr for UnitFileStatus {
284 type Err = SystemdErrors;
285
286 fn from_str(enablement_status: &str) -> Result<Self, Self::Err> {
287 if enablement_status.is_empty() {
288 info!("Empty Enablement Status: \"{enablement_status}\"");
289 return Ok(UnitFileStatus::Unknown);
290 }
291
292 let c = enablement_status.chars().next().unwrap();
293
294 let status = match c {
295 'a' => UnitFileStatus::Alias,
296 's' => UnitFileStatus::Static,
297 'd' => UnitFileStatus::Disabled,
298 'e' => {
299 if enablement_status.len() == ENABLED.len() {
300 UnitFileStatus::Enabled
301 } else {
302 UnitFileStatus::EnabledRuntime
303 }
304 }
305 'i' => UnitFileStatus::Indirect,
306 'l' => {
307 if enablement_status.len() == LINKED.len() {
308 UnitFileStatus::Linked
309 } else {
310 UnitFileStatus::LinkedRuntime
311 }
312 }
313 'm' => {
314 if enablement_status.len() == MASKED.len() {
315 UnitFileStatus::Masked
316 } else {
317 UnitFileStatus::MaskedRuntime
318 }
319 }
320 'b' => UnitFileStatus::Bad,
321 'g' => UnitFileStatus::Generated,
322 't' => UnitFileStatus::Transient,
323 _ => {
324 warn!("Unknown State: {enablement_status}");
325 UnitFileStatus::Unknown
326 }
327 };
328 Ok(status)
329 }
330}
331
332#[derive(
333 Clone, Copy, Default, Debug, PartialEq, Eq, EnumIter, Hash, glib::Enum, PartialOrd, Ord,
334)]
335#[enum_type(name = "ActiveState")]
336pub enum ActiveState {
337 Unknown,
338 Active,
339 Activating,
340 Reloading,
341 #[default]
342 Inactive,
343 Failed,
344 Deactivating,
345 Maintenance,
346 Refreshing,
347}
348
349impl ActiveState {
350 pub fn as_str(&self) -> &str {
351 match self {
352 ActiveState::Unknown => "unknown",
353 ActiveState::Active => "active",
354 ActiveState::Reloading => "reloading",
355 ActiveState::Inactive => "inactive",
356 ActiveState::Failed => "failed",
357 ActiveState::Activating => "activating",
358 ActiveState::Deactivating => "deactivating",
359 ActiveState::Maintenance => "maintenance",
360 ActiveState::Refreshing => "refreshing",
361 }
362 }
363
364 pub fn label(&self) -> &str {
365 match self {
366 ActiveState::Unknown => "<i>unset</i>",
367 _ => self.as_str(),
368 }
369 }
370
371 pub fn icon_name(&self) -> Option<&'static str> {
372 match self {
373 ActiveState::Active
374 | ActiveState::Reloading
375 | ActiveState::Refreshing
376 | ActiveState::Activating => Some("object-select-symbolic"),
377 ActiveState::Inactive | ActiveState::Deactivating => Some("window-close-symbolic"),
378 ActiveState::Failed => Some("computer-fail-symbolic"), ActiveState::Maintenance => Some("emblem-system-symbolic"), ActiveState::Unknown => None,
381 }
382 }
383
384 pub fn is_inactive(&self) -> bool {
385 matches!(
386 self,
387 ActiveState::Inactive | ActiveState::Deactivating | ActiveState::Unknown
388 )
389 }
390
391 pub fn glyph_str(&self) -> &str {
392 match self {
393 ActiveState::Active => "●",
394 ActiveState::Reloading => "↻",
395 ActiveState::Inactive => "○",
396 ActiveState::Failed => "×",
397 ActiveState::Activating => "●",
398 ActiveState::Deactivating => "●",
399 ActiveState::Maintenance => "○",
400 ActiveState::Refreshing => "↻",
401 _ => " ",
402 }
403 }
404
405 pub fn tooltip_info(&self) -> Option<&str> {
406 let value = match self {
407 ActiveState::Active => "Started, bound, plugged in, ..., depending on the unit type.",
408 ActiveState::Reloading => {
409 "Unit is <b>active</b> and it is reloading its configuration."
410 }
411 ActiveState::Inactive => {
412 "Stopped, unbound, unplugged, ..., depending on the unit type."
413 }
414 ActiveState::Failed => {
415 "Similar to inactive, but the unit failed in some way (process returned error code on exit, crashed, an operation timed out, or after too many restarts)."
416 }
417 ActiveState::Activating => "Changing from <b>inactive</b> to <b>active</b>. ",
418 ActiveState::Deactivating => "Changing from <b>active</b> to <b>inactive</b>. ",
419 ActiveState::Maintenance => {
420 "Unit is inactive and a maintenance operation is in progress."
421 }
422 ActiveState::Refreshing => {
423 "Unit is active and a new mount is being activated in its namespace."
424 }
425 _ => "",
426 };
427
428 Some(value)
429 }
430}
431
432impl Display for ActiveState {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 write!(f, "{}", self.as_str())
435 }
436}
437
438impl From<String> for ActiveState {
439 fn from(value: String) -> Self {
440 value.as_str().into()
441 }
442}
443
444impl From<&str> for ActiveState {
445 fn from(value: &str) -> Self {
446 match value {
447 "active" => ActiveState::Active,
448 "reloading" => ActiveState::Reloading,
449 "inactive" => ActiveState::Inactive,
450 "failed" => ActiveState::Failed,
451 "activating" => ActiveState::Activating,
452 "deactivating" => ActiveState::Deactivating,
453 "maintenance" => ActiveState::Maintenance,
454 "refreshing" => ActiveState::Refreshing,
455 _ => ActiveState::Unknown,
456 }
457 }
458}
459
460impl From<Option<&OwnedValue>> for ActiveState {
461 fn from(value: Option<&OwnedValue>) -> Self {
462 match value {
463 Some(value) => {
464 let state_str: &str = value.try_into().unwrap_or_default();
465 ActiveState::from(state_str)
466 }
467 None => ActiveState::Unknown,
468 }
469 }
470}
471
472#[derive(
473 Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumIter, glib::Enum, Hash,
474)]
475#[enum_type(name = "UnitType")]
476pub enum UnitType {
477 Automount,
478 Busname,
479 Device,
480 Manager,
481 Mount,
482 Path,
483 Scope,
484 Service,
485 Slice,
486 Snapshot,
487 Socket,
488 Swap,
489 Target,
490 Timer,
491 Unit,
492 #[default]
493 Unknown,
494}
495
496impl UnitType {
497 pub fn new(unit_type: &str) -> UnitType {
499 match unit_type {
500 "automount" => UnitType::Automount,
501 "busname" => UnitType::Busname,
502 "device" => UnitType::Device,
503 "mount" => UnitType::Mount,
504 "path" => UnitType::Path,
505 "scope" => UnitType::Scope,
506 "service" => UnitType::Service,
507 "slice" => UnitType::Slice,
508 "socket" => UnitType::Socket,
509 "swap" => UnitType::Swap,
510 "target" => UnitType::Target,
511 "timer" => UnitType::Timer,
512 "snapshot" => UnitType::Snapshot,
513 "unit" => UnitType::Unit,
514 _ => {
515 warn!("Unknown Unit Type name: {unit_type}");
516 UnitType::Unknown
517 }
518 }
519 }
520
521 pub fn as_str(&self) -> &'static str {
522 match self {
523 Self::Automount => "automount",
524 Self::Busname => "busname",
525 Self::Device => "device",
526 Self::Manager => "manager",
527 Self::Mount => "mount",
528 Self::Path => "path",
529 Self::Scope => "scope",
530 Self::Service => "service",
531 Self::Slice => "slice",
532 Self::Socket => "socket",
533 Self::Target => "target",
534 Self::Timer => "timer",
535 Self::Swap => "swap",
536 Self::Snapshot => "snapshot",
537 Self::Unit => "unit",
538 _ => "",
539 }
540 }
541
542 pub fn interface(&self) -> &str {
543 match self {
544 Self::Automount => "org.freedesktop.systemd1.Automount",
545 Self::Device => "org.freedesktop.systemd1.Device",
547 Self::Manager => INTERFACE_SYSTEMD_MANAGER,
548 Self::Mount => "org.freedesktop.systemd1.Mount",
549 Self::Path => "org.freedesktop.systemd1.Path",
550 Self::Scope => "org.freedesktop.systemd1.Scope",
551 Self::Service => "org.freedesktop.systemd1.Service",
552 Self::Slice => "org.freedesktop.systemd1.Slice",
553 Self::Snapshot => "org.freedesktop.systemd1.Snapshot",
554 Self::Socket => "org.freedesktop.systemd1.Socket",
555 Self::Swap => "org.freedesktop.systemd1.Swap",
556 Self::Target => "org.freedesktop.systemd1.Target",
557 Self::Timer => "org.freedesktop.systemd1.Timer",
558 Self::Unit => INTERFACE_SYSTEMD_UNIT,
559 Self::Unknown => "",
560
561 _ => INTERFACE_SYSTEMD_UNIT,
562 }
563 }
564
565 pub fn from_intreface(interface: &str) -> UnitType {
566 match interface {
567 "org.freedesktop.systemd1.Automount" => UnitType::Automount,
568 "org.freedesktop.systemd1.Device" => UnitType::Device,
569 "org.freedesktop.systemd1.Mount" => UnitType::Mount,
570 "org.freedesktop.systemd1.Path" => UnitType::Path,
571 "org.freedesktop.systemd1.Scope" => UnitType::Scope,
572 "org.freedesktop.systemd1.Service" => UnitType::Service,
573 "org.freedesktop.systemd1.Slice" => UnitType::Slice,
574 "org.freedesktop.systemd1.Snapshot" => UnitType::Snapshot,
575 "org.freedesktop.systemd1.Socket" => UnitType::Socket,
576 "org.freedesktop.systemd1.Swap" => UnitType::Swap,
577 "org.freedesktop.systemd1.Target" => UnitType::Target,
578 "org.freedesktop.systemd1.Timer" => UnitType::Timer,
579 INTERFACE_SYSTEMD_UNIT => UnitType::Unit,
580 _ => {
581 warn!("Unknown Unit Type: {interface}");
582 UnitType::Unknown
583 }
584 }
585 }
586
587 pub(crate) fn extends_unit(&self) -> bool {
588 match self {
589 Self::Automount => true,
590 Self::Device => true,
592 Self::Manager => false,
593 Self::Mount => true,
594 Self::Path => true,
595 Self::Scope => true,
596 Self::Service => true,
597 Self::Slice => true,
598 Self::Snapshot => true,
599 Self::Socket => true,
600 Self::Swap => true,
601 Self::Target => true,
602 Self::Timer => true,
603
604 _ => false,
605 }
606 }
607}
608
609impl From<&str> for UnitType {
610 fn from(value: &str) -> Self {
611 UnitType::new(value)
612 }
613}
614
615impl From<String> for UnitType {
616 fn from(value: String) -> Self {
617 UnitType::new(&value)
618 }
619}
620
621#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
625#[enum_type(name = "KillWho")]
626pub enum KillWho {
627 #[enum_value(name = "main", nick = "Main")]
629 Main,
630
631 #[enum_value(name = "control", nick = "Control")]
634 Control,
635
636 #[enum_value(name = "all", nick = "All" )]
638 All,
639}
640
641impl KillWho {
642 pub fn as_str(&self) -> &str {
643 match self {
644 Self::Main => "main",
645 Self::Control => "control",
646 Self::All => "all",
647 }
648 }
649
650 pub fn description(&self) -> &str {
651 match self {
652 Self::Main => "Only the main unit's process",
653 Self::Control => "Only the unit's controled processes",
654 Self::All => "All unit's processes",
655 }
656 }
657}
658
659impl Display for KillWho {
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 let value: glib::Value = self.to_value();
662
663 let out = if let Some(enum_value) = EnumValue::from_value(&value) {
664 enum_value.1.name()
665 } else {
666 ""
667 };
668
669 write!(f, "{out}")
670 }
671}
672
673impl From<i32> for KillWho {
674 fn from(value: i32) -> Self {
675 match value {
676 0 => KillWho::Main,
677 1 => KillWho::Control,
678 2 => KillWho::All,
679 _ => KillWho::Main,
680 }
681 }
682}
683
684impl From<u32> for KillWho {
685 fn from(value: u32) -> Self {
686 (value as i32).into()
687 }
688}
689
690#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, strum::EnumIter)]
691
692pub enum DependencyType {
693 #[default]
694 Forward = 0,
695 Reverse = 1,
696 After = 2,
697 Before = 3,
698}
699
700impl DependencyType {
701 pub fn label(&self) -> String {
702 match self {
703 DependencyType::Forward => pgettext("dependency", "Forward"),
705 DependencyType::Reverse => pgettext("dependency", "Reverse"),
707 DependencyType::After => pgettext("dependency", "After"),
709 DependencyType::Before => pgettext("dependency", "Before"),
711 }
712 }
713
714 pub(super) fn properties(&self) -> &[&str] {
715 let properties: &[&str] = match self {
716 DependencyType::Forward => &[
717 "Requires",
718 "Requisite",
719 "Wants",
720 "ConsistsOf",
721 "BindsTo",
722 "Upholds",
723 ],
724 DependencyType::Reverse => &[
725 "RequiredBy",
726 "RequisiteOf",
727 "WantedBy",
728 "PartOf",
729 "BoundBy",
730 "UpheldBy",
731 ],
732 DependencyType::After => &["After"],
733 DependencyType::Before => &["Before"],
734 };
735 properties
736 }
737}
738
739impl From<u32> for DependencyType {
740 fn from(dtype: u32) -> Self {
741 match dtype {
742 0 => DependencyType::Forward,
743 1 => DependencyType::Reverse,
744 2 => DependencyType::After,
745 3 => DependencyType::Before,
746 _ => DependencyType::Forward,
747 }
748 }
749}
750
751#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
752#[enum_type(name = "EnableUnitFileMode")]
753pub enum StartStopMode {
755 #[enum_value(name = "replace")]
758 Replace,
759
760 #[default]
763 #[enum_value(name = "fail")]
764 Fail,
765
766 #[enum_value(name = "isolate")]
770 Isolate,
771
772 #[enum_value(name = "ignore-dependencies")]
774 IgnoreDependencies,
775
776 #[enum_value(name = "ignore-requirements")]
778 IgnoreRequirements,
779}
780
781impl StartStopMode {
782 pub fn as_str(&self) -> &'static str {
783 let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
784 enum_value.name()
785 }
786
787 pub fn discriminant(&self) -> u32 {
788 let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
789 enum_value.value() as u32
790 }
791}
792
793impl From<&RefCell<String>> for StartStopMode {
794 fn from(value: &RefCell<String>) -> Self {
795 let borrowed = value.borrow();
796 StartStopMode::from(borrowed.as_str())
797 }
798}
799
800impl From<&str> for StartStopMode {
801 fn from(value: &str) -> Self {
802 match value.to_ascii_lowercase().as_str() {
803 "fail" => StartStopMode::Fail,
804 "replace" => StartStopMode::Replace,
805 "isolate" => StartStopMode::Isolate,
806 "ignore-dependencies" => StartStopMode::IgnoreDependencies,
807 "ignore-requirements" => StartStopMode::IgnoreRequirements,
808
809 unknown => {
810 warn!("unknown start mode {unknown:?}");
811 StartStopMode::default()
812 }
813 }
814 }
815}
816
817impl From<&glib::Variant> for StartStopMode {
818 fn from(value: &glib::Variant) -> Self {
819 let Some(value) = value.get::<String>() else {
820 warn!("Variant not String");
821 return StartStopMode::Fail;
822 };
823
824 StartStopMode::from(value.as_str())
825 }
826}
827
828impl From<glib::Variant> for StartStopMode {
829 fn from(value: glib::Variant) -> Self {
830 StartStopMode::from(&value)
831 }
832}
833
834#[bitflags]
849#[repr(u8)]
850#[derive(Copy, Clone)]
851pub enum DisEnableFlags {
852 SdSystemdUnitRuntime = 1,
853 SdSystemdUnitForce = 1 << 1,
854 SdSystemdUnitPortable = 1 << 2,
855}
856
857impl DisEnableFlags {
858 pub fn as_u64(&self) -> u64 {
859 self.bits() as u64
860 }
861}
862
863#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, EnumIter)]
864pub enum CleanOption {
865 Runtime,
866 State,
867 Cache,
868 Logs,
869 Configuration,
870 Fdstore,
871 All,
872}
873
874impl CleanOption {
875 pub fn label(&self) -> String {
876 match &self {
877 CleanOption::Runtime => pgettext("clean", "_Runtime"),
879 CleanOption::State => pgettext("clean", "_State"),
881 CleanOption::Cache => pgettext("clean", "Cac_he"),
883 CleanOption::Logs => pgettext("clean", "_Logs"),
885 CleanOption::Configuration => pgettext("clean", "_Configuration"),
887 CleanOption::Fdstore => pgettext("clean", "_File Descriptor Store"),
889 CleanOption::All => pgettext("clean", "_All"),
891 }
892 }
893
894 pub fn code(&self) -> &str {
895 match &self {
896 CleanOption::Runtime => "runtime",
897 CleanOption::State => "state",
898 CleanOption::Cache => "cache",
899 CleanOption::Logs => "logs",
900 CleanOption::Configuration => "configuration",
901 CleanOption::Fdstore => "fdstore",
902 CleanOption::All => "all",
903 }
904 }
905}
906
907#[derive(
908 Clone, Copy, Debug, PartialEq, Eq, EnumIter, Hash, Default, Ord, PartialOrd, glib::Enum,
909)]
910#[enum_type(name = "LoadState")]
911pub enum LoadState {
912 #[default]
913 Unknown,
914 Loaded,
915 NotFound,
916 BadSetting,
917 Error,
918 Masked,
919}
920
921impl LoadState {
922 pub fn as_str(&self) -> &'static str {
923 match self {
924 LoadState::Unknown => "",
925 LoadState::Loaded => "loaded",
926 LoadState::NotFound => "not-found",
927 LoadState::BadSetting => "bad-setting",
928 LoadState::Error => "error",
929 LoadState::Masked => "masked",
930 }
931 }
932
933 pub fn label(&self) -> &'static str {
934 match self {
935 LoadState::Unknown => "<i>not set</i>",
936 _ => self.as_str(),
937 }
938 }
939
940 pub fn tooltip_info(&self) -> Option<&str> {
941 None
942 }
943}
944
945impl From<String> for LoadState {
946 fn from(value: String) -> Self {
947 Some(value.as_str()).into()
948 }
949}
950
951impl From<&str> for LoadState {
952 fn from(value: &str) -> Self {
953 Some(value).into()
954 }
955}
956
957impl From<Option<&str>> for LoadState {
958 fn from(value: Option<&str>) -> Self {
959 match value {
960 Some("loaded") => LoadState::Loaded,
961 Some("not-found") => LoadState::NotFound,
962 Some("bad-setting") => LoadState::BadSetting,
963 Some("error") => LoadState::Error,
964 Some("masked") => LoadState::Masked,
965 _ => LoadState::Unknown,
966 }
967 }
968}
969
970impl From<Option<String>> for LoadState {
971 fn from(value: Option<String>) -> Self {
972 match value {
973 Some(s) => s.as_str().into(),
974 None => LoadState::Unknown,
975 }
976 }
977}
978
979impl From<Option<&OwnedValue>> for LoadState {
980 fn from(value: Option<&OwnedValue>) -> Self {
981 let value: Option<&zvariant::Value> = value.map(|v| &**v);
982 match value {
983 Some(zvariant::Value::Str(zvalue)) => zvalue.as_str().into(),
984 _ => LoadState::Unknown,
985 }
986 }
987}
988
989#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
990#[enum_type(name = "StrMatchType")]
991pub enum StrMatchType {
993 #[default]
994 #[enum_value(name = "contains")]
995 Contains,
996
997 #[enum_value(name = "start_with")]
998 StartWith,
999
1000 #[enum_value(name = "end_with")]
1001 EndWith,
1002
1003 #[enum_value(name = "equals")]
1004 Equals,
1005}
1006
1007impl StrMatchType {
1008 pub fn as_str(&self) -> &'static str {
1009 let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
1010 enum_value.name()
1011 }
1012
1013 pub fn position(&self) -> u32 {
1014 match self {
1015 StrMatchType::Contains => 0,
1016 StrMatchType::StartWith => 1,
1017 StrMatchType::EndWith => 2,
1018 StrMatchType::Equals => 3,
1019 }
1020 }
1021}
1022
1023impl From<u32> for StrMatchType {
1024 fn from(value: u32) -> Self {
1025 for (idx, mt) in StrMatchType::iter().enumerate() {
1026 if idx == value as usize {
1027 return mt;
1028 }
1029 }
1030 StrMatchType::default()
1031 }
1032}
1033
1034#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
1035#[enum_type(name = "NumMatchType")]
1036pub enum NumMatchType {
1038 #[default]
1039 #[enum_value(name = "equals")]
1040 Equals,
1041
1042 #[enum_value(name = "greater")]
1043 Greater,
1044
1045 #[enum_value(name = "smaller")]
1046 Smaller,
1047
1048 #[enum_value(name = "greater equals")]
1049 GreaterEquals,
1050
1051 #[enum_value(name = "smaller equals")]
1052 SmallerEquals,
1053}
1054
1055impl NumMatchType {
1056 pub fn as_str(&self) -> &'static str {
1057 let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
1058 enum_value.name()
1059 }
1060
1061 pub fn position(&self) -> u32 {
1062 match self {
1063 NumMatchType::Equals => 0,
1064 NumMatchType::Greater => 1,
1065 NumMatchType::Smaller => 2,
1066 NumMatchType::GreaterEquals => 3,
1067 &NumMatchType::SmallerEquals => 4,
1068 }
1069 }
1070}
1071
1072impl From<u32> for NumMatchType {
1073 fn from(value: u32) -> Self {
1074 for (idx, mt) in NumMatchType::iter().enumerate() {
1075 if idx == value as usize {
1076 return mt;
1077 }
1078 }
1079 NumMatchType::default()
1080 }
1081}
1082
1083#[cfg(test)]
1084mod tests {
1085
1086 use base::enums::UnitDBusLevel;
1087
1088 use super::*;
1089
1090 #[test]
1091 fn test_kill_who_glib() {
1092 assert_kill(KillWho::All);
1093 assert_kill(KillWho::Main);
1094 assert_kill(KillWho::Control);
1095 }
1096
1097 fn assert_kill(kill: KillWho) {
1098 assert_eq!(kill.as_str(), kill.to_string())
1099 }
1100
1101 #[test]
1102 fn test_unit_level() {
1103 fn test(ul: UnitDBusLevel) {
1104 let num_val = ul.value();
1105 let ul2: UnitDBusLevel = (num_val as u8).into();
1106 assert_eq!(ul, ul2);
1107
1108 let str_val = ul.as_str();
1109 let ul3: UnitDBusLevel = str_val.into();
1110 assert_eq!(ul, ul3);
1111 }
1112 test(UnitDBusLevel::System);
1113 test(UnitDBusLevel::UserSession);
1114 }
1115}