Skip to main content

sysd_manager_comcontroler/
enums.rs

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