Skip to content

Commit

Permalink
🏗️ Parse into intermediate list of elements
Browse files Browse the repository at this point in the history
The list of elements idea was inspired by work performed by @elmarco over in dbus2#23 , which brings a lot of benefits that I was trying to provide using explicit low-level readers and writers of XML events: namely that we have access to element sequence (important for "last `<type>` wins" and the correct interpolation of `<include>`). serde offers a [try_from](https://serde.rs/container-attrs.html#try_from) attribute that allows us to abstract this away from the consumer.
  • Loading branch information
jokeyrhyme authored and zeenix committed Nov 27, 2024
1 parent b331a0c commit 3cf5550
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 172 deletions.
94 changes: 72 additions & 22 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use anyhow::{Error, Result};
use quick_xml::{events::Event, Reader};
use serde::Deserialize;

mod transform;

const EXPECTED_DOCTYPE_PARTS: &[&str] = &[
"busconfig",
"PUBLIC",
Expand All @@ -19,11 +17,11 @@ const EXPECTED_DOCTYPE_PARTS: &[&str] = &[

/// implements [`dbus-daemon`'s Configuration File](https://dbus.freedesktop.org/doc/dbus-daemon.1.html#configuration_file)
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(try_from = "Document")]
pub struct BusConfig {
/// If `Some`, connections that authenticated using the ANONYMOUS mechanism will be authorized to connect.
/// This option has no practical effect unless the ANONYMOUS mechanism has also been enabled using the `auth` option.
// TODO: consider finding a way to make this `bool` instead of `Option<()>`
pub allow_anonymous: Option<()>,
pub allow_anonymous: bool,

/// Lists permitted authorization mechanisms.
/// If this element doesn't exist, then all known mechanisms are allowed.
Expand All @@ -33,18 +31,16 @@ pub struct BusConfig {
pub auth: HashSet<String>,

/// If `Some`, the bus daemon becomes a real daemon (forks into the background, etc.).
// TODO: consider finding a way to make this `bool` instead of `Option<()>`
pub fork: Option<()>,
pub fork: bool,

/// If `Some`, the bus daemon keeps its original umask when forking.
/// This may be useful to avoid affecting the behavior of child processes.
// TODO: consider finding a way to make this `bool` instead of `Option<()>`
pub keep_umask: Option<()>,
pub keep_umask: bool,

/// Address(es) that the bus should listen on.
/// The address is in the standard D-Bus format that contains a transport name plus possible parameters/options.
#[serde(default)]
pub listen: Vec<String>,
pub listen: HashSet<String>,

/// The bus daemon will write its pid to the specified file.
pub pidfile: Option<PathBuf>,
Expand All @@ -55,8 +51,7 @@ pub struct BusConfig {
pub servicedir: Vec<PathBuf>,

/// If `Some`, the bus daemon will log to syslog.
// TODO: consider finding a way to make this `bool` instead of `Option<()>`
pub syslog: Option<()>,
pub syslog: bool,

/// This element only controls which message bus specific environment variables are set in activated clients.
pub r#type: Option<Type>,
Expand All @@ -67,6 +62,37 @@ pub struct BusConfig {
pub user: Option<String>,
}

impl TryFrom<Document> for BusConfig {
type Error = Error;

fn try_from(value: Document) -> Result<Self> {
let mut bc = BusConfig::default();

for element in value.busconfig {
match element {
Element::AllowAnonymous => bc.allow_anonymous = true,
Element::Auth(s) => {
bc.auth.insert(s);
}
Element::Fork => bc.fork = true,
Element::KeepUmask => bc.keep_umask = true,
Element::Listen(s) => {
bc.listen.insert(s);
}
Element::Pidfile(p) => bc.pidfile = Some(p),
Element::Servicedir(p) => {
bc.servicedir.push(p);
}
Element::Syslog => bc.syslog = true,
Element::Type(TypeElement { r#type: value }) => bc.r#type = Some(value),
Element::User(s) => bc.user = Some(s),
}
}

Ok(bc)
}
}

impl BusConfig {
pub fn parse(s: &str) -> Result<Self> {
let mut reader = Reader::from_reader(s.as_bytes());
Expand Down Expand Up @@ -104,10 +130,7 @@ impl BusConfig {
}
}

let reader = transform::resolve_includes(s.as_bytes())?;
let reader = transform::last_occurrence_wins(reader.into_inner().as_slice())?;

quick_xml::de::from_reader(reader.into_inner().as_slice()).map_err(Error::from)
quick_xml::de::from_str(s).map_err(Error::from)
}
}

Expand All @@ -118,6 +141,33 @@ pub enum Type {
System,
}

#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
struct Document {
#[serde(rename = "$value", default)]
busconfig: Vec<Element>,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
enum Element {
AllowAnonymous,
Auth(String),
Fork,
KeepUmask,
Listen(String),
Pidfile(PathBuf),
Servicedir(PathBuf),
Syslog,
Type(TypeElement),
User(String),
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
struct TypeElement {
#[serde(rename = "$text")]
r#type: Type,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -188,10 +238,10 @@ mod tests {
assert_eq!(
busconfig,
BusConfig {
allow_anonymous: Some(()),
fork: Some(()),
keep_umask: Some(()),
syslog: Some(()),
allow_anonymous: true,
fork: true,
keep_umask: true,
syslog: true,
..Default::default()
}
);
Expand Down Expand Up @@ -230,11 +280,11 @@ mod tests {
assert_eq!(
busconfig,
BusConfig {
listen: vec![
listen: HashSet::from_iter(vec![
String::from("unix:path=/tmp/foo"),
String::from("tcp:host=localhost,port=1234"),
String::from("tcp:host=localhost,port=0,family=ipv4"),
],
]),
..Default::default()
}
);
Expand Down Expand Up @@ -275,7 +325,7 @@ mod tests {
assert_eq!(
busconfig,
BusConfig {
servicedir: vec![PathBuf::from("/example"), PathBuf::from("/anotherexample"),],
servicedir: vec![PathBuf::from("/example"), PathBuf::from("/anotherexample")],
..Default::default()
}
);
Expand Down
150 changes: 0 additions & 150 deletions src/config/transform.rs

This file was deleted.

0 comments on commit 3cf5550

Please sign in to comment.