1use 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 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 EnablementStatus::Alias => Some(pgettext(
173 "list",
174 "The name is an alias (symlink to another unit file).",
175 )),
176 EnablementStatus::Bad => Some(pgettext(
178 "list",
179 "The unit file is invalid or another error occurred.",
180 )),
181 EnablementStatus::Disabled => Some(pgettext(
183 "list",
184 "The unit file is not enabled, but contains an [Install] section with installation instructions.",
185 )),
186 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 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 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 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 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 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 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 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 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 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"), ActiveState::Maintenance => Some("emblem-system-symbolic"), 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 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::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::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#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
608#[enum_type(name = "KillWho")]
609pub enum KillWho {
610 #[enum_value(name = "main", nick = "Main")]
612 Main,
613
614 #[enum_value(name = "control", nick = "Control")]
617 Control,
618
619 #[enum_value(name = "all", nick = "All" )]
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 DependencyType::Forward => pgettext("dependency", "Forward"),
688 DependencyType::Reverse => pgettext("dependency", "Reverse"),
690 DependencyType::After => pgettext("dependency", "After"),
692 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")]
736pub enum StartStopMode {
738 #[enum_value(name = "replace")]
741 Replace,
742
743 #[default]
746 #[enum_value(name = "fail")]
747 Fail,
748
749 #[enum_value(name = "isolate")]
753 Isolate,
754
755 #[enum_value(name = "ignore-dependencies")]
757 IgnoreDependencies,
758
759 #[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#[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 CleanOption::Runtime => pgettext("clean", "_Runtime"),
862 CleanOption::State => pgettext("clean", "_State"),
864 CleanOption::Cache => pgettext("clean", "Cac_he"),
866 CleanOption::Logs => pgettext("clean", "_Logs"),
868 CleanOption::Configuration => pgettext("clean", "_Configuration"),
870 CleanOption::Fdstore => pgettext("clean", "_File Descriptor Store"),
872 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")]
968pub 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")]
1013pub 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}