systemd_connector/
properties.rs1use std::{collections::HashMap, io, str::FromStr};
4
5use thiserror::Error;
6
7pub fn properties(unit: &str) -> Result<SystemDProperties, PropertyParseError> {
9 let mut cmd = std::process::Command::new("systemctl");
10 cmd.arg("show");
11 cmd.arg(unit);
12
13 let output = cmd.output()?;
14
15 String::from_utf8(output.stdout).unwrap().parse()
16}
17
18#[derive(Debug, Clone, Copy)]
20pub enum ActiveState {
21 Active,
23
24 Reloading,
26
27 Inactive,
29
30 Failed,
32
33 Activating,
35
36 Deactivating,
38}
39
40#[derive(Debug, Error)]
42#[error("{0} is not a valid state")]
43pub struct StateParseError(String);
44
45impl FromStr for ActiveState {
46 type Err = StateParseError;
47 fn from_str(s: &str) -> Result<Self, Self::Err> {
48 use ActiveState::*;
49 match s {
50 "active" => Ok(Active),
51 "reloading" => Ok(Reloading),
52 "inactive" => Ok(Inactive),
53 "failed" => Ok(Failed),
54 "activating" => Ok(Activating),
55 "deactivating" => Ok(Deactivating),
56 _ => Err(StateParseError(s.into())),
57 }
58 }
59}
60
61#[derive(Debug, Error)]
63pub enum PropertyParseError {
64 #[error(transparent)]
66 State(#[from] StateParseError),
67
68 #[error("Line {0} is missing the delimiter '='")]
70 MissingDelimeter(String),
71
72 #[error("Missing property {0}")]
74 MissingProperty(&'static str),
75
76 #[error("Running systemctl: {0}")]
78 CommandError(#[from] io::Error),
79}
80
81#[derive(Debug, Clone)]
83pub struct SystemDProperties {
84 properties: HashMap<String, String>,
85 active: ActiveState,
86}
87
88impl SystemDProperties {
89 pub fn state(&self) -> ActiveState {
91 self.active
92 }
93
94 pub fn property(&self, name: &str) -> Option<&str> {
96 self.properties.get(name).map(|s| s.as_str())
97 }
98}
99
100impl FromStr for SystemDProperties {
101 type Err = PropertyParseError;
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 let mut properties = HashMap::new();
104
105 for line in s.lines() {
106 let (key, value) = line
107 .trim()
108 .split_once('=')
109 .ok_or_else(|| PropertyParseError::MissingDelimeter(line.into()))?;
110
111 properties.insert(key.to_owned(), value.to_owned());
112 }
113
114 let active = properties
115 .get("ActiveState")
116 .ok_or(PropertyParseError::MissingProperty("ActiveState"))?
117 .parse()?;
118
119 Ok(Self { properties, active })
120 }
121}