Skip to content

Commit

Permalink
netdog: Move to quick-xml for XML serialization
Browse files Browse the repository at this point in the history
`serde_xml_rs` has a few bugs that we were able to work around, but the
inability to serialize a `Vec` is something we can't work around.  This
change moves the netdog code to `quick-xml` for serialization.
  • Loading branch information
zmrow committed Jul 28, 2022
1 parent 1c165f5 commit e818b7d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 33 deletions.
12 changes: 11 additions & 1 deletion sources/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sources/api/netdog/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ ipnet = { version = "2.0", features = ["serde"] }
indexmap = { version = "1.8", features = ["serde"]}
envy = "0.4"
lazy_static = "1.2"
quick-xml = {version = "0.23", features = ["serialize"]}
rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] }
regex = "1.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde_plain = "1.0"
serde-xml-rs = "0.5"
snafu = "0.7"
toml = { version = "0.5", features = ["preserve_order"] }

Expand Down
44 changes: 13 additions & 31 deletions sources/api/netdog/src/wicked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const WICKED_FILE_EXT: &str = "xml";
#[derive(Debug, Serialize, PartialEq)]
#[serde(rename = "interface")]
pub(crate) struct WickedInterface {
#[serde(rename = "$unflatten=name")]
name: InterfaceName,
control: WickedControl,
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -29,18 +30,11 @@ pub(crate) struct WickedInterface {
#[derive(Debug, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
struct WickedControl {
#[serde(rename = "$unflatten=mode")]
#[serde(skip_serializing_if = "Option::is_none")]
mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
link_detection: Option<LinkDetection>,
// TODO: `serde_xml_rs` has a known issue with serializing nested structures, where it will
// insert additional tag with the structure name. It has since been fixed but not released
// officially yet. This struct member works around that issue.
// https://github.com/RReverser/serde-xml-rs/issues/126
// The workaround:
// https://stackoverflow.com/questions/70124048/how-to-create-xml-from-struct-in-rust
#[serde(flatten, skip)]
_f: (),
}

// We assume that all configured interfaces are wanted at boot and will require a link to
Expand All @@ -50,7 +44,6 @@ impl Default for WickedControl {
WickedControl {
mode: Some("boot".to_string()),
link_detection: Some(LinkDetection::default()),
_f: (),
}
}
}
Expand All @@ -59,23 +52,23 @@ impl Default for WickedControl {
#[serde(rename_all = "kebab-case")]
struct LinkDetection {
// This will serialize to an empty tag
#[serde(rename = "$unflatten=require-link")]
require_link: (),
#[serde(flatten, skip)]
_f: (),
}

#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub(crate) struct WickedDhcp4 {
#[serde(rename = "$unflatten=enabled")]
enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "$unflatten=route-priority")]
route_priority: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "$unflatten=defer-timeout")]
defer_timeout: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
flags: Option<AddrConfFlags>,
#[serde(flatten, skip)]
_f: (),
}

impl Default for WickedDhcp4 {
Expand All @@ -85,21 +78,20 @@ impl Default for WickedDhcp4 {
route_priority: None,
defer_timeout: None,
flags: None,
_f: (),
}
}
}

#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub(crate) struct WickedDhcp6 {
#[serde(rename = "$unflatten=enabled")]
enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "$unflatten=defer-timeout")]
defer_timeout: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
flags: Option<AddrConfFlags>,
#[serde(flatten, skip)]
_f: (),
}

impl Default for WickedDhcp6 {
Expand All @@ -108,7 +100,6 @@ impl Default for WickedDhcp6 {
enabled: true,
defer_timeout: None,
flags: None,
_f: (),
}
}
}
Expand All @@ -117,17 +108,15 @@ impl Default for WickedDhcp6 {
// the user, a struct makes handling tags much simpler.
#[derive(Default, Clone, Debug, Serialize, PartialEq)]
struct AddrConfFlags {
#[serde(rename = "$unflatten=optional")]
optional: (),
#[serde(flatten, skip)]
_f: (),
}

impl From<Dhcp4Config> for WickedDhcp4 {
fn from(dhcp4: Dhcp4Config) -> Self {
match dhcp4 {
Dhcp4Config::DhcpEnabled(b) => WickedDhcp4 {
enabled: b,
_f: (),
..Default::default()
},
Dhcp4Config::WithOptions(o) => WickedDhcp4::from(o),
Expand All @@ -150,7 +139,6 @@ impl From<Dhcp4Options> for WickedDhcp4 {
route_priority: options.route_metric,
defer_timeout,
flags,
_f: (),
}
}
}
Expand All @@ -160,7 +148,6 @@ impl From<Dhcp6Config> for WickedDhcp6 {
match dhcp6 {
Dhcp6Config::DhcpEnabled(b) => WickedDhcp6 {
enabled: b,
_f: (),
..Default::default()
},
Dhcp6Config::WithOptions(o) => WickedDhcp6::from(o),
Expand All @@ -182,7 +169,6 @@ impl From<Dhcp6Options> for WickedDhcp6 {
enabled: options.enabled,
defer_timeout,
flags,
_f: (),
}
}
}
Expand All @@ -208,11 +194,7 @@ impl WickedInterface {
let mut cfg_path = Path::new(WICKED_CONFIG_DIR).join(self.name.to_string());
cfg_path.set_extension(WICKED_FILE_EXT);

// TODO: pretty print these files. `serde_xml_rs` doesn't support pretty printing;
// `quick_xml` does, however we require a few features that haven't been released yet to
// properly serialize the above data structures:
// https://github.com/tafia/quick-xml/issues/340#issuecomment-981093602
let xml = serde_xml_rs::to_string(&self).context(error::XmlSerializeSnafu {
let xml = quick_xml::se::to_string(&self).context(error::XmlSerializeSnafu {
interface: self.name.to_string(),
})?;
fs::write(&cfg_path, xml).context(error::WickedConfigWriteSnafu { path: cfg_path })
Expand All @@ -233,7 +215,7 @@ mod error {
#[snafu(display("Error serializing config for '{}' to XML: {}", interface, source))]
XmlSerialize {
interface: String,
source: serde_xml_rs::Error,
source: quick_xml::DeError,
},
}
}
Expand Down Expand Up @@ -276,7 +258,7 @@ mod tests {

for (name, config) in net_config.interfaces {
let interface = WickedInterface::from_config(name, config);
let generated = serde_xml_rs::to_string(&interface).unwrap();
let generated = quick_xml::se::to_string(&interface).unwrap();

let mut path = test_data().join(interface.name.to_string());
path.set_extension("xml");
Expand All @@ -299,7 +281,7 @@ mod tests {
let expected = fs::read_to_string(path).unwrap();

let interface = WickedInterface::from_config(name, config);
let generated = serde_xml_rs::to_string(&interface).unwrap();
let generated = quick_xml::se::to_string(&interface).unwrap();

assert_eq!(expected.trim(), generated)
}
Expand Down

0 comments on commit e818b7d

Please sign in to comment.