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_active(&self) -> bool {
385        matches!(
386            self,
387            ActiveState::Active
388                | ActiveState::Activating
389                | ActiveState::Reloading
390                | ActiveState::Refreshing
391        )
392    }
393
394    pub fn is_inactive(&self) -> bool {
395        matches!(
396            self,
397            ActiveState::Inactive | ActiveState::Deactivating | ActiveState::Unknown
398        )
399    }
400
401    pub fn glyph_str(&self) -> &str {
402        match self {
403            ActiveState::Active => "●",
404            ActiveState::Reloading => "↻",
405            ActiveState::Inactive => "○",
406            ActiveState::Failed => "×",
407            ActiveState::Activating => "●",
408            ActiveState::Deactivating => "●",
409            ActiveState::Maintenance => "○",
410            ActiveState::Refreshing => "↻",
411            _ => " ",
412        }
413    }
414
415    pub fn tooltip_info(&self) -> Option<&str> {
416        let value = match self {
417            ActiveState::Active => "Started, bound, plugged in, ..., depending on the unit type.",
418            ActiveState::Reloading => {
419                "Unit is <b>active</b> and it is reloading its configuration."
420            }
421            ActiveState::Inactive => {
422                "Stopped, unbound, unplugged, ..., depending on the unit type."
423            }
424            ActiveState::Failed => {
425                "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)."
426            }
427            ActiveState::Activating => "Changing from <b>inactive</b> to <b>active</b>. ",
428            ActiveState::Deactivating => "Changing from <b>active</b> to <b>inactive</b>. ",
429            ActiveState::Maintenance => {
430                "Unit is inactive and a maintenance operation is in progress."
431            }
432            ActiveState::Refreshing => {
433                "Unit is active and a new mount is being activated in its namespace."
434            }
435            _ => "",
436        };
437
438        Some(value)
439    }
440}
441
442impl Display for ActiveState {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        write!(f, "{}", self.as_str())
445    }
446}
447
448impl From<String> for ActiveState {
449    fn from(value: String) -> Self {
450        value.as_str().into()
451    }
452}
453
454impl From<&str> for ActiveState {
455    fn from(value: &str) -> Self {
456        match value {
457            "active" => ActiveState::Active,
458            "reloading" => ActiveState::Reloading,
459            "inactive" => ActiveState::Inactive,
460            "failed" => ActiveState::Failed,
461            "activating" => ActiveState::Activating,
462            "deactivating" => ActiveState::Deactivating,
463            "maintenance" => ActiveState::Maintenance,
464            "refreshing" => ActiveState::Refreshing,
465            _ => ActiveState::Unknown,
466        }
467    }
468}
469
470impl From<Option<&OwnedValue>> for ActiveState {
471    fn from(value: Option<&OwnedValue>) -> Self {
472        match value {
473            Some(value) => {
474                let state_str: &str = value.try_into().unwrap_or_default();
475                ActiveState::from(state_str)
476            }
477            None => ActiveState::Unknown,
478        }
479    }
480}
481
482#[derive(
483    Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumIter, glib::Enum, Hash,
484)]
485#[enum_type(name = "UnitType")]
486pub enum UnitType {
487    Automount,
488    Busname,
489    Device,
490    Manager,
491    Mount,
492    Path,
493    Scope,
494    Service,
495    Slice,
496    Snapshot,
497    Socket,
498    Swap,
499    Target,
500    Timer,
501    Unit,
502    #[default]
503    Unknown,
504}
505
506impl UnitType {
507    /// Takes the pathname of the unit as input to determine what type of unit it is.
508    pub fn new(unit_type: &str) -> UnitType {
509        match unit_type {
510            "automount" => UnitType::Automount,
511            "busname" => UnitType::Busname,
512            "device" => UnitType::Device,
513            "mount" => UnitType::Mount,
514            "path" => UnitType::Path,
515            "scope" => UnitType::Scope,
516            "service" => UnitType::Service,
517            "slice" => UnitType::Slice,
518            "socket" => UnitType::Socket,
519            "swap" => UnitType::Swap,
520            "target" => UnitType::Target,
521            "timer" => UnitType::Timer,
522            "snapshot" => UnitType::Snapshot,
523            "unit" => UnitType::Unit,
524            _ => {
525                warn!("Unknown Unit Type name: {unit_type}");
526                UnitType::Unknown
527            }
528        }
529    }
530
531    pub fn as_str(&self) -> &'static str {
532        match self {
533            Self::Automount => "automount",
534            Self::Busname => "busname",
535            Self::Device => "device",
536            Self::Manager => "manager",
537            Self::Mount => "mount",
538            Self::Path => "path",
539            Self::Scope => "scope",
540            Self::Service => "service",
541            Self::Slice => "slice",
542            Self::Socket => "socket",
543            Self::Target => "target",
544            Self::Timer => "timer",
545            Self::Swap => "swap",
546            Self::Snapshot => "snapshot",
547            Self::Unit => "unit",
548            _ => "",
549        }
550    }
551
552    pub fn interface(&self) -> &str {
553        match self {
554            Self::Automount => "org.freedesktop.systemd1.Automount",
555            //Self::Busname => "busname",
556            Self::Device => "org.freedesktop.systemd1.Device",
557            Self::Manager => INTERFACE_SYSTEMD_MANAGER,
558            Self::Mount => "org.freedesktop.systemd1.Mount",
559            Self::Path => "org.freedesktop.systemd1.Path",
560            Self::Scope => "org.freedesktop.systemd1.Scope",
561            Self::Service => "org.freedesktop.systemd1.Service",
562            Self::Slice => "org.freedesktop.systemd1.Slice",
563            Self::Snapshot => "org.freedesktop.systemd1.Snapshot",
564            Self::Socket => "org.freedesktop.systemd1.Socket",
565            Self::Swap => "org.freedesktop.systemd1.Swap",
566            Self::Target => "org.freedesktop.systemd1.Target",
567            Self::Timer => "org.freedesktop.systemd1.Timer",
568            Self::Unit => INTERFACE_SYSTEMD_UNIT,
569            Self::Unknown => "",
570
571            _ => INTERFACE_SYSTEMD_UNIT,
572        }
573    }
574
575    pub fn from_intreface(interface: &str) -> UnitType {
576        match interface {
577            "org.freedesktop.systemd1.Automount" => UnitType::Automount,
578            "org.freedesktop.systemd1.Device" => UnitType::Device,
579            "org.freedesktop.systemd1.Mount" => UnitType::Mount,
580            "org.freedesktop.systemd1.Path" => UnitType::Path,
581            "org.freedesktop.systemd1.Scope" => UnitType::Scope,
582            "org.freedesktop.systemd1.Service" => UnitType::Service,
583            "org.freedesktop.systemd1.Slice" => UnitType::Slice,
584            "org.freedesktop.systemd1.Snapshot" => UnitType::Snapshot,
585            "org.freedesktop.systemd1.Socket" => UnitType::Socket,
586            "org.freedesktop.systemd1.Swap" => UnitType::Swap,
587            "org.freedesktop.systemd1.Target" => UnitType::Target,
588            "org.freedesktop.systemd1.Timer" => UnitType::Timer,
589            INTERFACE_SYSTEMD_UNIT => UnitType::Unit,
590            _ => {
591                warn!("Unknown Unit Type: {interface}");
592                UnitType::Unknown
593            }
594        }
595    }
596
597    pub(crate) fn extends_unit(&self) -> bool {
598        match self {
599            Self::Automount => true,
600            //Self::Busname => "busname",
601            Self::Device => true,
602            Self::Manager => false,
603            Self::Mount => true,
604            Self::Path => true,
605            Self::Scope => true,
606            Self::Service => true,
607            Self::Slice => true,
608            Self::Snapshot => true,
609            Self::Socket => true,
610            Self::Swap => true,
611            Self::Target => true,
612            Self::Timer => true,
613
614            _ => false,
615        }
616    }
617}
618
619impl From<&str> for UnitType {
620    fn from(value: &str) -> Self {
621        UnitType::new(value)
622    }
623}
624
625impl From<String> for UnitType {
626    fn from(value: String) -> Self {
627        UnitType::new(&value)
628    }
629}
630
631/// KillUnit() may be used to kill (i.e. send a signal to) all processes of a unit.
632/// Takes the unit name, an enum who and a UNIX signal number to send.
633/// 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.
634#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
635#[enum_type(name = "KillWho")]
636pub enum KillWho {
637    /// If "main", only the main process of a unit is killed.
638    #[enum_value(name = "main", nick = "Main"/* "Only the main unit's process" */)]
639    Main,
640
641    ///If "control" only the control process of the unit is killed
642    /// 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.
643    #[enum_value(name = "control", nick = "Control"/* "Only the unit's controled processes" */)]
644    Control,
645
646    ///If "all" all processes are killed.
647    #[enum_value(name = "all", nick =  "All" /* "All unit's processes" */)]
648    All,
649}
650
651impl KillWho {
652    pub fn as_str(&self) -> &str {
653        match self {
654            Self::Main => "main",
655            Self::Control => "control",
656            Self::All => "all",
657        }
658    }
659
660    pub fn description(&self) -> &str {
661        match self {
662            Self::Main => "Only the main unit's process",
663            Self::Control => "Only the unit's controled processes",
664            Self::All => "All unit's processes",
665        }
666    }
667}
668
669impl Display for KillWho {
670    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671        let value: glib::Value = self.to_value();
672
673        let out = if let Some(enum_value) = EnumValue::from_value(&value) {
674            enum_value.1.name()
675        } else {
676            ""
677        };
678
679        write!(f, "{out}")
680    }
681}
682
683impl From<i32> for KillWho {
684    fn from(value: i32) -> Self {
685        match value {
686            0 => KillWho::Main,
687            1 => KillWho::Control,
688            2 => KillWho::All,
689            _ => KillWho::Main,
690        }
691    }
692}
693
694impl From<u32> for KillWho {
695    fn from(value: u32) -> Self {
696        (value as i32).into()
697    }
698}
699
700#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, strum::EnumIter)]
701
702pub enum DependencyType {
703    #[default]
704    Forward = 0,
705    Reverse = 1,
706    After = 2,
707    Before = 3,
708}
709
710impl DependencyType {
711    pub fn label(&self) -> String {
712        match self {
713            //menu option
714            DependencyType::Forward => pgettext("dependency", "Forward"),
715            //menu option
716            DependencyType::Reverse => pgettext("dependency", "Reverse"),
717            //menu option
718            DependencyType::After => pgettext("dependency", "After"),
719            //menu option
720            DependencyType::Before => pgettext("dependency", "Before"),
721        }
722    }
723
724    pub(super) fn properties(&self) -> &[&str] {
725        let properties: &[&str] = match self {
726            DependencyType::Forward => &[
727                "Requires",
728                "Requisite",
729                "Wants",
730                "ConsistsOf",
731                "BindsTo",
732                "Upholds",
733            ],
734            DependencyType::Reverse => &[
735                "RequiredBy",
736                "RequisiteOf",
737                "WantedBy",
738                "PartOf",
739                "BoundBy",
740                "UpheldBy",
741            ],
742            DependencyType::After => &["After"],
743            DependencyType::Before => &["Before"],
744        };
745        properties
746    }
747}
748
749impl From<u32> for DependencyType {
750    fn from(dtype: u32) -> Self {
751        match dtype {
752            0 => DependencyType::Forward,
753            1 => DependencyType::Reverse,
754            2 => DependencyType::After,
755            3 => DependencyType::Before,
756            _ => DependencyType::Forward,
757        }
758    }
759}
760
761#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
762#[enum_type(name = "EnableUnitFileMode")]
763//#[allow(dead_code)]
764pub enum StartStopMode {
765    ///If "replace" the call will start the unit and its dependencies,
766    /// possibly replacing already queued jobs that conflict with this.
767    #[enum_value(name = "replace")]
768    Replace,
769
770    ///If "fail" the call will start the unit and its dependencies, but will fail if this
771    ///would change an already queued job.
772    #[default]
773    #[enum_value(name = "fail")]
774    Fail,
775
776    ///If "isolate" the call will start the unit in
777    ///question and terminate all units that aren't dependencies of it.
778    ///Note that "isolate" mode is invalid for method **StopUnit**.
779    #[enum_value(name = "isolate")]
780    Isolate,
781
782    ///If "ignore-dependencies" it will start a unit but ignore all its dependencies.
783    #[enum_value(name = "ignore-dependencies")]
784    IgnoreDependencies,
785
786    ///If "ignore-requirements" it will start a unit but only ignore the requirement dependencies.
787    #[enum_value(name = "ignore-requirements")]
788    IgnoreRequirements,
789}
790
791impl StartStopMode {
792    pub fn as_str(&self) -> &'static str {
793        let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
794        enum_value.name()
795    }
796
797    pub fn discriminant(&self) -> u32 {
798        let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
799        enum_value.value() as u32
800    }
801}
802
803impl From<&RefCell<String>> for StartStopMode {
804    fn from(value: &RefCell<String>) -> Self {
805        let borrowed = value.borrow();
806        StartStopMode::from(borrowed.as_str())
807    }
808}
809
810impl From<&str> for StartStopMode {
811    fn from(value: &str) -> Self {
812        match value.to_ascii_lowercase().as_str() {
813            "fail" => StartStopMode::Fail,
814            "replace" => StartStopMode::Replace,
815            "isolate" => StartStopMode::Isolate,
816            "ignore-dependencies" => StartStopMode::IgnoreDependencies,
817            "ignore-requirements" => StartStopMode::IgnoreRequirements,
818
819            unknown => {
820                warn!("unknown start mode {unknown:?}");
821                StartStopMode::default()
822            }
823        }
824    }
825}
826
827impl From<&glib::Variant> for StartStopMode {
828    fn from(value: &glib::Variant) -> Self {
829        let Some(value) = value.get::<String>() else {
830            warn!("Variant not String");
831            return StartStopMode::Fail;
832        };
833
834        StartStopMode::from(value.as_str())
835    }
836}
837
838impl From<glib::Variant> for StartStopMode {
839    fn from(value: glib::Variant) -> Self {
840        StartStopMode::from(&value)
841    }
842}
843
844/* impl From<Option<glib::Object>> for StartStopMode {
845    fn from(value: Option<glib::Object>) -> Self {
846        let Some(object) = value else {
847            return StartStopMode::default();
848        };
849
850        let enum_list_item = object
851            .downcast::<adw::EnumListItem>()
852            .expect("Needs to be EnumListItem");
853
854        StartStopMode::from(enum_list_item.name().as_str())
855    }
856} */
857
858#[bitflags]
859#[repr(u8)]
860#[derive(Copy, Clone)]
861pub enum DisEnableFlags {
862    SdSystemdUnitRuntime = 1,
863    SdSystemdUnitForce = 1 << 1,
864    SdSystemdUnitPortable = 1 << 2,
865}
866
867impl DisEnableFlags {
868    pub fn as_u64(&self) -> u64 {
869        self.bits() as u64
870    }
871}
872
873#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, EnumIter)]
874pub enum CleanOption {
875    Runtime,
876    State,
877    Cache,
878    Logs,
879    Configuration,
880    Fdstore,
881    All,
882}
883
884impl CleanOption {
885    pub fn label(&self) -> String {
886        match &self {
887            //clean options
888            CleanOption::Runtime => pgettext("clean", "_Runtime"),
889            //clean options
890            CleanOption::State => pgettext("clean", "_State"),
891            //clean options
892            CleanOption::Cache => pgettext("clean", "Cac_he"),
893            //clean options
894            CleanOption::Logs => pgettext("clean", "_Logs"),
895            //clean options
896            CleanOption::Configuration => pgettext("clean", "_Configuration"),
897            //clean options
898            CleanOption::Fdstore => pgettext("clean", "_File Descriptor Store"),
899            //clean options
900            CleanOption::All => pgettext("clean", "_All"),
901        }
902    }
903
904    pub fn code(&self) -> &str {
905        match &self {
906            CleanOption::Runtime => "runtime",
907            CleanOption::State => "state",
908            CleanOption::Cache => "cache",
909            CleanOption::Logs => "logs",
910            CleanOption::Configuration => "configuration",
911            CleanOption::Fdstore => "fdstore",
912            CleanOption::All => "all",
913        }
914    }
915}
916
917#[derive(
918    Clone, Copy, Debug, PartialEq, Eq, EnumIter, Hash, Default, Ord, PartialOrd, glib::Enum,
919)]
920#[enum_type(name = "LoadState")]
921pub enum LoadState {
922    #[default]
923    Unknown,
924    Loaded,
925    NotFound,
926    BadSetting,
927    Error,
928    Masked,
929}
930
931impl LoadState {
932    pub fn as_str(&self) -> &'static str {
933        match self {
934            LoadState::Unknown => "",
935            LoadState::Loaded => "loaded",
936            LoadState::NotFound => "not-found",
937            LoadState::BadSetting => "bad-setting",
938            LoadState::Error => "error",
939            LoadState::Masked => "masked",
940        }
941    }
942
943    pub fn label(&self) -> &'static str {
944        match self {
945            LoadState::Unknown => "<i>not set</i>",
946            _ => self.as_str(),
947        }
948    }
949
950    pub fn tooltip_info(&self) -> Option<&str> {
951        None
952    }
953}
954
955impl From<String> for LoadState {
956    fn from(value: String) -> Self {
957        Some(value.as_str()).into()
958    }
959}
960
961impl From<&str> for LoadState {
962    fn from(value: &str) -> Self {
963        Some(value).into()
964    }
965}
966
967impl From<Option<&str>> for LoadState {
968    fn from(value: Option<&str>) -> Self {
969        match value {
970            Some("loaded") => LoadState::Loaded,
971            Some("not-found") => LoadState::NotFound,
972            Some("bad-setting") => LoadState::BadSetting,
973            Some("error") => LoadState::Error,
974            Some("masked") => LoadState::Masked,
975            _ => LoadState::Unknown,
976        }
977    }
978}
979
980impl From<Option<String>> for LoadState {
981    fn from(value: Option<String>) -> Self {
982        match value {
983            Some(s) => s.as_str().into(),
984            None => LoadState::Unknown,
985        }
986    }
987}
988
989impl From<Option<&OwnedValue>> for LoadState {
990    fn from(value: Option<&OwnedValue>) -> Self {
991        let value: Option<&zvariant::Value> = value.map(|v| &**v);
992        match value {
993            Some(zvariant::Value::Str(zvalue)) => zvalue.as_str().into(),
994            _ => LoadState::Unknown,
995        }
996    }
997}
998
999#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
1000#[enum_type(name = "StrMatchType")]
1001//#[allow(dead_code)]
1002pub enum StrMatchType {
1003    #[default]
1004    #[enum_value(name = "contains")]
1005    Contains,
1006
1007    #[enum_value(name = "start_with")]
1008    StartWith,
1009
1010    #[enum_value(name = "end_with")]
1011    EndWith,
1012
1013    #[enum_value(name = "equals")]
1014    Equals,
1015}
1016
1017impl StrMatchType {
1018    pub fn as_str(&self) -> &'static str {
1019        let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
1020        enum_value.name()
1021    }
1022
1023    pub fn position(&self) -> u32 {
1024        match self {
1025            StrMatchType::Contains => 0,
1026            StrMatchType::StartWith => 1,
1027            StrMatchType::EndWith => 2,
1028            StrMatchType::Equals => 3,
1029        }
1030    }
1031}
1032
1033impl From<u32> for StrMatchType {
1034    fn from(value: u32) -> Self {
1035        for (idx, mt) in StrMatchType::iter().enumerate() {
1036            if idx == value as usize {
1037                return mt;
1038            }
1039        }
1040        StrMatchType::default()
1041    }
1042}
1043
1044#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, glib::Enum, EnumIter)]
1045#[enum_type(name = "NumMatchType")]
1046//#[allow(dead_code)]
1047pub enum NumMatchType {
1048    #[default]
1049    #[enum_value(name = "equals")]
1050    Equals,
1051
1052    #[enum_value(name = "greater")]
1053    Greater,
1054
1055    #[enum_value(name = "smaller")]
1056    Smaller,
1057
1058    #[enum_value(name = "greater equals")]
1059    GreaterEquals,
1060
1061    #[enum_value(name = "smaller equals")]
1062    SmallerEquals,
1063}
1064
1065impl NumMatchType {
1066    pub fn as_str(&self) -> &'static str {
1067        let enum_value: &glib::EnumValue = self.to_value().get().expect("it's an enum");
1068        enum_value.name()
1069    }
1070
1071    pub fn position(&self) -> u32 {
1072        match self {
1073            NumMatchType::Equals => 0,
1074            NumMatchType::Greater => 1,
1075            NumMatchType::Smaller => 2,
1076            NumMatchType::GreaterEquals => 3,
1077            &NumMatchType::SmallerEquals => 4,
1078        }
1079    }
1080}
1081
1082impl From<u32> for NumMatchType {
1083    fn from(value: u32) -> Self {
1084        for (idx, mt) in NumMatchType::iter().enumerate() {
1085            if idx == value as usize {
1086                return mt;
1087            }
1088        }
1089        NumMatchType::default()
1090    }
1091}
1092
1093#[cfg(test)]
1094mod tests {
1095
1096    use base::enums::UnitDBusLevel;
1097
1098    use super::*;
1099
1100    #[test]
1101    fn test_kill_who_glib() {
1102        assert_kill(KillWho::All);
1103        assert_kill(KillWho::Main);
1104        assert_kill(KillWho::Control);
1105    }
1106
1107    fn assert_kill(kill: KillWho) {
1108        assert_eq!(kill.as_str(), kill.to_string())
1109    }
1110
1111    #[test]
1112    fn test_unit_level() {
1113        fn test(ul: UnitDBusLevel) {
1114            let num_val = ul.value();
1115            let ul2: UnitDBusLevel = (num_val as u8).into();
1116            assert_eq!(ul, ul2);
1117
1118            let str_val = ul.as_str();
1119            let ul3: UnitDBusLevel = str_val.into();
1120            assert_eq!(ul, ul3);
1121        }
1122        test(UnitDBusLevel::System);
1123        test(UnitDBusLevel::UserSession);
1124    }
1125}