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