Skip to main content

sysd_manager_comcontroler/
enums.rs

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    /// Takes the string containing the state information from the dbus message and converts it
99    /// into a UnitType by matching the first character.
100    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            //tooltip column cell
173            UnitFileStatus::Alias => Some(pgettext(
174                "list",
175                "The name is an alias (symlink to another unit file).",
176            )),
177            //tooltip column cell
178            UnitFileStatus::Bad => Some(pgettext(
179                "list",
180                "The unit file is invalid or another error occurred.",
181            )),
182            //tooltip column cell
183            UnitFileStatus::Disabled => Some(pgettext(
184                "list",
185                "The unit file is not enabled, but contains an [Install] section with installation instructions.",
186            )),
187            //tooltip column cell
188            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            //tooltip column cell
193            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            //tooltip column cell
198            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            //tooltip column cell
203            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            //tooltip column cell
208            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            //tooltip column cell
213            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            //tooltip column cell
218            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            //tooltip column cell
225            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            //tooltip column cell
230            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            //tooltip column cell
235            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"), //not sure of the icon choice
379            ActiveState::Maintenance => Some("emblem-system-symbolic"), //not sure of the icon choice
380            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    /// Takes the pathname of the unit as input to determine what type of unit it is.
498    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::Busname => "busname",
546            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::Busname => "busname",
591            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/// KillUnit() may be used to kill (i.e. send a signal to) all processes of a unit.
622/// Takes the unit name, an enum who and a UNIX signal number to send.
623/// The who enum is one of "main", "control" or "all". If "main", only the main process of a unit is killed. If "control" only the control process of the unit is killed, if "all" all processes are killed. A "control" process is for example a process that is configured via ExecStop= and is spawned in parallel to the main daemon process, in order to shut it down.
624#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
625#[enum_type(name = "KillWho")]
626pub enum KillWho {
627    /// If "main", only the main process of a unit is killed.
628    #[enum_value(name = "main", nick = "Main"/* "Only the main unit's process" */)]
629    Main,
630
631    ///If "control" only the control process of the unit is killed
632    /// A "control" process is for example a process that is configured via ExecStop= and is spawned in parallel to the main daemon process, in order to shut it down.
633    #[enum_value(name = "control", nick = "Control"/* "Only the unit's controled processes" */)]
634    Control,
635
636    ///If "all" all processes are killed.
637    #[enum_value(name = "all", nick =  "All" /* "All unit's processes" */)]
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            //menu option
704            DependencyType::Forward => pgettext("dependency", "Forward"),
705            //menu option
706            DependencyType::Reverse => pgettext("dependency", "Reverse"),
707            //menu option
708            DependencyType::After => pgettext("dependency", "After"),
709            //menu option
710            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")]
753//#[allow(dead_code)]
754pub enum StartStopMode {
755    ///If "replace" the call will start the unit and its dependencies,
756    /// possibly replacing already queued jobs that conflict with this.
757    #[enum_value(name = "replace")]
758    Replace,
759
760    ///If "fail" the call will start the unit and its dependencies, but will fail if this
761    ///would change an already queued job.
762    #[default]
763    #[enum_value(name = "fail")]
764    Fail,
765
766    ///If "isolate" the call will start the unit in
767    ///question and terminate all units that aren't dependencies of it.
768    ///Note that "isolate" mode is invalid for method **StopUnit**.
769    #[enum_value(name = "isolate")]
770    Isolate,
771
772    ///If "ignore-dependencies" it will start a unit but ignore all its dependencies.
773    #[enum_value(name = "ignore-dependencies")]
774    IgnoreDependencies,
775
776    ///If "ignore-requirements" it will start a unit but only ignore the requirement dependencies.
777    #[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/* impl From<Option<glib::Object>> for StartStopMode {
835    fn from(value: Option<glib::Object>) -> Self {
836        let Some(object) = value else {
837            return StartStopMode::default();
838        };
839
840        let enum_list_item = object
841            .downcast::<adw::EnumListItem>()
842            .expect("Needs to be EnumListItem");
843
844        StartStopMode::from(enum_list_item.name().as_str())
845    }
846} */
847
848#[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            //clean options
878            CleanOption::Runtime => pgettext("clean", "_Runtime"),
879            //clean options
880            CleanOption::State => pgettext("clean", "_State"),
881            //clean options
882            CleanOption::Cache => pgettext("clean", "Cac_he"),
883            //clean options
884            CleanOption::Logs => pgettext("clean", "_Logs"),
885            //clean options
886            CleanOption::Configuration => pgettext("clean", "_Configuration"),
887            //clean options
888            CleanOption::Fdstore => pgettext("clean", "_File Descriptor Store"),
889            //clean options
890            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")]
991//#[allow(dead_code)]
992pub 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")]
1036//#[allow(dead_code)]
1037pub 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}