1use crate::errors::SystemdErrors;
2use crate::sysdbus::{INTERFACE_SYSTEMD_MANAGER, INTERFACE_SYSTEMD_UNIT};
3use enumflags2::{_internal::RawBitFlags, bitflags};
4use gettextrs::pgettext;
5use glib::{self, EnumValue, value::ToValue};
6use std::{cell::RefCell, fmt::Display, str::FromStr};
7use strum::{EnumIter, IntoEnumIterator};
8use tracing::{info, warn};
9use zvariant::OwnedValue;
10
11const MASKED: &str = "masked";
12const ENABLED: &str = "enabled";
13const LINKED: &str = "linked";
14
15#[derive(
16 Clone, Copy, Debug, PartialEq, Eq, EnumIter, Default, Hash, glib::Enum, Ord, PartialOrd,
17)]
18#[enum_type(name = "Preset")]
19pub enum Preset {
20 #[default]
21 UnSet,
22 Enabled,
23 Disabled,
24 Ignore,
25}
26
27impl Preset {
28 pub fn as_str(&self) -> &'static str {
29 match self {
30 Preset::UnSet => "",
31 Preset::Ignore => "ignored",
32 Preset::Disabled => "disabled",
33 Preset::Enabled => ENABLED,
34 }
35 }
36
37 pub fn as_str_op(&self) -> Option<&'static str> {
38 match self {
39 Preset::UnSet => None,
40 Preset::Ignore => Some("ignored"),
41 Preset::Disabled => Some("disabled"),
42 Preset::Enabled => Some(ENABLED),
43 }
44 }
45
46 pub fn label(&self) -> &'static str {
47 match self {
48 Preset::UnSet => "<i>not set</i>",
49 _ => self.as_str(),
50 }
51 }
52
53 pub fn tooltip_info(&self) -> Option<&str> {
54 None
55 }
56}
57
58impl From<&str> for Preset {
59 fn from(value: &str) -> Self {
60 match value {
61 ENABLED => Preset::Enabled,
62 "disabled" => Preset::Disabled,
63 "ignored" => Preset::Ignore,
64 _ => Preset::UnSet,
65 }
66 }
67}
68
69impl From<String> for Preset {
70 fn from(value: String) -> Self {
71 value.as_str().into()
72 }
73}
74
75#[derive(
76 Clone, Copy, Debug, PartialEq, Eq, EnumIter, Default, Hash, glib::Enum, PartialOrd, Ord,
77)]
78#[enum_type(name = "EnablementStatus")]
79pub enum UnitFileStatus {
80 #[default]
81 Unknown,
82 Alias,
83 Bad,
84 Disabled,
85 Enabled,
86 EnabledRuntime,
87 Generated,
88 Indirect,
89 Linked,
90 LinkedRuntime,
91 Masked,
92 MaskedRuntime,
93 Static,
94 Transient,
95}
96
97impl UnitFileStatus {
98 pub fn from_strr(enablement_status: &str) -> UnitFileStatus {
101 if enablement_status.is_empty() {
102 info!("Empty Enablement Status: \"{enablement_status}\"");
103 return UnitFileStatus::Unknown;
104 }
105
106 let c = enablement_status.chars().next().unwrap();
107
108 match c {
109 'a' => UnitFileStatus::Alias,
110 's' => UnitFileStatus::Static,
111 'd' => UnitFileStatus::Disabled,
112 'e' => {
113 if enablement_status.len() == ENABLED.len() {
114 UnitFileStatus::Enabled
115 } else {
116 UnitFileStatus::EnabledRuntime
117 }
118 }
119 'i' => UnitFileStatus::Indirect,
120 'l' => {
121 if enablement_status.len() == LINKED.len() {
122 UnitFileStatus::Linked
123 } else {
124 UnitFileStatus::LinkedRuntime
125 }
126 }
127 'm' => {
128 if enablement_status.len() == MASKED.len() {
129 UnitFileStatus::Masked
130 } else {
131 UnitFileStatus::MaskedRuntime
132 }
133 }
134 'b' => UnitFileStatus::Bad,
135 'g' => UnitFileStatus::Generated,
136 't' => UnitFileStatus::Transient,
137 _ => {
138 warn!("Unknown State: {enablement_status}");
139 UnitFileStatus::Unknown
140 }
141 }
142 }
143
144 pub fn as_str(&self) -> &'static str {
145 match self {
146 UnitFileStatus::Alias => "alias",
147 UnitFileStatus::Bad => "bad",
148 UnitFileStatus::Disabled => "disabled",
149 UnitFileStatus::Enabled => ENABLED,
150 UnitFileStatus::Indirect => "indirect",
151 UnitFileStatus::Linked => LINKED,
152 UnitFileStatus::Masked => MASKED,
153 UnitFileStatus::Static => "static",
154 UnitFileStatus::Generated => "generated",
155 UnitFileStatus::Transient => "transient",
156 UnitFileStatus::EnabledRuntime => "enabled-runtime",
157 UnitFileStatus::LinkedRuntime => "linked-runtime",
158 UnitFileStatus::MaskedRuntime => "masked-runtime",
159 UnitFileStatus::Unknown => "",
160 }
161 }
162
163 pub fn label(&self) -> &'static str {
164 match self {
165 UnitFileStatus::Unknown => "<i>unset</i>",
166 _ => self.as_str(),
167 }
168 }
169
170 pub fn tooltip_info(&self) -> Option<String> {
171 match self {
172 UnitFileStatus::Alias => Some(pgettext(
174 "list",
175 "The name is an alias (symlink to another unit file).",
176 )),
177 UnitFileStatus::Bad => Some(pgettext(
179 "list",
180 "The unit file is invalid or another error occurred.",
181 )),
182 UnitFileStatus::Disabled => Some(pgettext(
184 "list",
185 "The unit file is not enabled, but contains an [Install] section with installation instructions.",
186 )),
187 UnitFileStatus::Enabled => Some(pgettext(
189 "list",
190 "Enabled via <span fgcolor='#62a0ea'>.wants/</span>, <span fgcolor='#62a0ea'>.requires/</span> or <u>Alias=</u> symlinks (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span>, or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>).",
191 )),
192 UnitFileStatus::Generated => Some(pgettext(
194 "list",
195 "The unit file was generated dynamically via a generator tool. See <b>man systemd.generator(7)</b>. Generated unit files may not be enabled, they are enabled implicitly by their generator.",
196 )),
197 UnitFileStatus::Indirect => Some(pgettext(
199 "list",
200 "The unit file itself is not enabled, but it has a non-empty <u>Also=</u> setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <u>Also=</u>. For template unit files, an instance different than the one specified in <u>DefaultInstance=</u> is enabled.",
201 )),
202 UnitFileStatus::Linked => Some(pgettext(
204 "list",
205 "Made available through one or more symlinks to the unit file (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>), even though the unit file might reside outside of the unit file search path.",
206 )),
207 UnitFileStatus::Masked => Some(pgettext(
209 "list",
210 "Completely disabled, so that any start operation on it fails (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/systemd/</span>).",
211 )),
212 UnitFileStatus::Static => Some(pgettext(
214 "list",
215 "The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section.",
216 )),
217 UnitFileStatus::Transient => Some(pgettext(
219 "list",
220 "The unit file has been created dynamically with the runtime API. Transient units may not be enabled.",
221 )),
222
223 UnitFileStatus::Unknown => None,
224 UnitFileStatus::EnabledRuntime => Some(pgettext(
226 "list",
227 "Enabled via <span fgcolor='#62a0ea'>.wants/</span>, <span fgcolor='#62a0ea'>.requires/</span> or <u>Alias=</u> symlinks (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span>, or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>).",
228 )),
229 UnitFileStatus::LinkedRuntime => Some(pgettext(
231 "list",
232 "Made available through one or more symlinks to the unit file (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/system/</span>), even though the unit file might reside outside of the unit file search path.",
233 )),
234 UnitFileStatus::MaskedRuntime => Some(pgettext(
236 "list",
237 "Completely disabled, so that any start operation on it fails (permanently in <span fgcolor='#62a0ea'>/etc/systemd/system/</span> or transiently in <span fgcolor='#62a0ea'>/run/systemd/systemd/</span>).",
238 )),
239 }
240 }
241
242 pub fn is_runtime(&self) -> bool {
243 matches!(
244 self,
245 UnitFileStatus::LinkedRuntime
246 | UnitFileStatus::MaskedRuntime
247 | UnitFileStatus::EnabledRuntime
248 )
249 }
250
251 pub fn has_status(&self) -> bool {
252 !matches!(self, UnitFileStatus::Unknown)
253 }
254}
255
256impl Display for UnitFileStatus {
257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
258 write!(f, "{}", self.as_str())
259 }
260}
261
262impl From<Option<String>> for UnitFileStatus {
263 fn from(value: Option<String>) -> Self {
264 if let Some(str_val) = value {
265 return UnitFileStatus::from_str(&str_val).expect("always Status");
266 }
267 UnitFileStatus::Unknown
268 }
269}
270
271impl From<String> for UnitFileStatus {
272 fn from(value: String) -> Self {
273 UnitFileStatus::from_str(&value).expect("always Status")
274 }
275}
276
277impl From<&str> for UnitFileStatus {
278 fn from(value: &str) -> Self {
279 UnitFileStatus::from_str(value).expect("always Status")
280 }
281}
282
283impl FromStr for UnitFileStatus {
284 type Err = SystemdErrors;
285
286 fn from_str(enablement_status: &str) -> Result<Self, Self::Err> {
287 if enablement_status.is_empty() {
288 info!("Empty Enablement Status: \"{enablement_status}\"");
289 return Ok(UnitFileStatus::Unknown);
290 }
291
292 let c = enablement_status.chars().next().unwrap();
293
294 let status = match c {
295 'a' => UnitFileStatus::Alias,
296 's' => UnitFileStatus::Static,
297 'd' => UnitFileStatus::Disabled,
298 'e' => {
299 if enablement_status.len() == ENABLED.len() {
300 UnitFileStatus::Enabled
301 } else {
302 UnitFileStatus::EnabledRuntime
303 }
304 }
305 'i' => UnitFileStatus::Indirect,
306 'l' => {
307 if enablement_status.len() == LINKED.len() {
308 UnitFileStatus::Linked
309 } else {
310 UnitFileStatus::LinkedRuntime
311 }
312 }
313 'm' => {
314 if enablement_status.len() == MASKED.len() {
315 UnitFileStatus::Masked
316 } else {
317 UnitFileStatus::MaskedRuntime
318 }
319 }
320 'b' => UnitFileStatus::Bad,
321 'g' => UnitFileStatus::Generated,
322 't' => UnitFileStatus::Transient,
323 _ => {
324 warn!("Unknown State: {enablement_status}");
325 UnitFileStatus::Unknown
326 }
327 };
328 Ok(status)
329 }
330}
331
332#[derive(
333 Clone, Copy, Default, Debug, PartialEq, Eq, EnumIter, Hash, glib::Enum, PartialOrd, Ord,
334)]
335#[enum_type(name = "ActiveState")]
336pub enum ActiveState {
337 Unknown,
338 Active,
339 Activating,
340 Reloading,
341 #[default]
342 Inactive,
343 Failed,
344 Deactivating,
345 Maintenance,
346 Refreshing,
347}
348
349impl ActiveState {
350 pub fn as_str(&self) -> &str {
351 match self {
352 ActiveState::Unknown => "unknown",
353 ActiveState::Active => "active",
354 ActiveState::Reloading => "reloading",
355 ActiveState::Inactive => "inactive",
356 ActiveState::Failed => "failed",
357 ActiveState::Activating => "activating",
358 ActiveState::Deactivating => "deactivating",
359 ActiveState::Maintenance => "maintenance",
360 ActiveState::Refreshing => "refreshing",
361 }
362 }
363
364 pub fn label(&self) -> &str {
365 match self {
366 ActiveState::Unknown => "<i>unset</i>",
367 _ => self.as_str(),
368 }
369 }
370
371 pub fn icon_name(&self) -> Option<&'static str> {
372 match self {
373 ActiveState::Active
374 | ActiveState::Reloading
375 | ActiveState::Refreshing
376 | ActiveState::Activating => Some("object-select-symbolic"),
377 ActiveState::Inactive | ActiveState::Deactivating => Some("window-close-symbolic"),
378 ActiveState::Failed => Some("computer-fail-symbolic"), ActiveState::Maintenance => Some("emblem-system-symbolic"), ActiveState::Unknown => None,
381 }
382 }
383
384 pub fn is_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 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::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::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#[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)]
635#[enum_type(name = "KillWho")]
636pub enum KillWho {
637 #[enum_value(name = "main", nick = "Main")]
639 Main,
640
641 #[enum_value(name = "control", nick = "Control")]
644 Control,
645
646 #[enum_value(name = "all", nick = "All" )]
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 DependencyType::Forward => pgettext("dependency", "Forward"),
715 DependencyType::Reverse => pgettext("dependency", "Reverse"),
717 DependencyType::After => pgettext("dependency", "After"),
719 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")]
763pub enum StartStopMode {
765 #[enum_value(name = "replace")]
768 Replace,
769
770 #[default]
773 #[enum_value(name = "fail")]
774 Fail,
775
776 #[enum_value(name = "isolate")]
780 Isolate,
781
782 #[enum_value(name = "ignore-dependencies")]
784 IgnoreDependencies,
785
786 #[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#[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 CleanOption::Runtime => pgettext("clean", "_Runtime"),
889 CleanOption::State => pgettext("clean", "_State"),
891 CleanOption::Cache => pgettext("clean", "Cac_he"),
893 CleanOption::Logs => pgettext("clean", "_Logs"),
895 CleanOption::Configuration => pgettext("clean", "_Configuration"),
897 CleanOption::Fdstore => pgettext("clean", "_File Descriptor Store"),
899 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")]
1001pub 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")]
1046pub 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}