1use std::net::IpAddr;
8use std::time::Duration;
9
10use zbus::zvariant::{Signature, Structure, Type, Value};
11
12pub trait PropValue: Type + Into<Value<'static>> {}
14impl<T: Type + Into<Value<'static>>> PropValue for T {}
15
16macro_rules! unit_properties {
17 ($( $name:ident : $ty:ty = $sig:literal ),* $(,)?) => {
18 #[allow(clippy::upper_case_acronyms)]
20 #[derive(Debug, Clone)]
21 pub enum UnitProperty {
22 $( #[doc = concat!("`", $sig, "`")] $name($ty), )*
23 Custom(std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>),
25 }
26
27 impl UnitProperty {
28 pub fn into_pair(self) -> (std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>) {
29 match self {
30 $( Self::$name(v) => (std::borrow::Cow::Borrowed(stringify!($name)), v.into()), )*
31 Self::Custom(k, v) => (k, v),
32 }
33 }
34 }
35
36 #[test]
38 fn signatures() {
39 $( assert_eq!(<$ty as zbus::zvariant::Type>::SIGNATURE.to_string(), $sig, stringify!($name)); )*
40 }
41 };
42}
43
44macro_rules! str_enum {
45 ($name:ident { $($var:ident => $s:literal),+ $(,)? }) => {
46 #[allow(non_camel_case_types)]
47 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48 pub enum $name { $($var),+ }
49
50 impl $name {
51 pub const fn as_str(self) -> &'static str {
52 match self { $(Self::$var => $s),+ }
53 }
54 }
55
56 impl std::str::FromStr for $name {
57 type Err = zbus::zvariant::Error;
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 match s {
60 $($s => Ok(Self::$var),)+
61 _ => Err(zbus::zvariant::Error::Message(format!(concat!("invalid ", stringify!($name), ": {}"), s))),
62 }
63 }
64 }
65
66 impl zbus::zvariant::Type for $name {
67 const SIGNATURE: &'static zbus::zvariant::Signature = &zbus::zvariant::Signature::Str;
68 }
69
70 impl From<$name> for zbus::zvariant::Value<'_> {
71 fn from(v: $name) -> Self { zbus::zvariant::Value::from(v.as_str()) }
72 }
73
74 impl TryFrom<zbus::zvariant::Value<'_>> for $name {
75 type Error = zbus::zvariant::Error;
76 fn try_from(v: zbus::zvariant::Value<'_>) -> Result<Self, Self::Error> {
77 <&str>::try_from(&v)?.parse()
78 }
79 }
80 };
81}
82
83mod generated;
84pub use generated::*;
85
86pub fn get_property<F, R>(props: &[UnitProperty], filter: F) -> Option<R>
87where
88 F: Fn(&UnitProperty) -> Option<R>,
89{
90 props
91 .iter()
92 .flat_map(|props| filter(props).into_iter())
93 .next()
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct Usec(pub u64);
101
102impl Usec {
103 pub const INFINITY: Self = Self(u64::MAX);
104}
105
106impl From<Duration> for Usec {
107 fn from(d: Duration) -> Self {
108 Self(u64::try_from(d.as_micros()).unwrap_or(u64::MAX))
109 }
110}
111
112impl From<Usec> for Duration {
113 fn from(value: Usec) -> Self {
114 Self::from_micros(value.0)
115 }
116}
117
118impl Type for Usec {
119 const SIGNATURE: &'static Signature = &Signature::U64;
120}
121
122impl From<Usec> for Value<'_> {
123 fn from(u: Usec) -> Self {
124 Value::from(u.0)
125 }
126}
127
128#[derive(Debug, Clone, Type, Value)]
131pub struct ExecCommand {
132 pub path: String,
133 pub argv: Vec<String>,
135 pub ignore_failure: bool,
136}
137
138#[derive(Debug, Clone, Type, Value)]
139pub struct ExecCommandEx {
140 pub path: String,
141 pub argv: Vec<String>,
142 pub flags: Vec<ExecCommandFlag>,
143}
144
145#[derive(Debug, Clone, Type, Value)]
146pub struct Condition {
147 pub kind: String,
149 pub trigger: bool,
151 pub negate: bool,
153 pub parameter: String,
154}
155
156#[derive(Debug, Clone, Default, Type, Value)]
157pub struct ExitStatusSet {
158 pub statuses: Vec<i32>,
159 pub signals: Vec<i32>,
160}
161
162#[derive(Debug, Clone, Type, Value)]
163pub struct OpenFile {
164 pub path: String,
165 pub fd_name: String,
166 pub flags: u64,
168}
169
170#[derive(Debug, Clone, Type, Value)]
171pub struct RefreshOnReload {
172 pub invert: bool,
174 pub flag: String,
176}
177
178#[derive(Debug, Clone, Type, Value)]
179pub struct SecurityLabel {
180 pub ignore_failure: bool,
182 pub label: String,
183}
184
185pub const MS_REC: u64 = 16384;
186
187#[derive(Debug, Clone, Type, Value)]
188pub struct BindPath {
189 pub source: String,
190 pub destination: String,
191 pub ignore_missing: bool,
193 pub flags: u64,
195}
196
197#[derive(Debug, Clone, Copy)]
199pub struct DirectoryQuota {
200 pub absolute: u64,
202 pub scale: u32,
204 pub enforce: bool,
205}
206
207impl Type for DirectoryQuota {
208 const SIGNATURE: &'static Signature = <(u64, u32, String)>::SIGNATURE;
209}
210
211impl From<DirectoryQuota> for Value<'_> {
212 fn from(q: DirectoryQuota) -> Self {
213 Structure::from((q.absolute, q.scale, if q.enforce { "yes" } else { "no" })).into()
214 }
215}
216
217#[derive(Debug, Clone, Type, Value)]
218pub struct DirectorySymlink {
219 pub source: String,
220 pub destination: String,
221 pub flags: u64,
223}
224
225#[derive(Debug, Clone, Type, Value)]
226pub struct EnvironmentFile {
227 pub path: String,
228 pub ignore_missing: bool,
229}
230
231#[derive(Debug, Clone, Type, Value)]
232pub struct MountOption {
233 pub partition: String,
235 pub options: String,
236}
237
238#[derive(Debug, Clone, Type, Value)]
239pub struct ExtensionImage {
240 pub source: String,
241 pub ignore_missing: bool,
242 pub options: Vec<MountOption>,
243}
244
245#[derive(Debug, Clone, Type, Value)]
246pub struct MountImage {
247 pub source: String,
248 pub destination: String,
249 pub ignore_missing: bool,
250 pub options: Vec<MountOption>,
251}
252
253#[derive(Debug, Clone, Type, Value)]
254pub struct ImportCredential {
255 pub glob: String,
256 pub rename: String,
258}
259
260#[derive(Debug, Clone, Type, Value)]
261pub struct LoadCredential {
262 pub id: String,
263 pub path: String,
264}
265
266#[derive(Debug, Clone, Type, Value)]
267pub struct SetCredential {
268 pub id: String,
269 pub data: Vec<u8>,
270}
271
272#[derive(Debug, Clone, Type, Value)]
273pub struct LogFilterPattern {
274 pub allow: bool,
276 pub pattern: String,
277}
278
279#[derive(Debug, Clone, Type, Value)]
280pub struct ProtectHostnameEx {
281 pub mode: ProtectHostname,
282 pub hostname: String,
284}
285
286#[derive(Debug, Clone, Type, Value)]
289pub struct FilterList {
290 pub allow_list: bool,
292 pub items: Vec<String>,
293}
294
295#[derive(Debug, Clone, Type, Value)]
296pub struct TmpFs {
297 pub path: String,
298 pub options: String,
299}
300
301#[derive(Debug, Clone, Type, Value)]
302pub struct BpfProgram {
303 pub attach_type: String,
305 pub path: String,
306}
307
308#[derive(Debug, Clone, Type, Value)]
309pub struct DeviceAllow {
310 pub path: String,
311 pub access: String,
313}
314
315#[derive(Debug, Clone, Type, Value)]
317pub struct DeviceValue {
318 pub path: String,
319 pub value: u64,
320}
321
322#[derive(Debug, Clone, Copy, Type, Value)]
323pub struct SocketBind {
324 pub family: i32,
326 pub protocol: i32,
328 pub port_count: u16,
330 pub port_min: u16,
331}
332
333#[derive(Debug, Clone, Type, Value)]
334pub struct NftSet {
335 pub source: i32,
337 pub nfproto: i32,
339 pub table: String,
340 pub set: String,
341}
342
343#[derive(Debug, Clone, Copy)]
345pub struct IpPrefix {
346 pub address: IpAddr,
347 pub prefix_len: u32,
348}
349
350impl Type for IpPrefix {
351 const SIGNATURE: &'static Signature = <(i32, Vec<u8>, u32)>::SIGNATURE;
352}
353
354impl From<IpPrefix> for Value<'_> {
355 fn from(p: IpPrefix) -> Self {
356 const AF_INET: i32 = 2;
357 const AF_INET6: i32 = 10;
358 let (af, bytes) = match p.address {
359 IpAddr::V4(a) => (AF_INET, a.octets().to_vec()),
360 IpAddr::V6(a) => (AF_INET6, a.octets().to_vec()),
361 };
362 Structure::from((af, bytes, p.prefix_len)).into()
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn exec_start_wire_format() {
372 let (k, v) = UnitProperty::ExecStart(vec![ExecCommand {
373 path: "/bin/true".into(),
374 argv: vec!["true".into()],
375 ignore_failure: false,
376 }])
377 .into_pair();
378 assert_eq!(k, "ExecStart");
379 assert_eq!(v.value_signature().to_string(), "a(sasb)");
380 }
381
382 #[test]
383 fn enum_round_trip() {
384 let v: Value = ServiceType::NotifyReload.into();
385 assert_eq!(ServiceType::try_from(v).unwrap(), ServiceType::NotifyReload);
386 }
387}