use std::net::IpAddr;
use std::time::Duration;
use zbus::zvariant::{Signature, Structure, Type, Value};
pub trait PropValue: Type + Into<Value<'static>> {}
impl<T: Type + Into<Value<'static>>> PropValue for T {}
macro_rules! unit_properties {
($( $name:ident : $ty:ty = $sig:literal ),* $(,)?) => {
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone)]
pub enum UnitProperty {
$( #[doc = concat!("`", $sig, "`")] $name($ty), )*
Custom(std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>),
}
impl UnitProperty {
pub fn into_pair(self) -> (std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>) {
match self {
$( Self::$name(v) => (std::borrow::Cow::Borrowed(stringify!($name)), v.into()), )*
Self::Custom(k, v) => (k, v),
}
}
}
#[test]
fn signatures() {
$( assert_eq!(<$ty as zbus::zvariant::Type>::SIGNATURE.to_string(), $sig, stringify!($name)); )*
}
};
}
macro_rules! str_enum {
($name:ident { $($var:ident => $s:literal),+ $(,)? }) => {
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum $name { $($var),+ }
impl $name {
pub const fn as_str(self) -> &'static str {
match self { $(Self::$var => $s),+ }
}
}
impl std::str::FromStr for $name {
type Err = zbus::zvariant::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$($s => Ok(Self::$var),)+
_ => Err(zbus::zvariant::Error::Message(format!(concat!("invalid ", stringify!($name), ": {}"), s))),
}
}
}
impl zbus::zvariant::Type for $name {
const SIGNATURE: &'static zbus::zvariant::Signature = &zbus::zvariant::Signature::Str;
}
impl From<$name> for zbus::zvariant::Value<'_> {
fn from(v: $name) -> Self { zbus::zvariant::Value::from(v.as_str()) }
}
impl TryFrom<zbus::zvariant::Value<'_>> for $name {
type Error = zbus::zvariant::Error;
fn try_from(v: zbus::zvariant::Value<'_>) -> Result<Self, Self::Error> {
<&str>::try_from(&v)?.parse()
}
}
};
}
mod generated;
pub use generated::*;
pub fn get_property<F, R>(props: &[UnitProperty], filter: F) -> Option<R>
where
F: Fn(&UnitProperty) -> Option<R>,
{
props
.iter()
.flat_map(|props| filter(props).into_iter())
.next()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Usec(pub u64);
impl Usec {
pub const INFINITY: Self = Self(u64::MAX);
}
impl From<Duration> for Usec {
fn from(d: Duration) -> Self {
Self(u64::try_from(d.as_micros()).unwrap_or(u64::MAX))
}
}
impl From<Usec> for Duration {
fn from(value: Usec) -> Self {
Self::from_micros(value.0)
}
}
impl Type for Usec {
const SIGNATURE: &'static Signature = &Signature::U64;
}
impl From<Usec> for Value<'_> {
fn from(u: Usec) -> Self {
Value::from(u.0)
}
}
#[derive(Debug, Clone, Type, Value)]
pub struct ExecCommand {
pub path: String,
pub argv: Vec<String>,
pub ignore_failure: bool,
}
#[derive(Debug, Clone, Type, Value)]
pub struct ExecCommandEx {
pub path: String,
pub argv: Vec<String>,
pub flags: Vec<ExecCommandFlag>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct Condition {
pub kind: String,
pub trigger: bool,
pub negate: bool,
pub parameter: String,
}
#[derive(Debug, Clone, Default, Type, Value)]
pub struct ExitStatusSet {
pub statuses: Vec<i32>,
pub signals: Vec<i32>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct OpenFile {
pub path: String,
pub fd_name: String,
pub flags: u64,
}
#[derive(Debug, Clone, Type, Value)]
pub struct RefreshOnReload {
pub invert: bool,
pub flag: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct SecurityLabel {
pub ignore_failure: bool,
pub label: String,
}
pub const MS_REC: u64 = 16384;
#[derive(Debug, Clone, Type, Value)]
pub struct BindPath {
pub source: String,
pub destination: String,
pub ignore_missing: bool,
pub flags: u64,
}
#[derive(Debug, Clone, Copy)]
pub struct DirectoryQuota {
pub absolute: u64,
pub scale: u32,
pub enforce: bool,
}
impl Type for DirectoryQuota {
const SIGNATURE: &'static Signature = <(u64, u32, String)>::SIGNATURE;
}
impl From<DirectoryQuota> for Value<'_> {
fn from(q: DirectoryQuota) -> Self {
Structure::from((q.absolute, q.scale, if q.enforce { "yes" } else { "no" })).into()
}
}
#[derive(Debug, Clone, Type, Value)]
pub struct DirectorySymlink {
pub source: String,
pub destination: String,
pub flags: u64,
}
#[derive(Debug, Clone, Type, Value)]
pub struct EnvironmentFile {
pub path: String,
pub ignore_missing: bool,
}
#[derive(Debug, Clone, Type, Value)]
pub struct MountOption {
pub partition: String,
pub options: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct ExtensionImage {
pub source: String,
pub ignore_missing: bool,
pub options: Vec<MountOption>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct MountImage {
pub source: String,
pub destination: String,
pub ignore_missing: bool,
pub options: Vec<MountOption>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct ImportCredential {
pub glob: String,
pub rename: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct LoadCredential {
pub id: String,
pub path: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct SetCredential {
pub id: String,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct LogFilterPattern {
pub allow: bool,
pub pattern: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct ProtectHostnameEx {
pub mode: ProtectHostname,
pub hostname: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct FilterList {
pub allow_list: bool,
pub items: Vec<String>,
}
#[derive(Debug, Clone, Type, Value)]
pub struct TmpFs {
pub path: String,
pub options: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct BpfProgram {
pub attach_type: String,
pub path: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct DeviceAllow {
pub path: String,
pub access: String,
}
#[derive(Debug, Clone, Type, Value)]
pub struct DeviceValue {
pub path: String,
pub value: u64,
}
#[derive(Debug, Clone, Copy, Type, Value)]
pub struct SocketBind {
pub family: i32,
pub protocol: i32,
pub port_count: u16,
pub port_min: u16,
}
#[derive(Debug, Clone, Type, Value)]
pub struct NftSet {
pub source: i32,
pub nfproto: i32,
pub table: String,
pub set: String,
}
#[derive(Debug, Clone, Copy)]
pub struct IpPrefix {
pub address: IpAddr,
pub prefix_len: u32,
}
impl Type for IpPrefix {
const SIGNATURE: &'static Signature = <(i32, Vec<u8>, u32)>::SIGNATURE;
}
impl From<IpPrefix> for Value<'_> {
fn from(p: IpPrefix) -> Self {
const AF_INET: i32 = 2;
const AF_INET6: i32 = 10;
let (af, bytes) = match p.address {
IpAddr::V4(a) => (AF_INET, a.octets().to_vec()),
IpAddr::V6(a) => (AF_INET6, a.octets().to_vec()),
};
Structure::from((af, bytes, p.prefix_len)).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exec_start_wire_format() {
let (k, v) = UnitProperty::ExecStart(vec![ExecCommand {
path: "/bin/true".into(),
argv: vec!["true".into()],
ignore_failure: false,
}])
.into_pair();
assert_eq!(k, "ExecStart");
assert_eq!(v.value_signature().to_string(), "a(sasb)");
}
#[test]
fn enum_round_trip() {
let v: Value = ServiceType::NotifyReload.into();
assert_eq!(ServiceType::try_from(v).unwrap(), ServiceType::NotifyReload);
}
}