#[cfg(feature = "serde")]
mod bool;
mod netplan;
pub use netplan::*;
use std::collections::HashMap;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "derive_builder")]
use derive_builder::Builder;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "derive_builder", derive(Builder))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NetplanConfig {
pub network: NetworkConfig,
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "derive_builder", derive(Builder))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NetworkConfig {
pub version: u8,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub renderer: Option<Renderer>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ethernets: Option<HashMap<String, EthernetConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub modems: Option<HashMap<String, ModemConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub wifis: Option<HashMap<String, WifiConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub bonds: Option<HashMap<String, BondConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub bridges: Option<HashMap<String, BridgeConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub vlans: Option<HashMap<String, VlanConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub tunnels: Option<HashMap<String, TunnelConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub vrfs: Option<HashMap<String, VrfsConfig>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub dummy_devices: Option<HashMap<String, DummyDeviceConfig>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum Renderer {
#[cfg_attr(feature = "serde", serde(rename = "networkd"))]
Networkd,
#[cfg_attr(feature = "serde", serde(rename = "NetworkManager"))]
NetworkManager,
#[cfg_attr(feature = "serde", serde(rename = "sriov"))]
Sriov,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UseDomains {
Boolean(bool),
Route,
}
#[cfg(feature = "serde")]
impl Serialize for UseDomains {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
UseDomains::Boolean(b) => serializer.serialize_bool(*b),
UseDomains::Route => serializer.serialize_str("route"),
}
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for UseDomains {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct UseDomainsVisitor;
impl serde::de::Visitor<'_> for UseDomainsVisitor {
type Value = UseDomains;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a boolean, or the string \"route\"")
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UseDomains::Boolean(v))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v.to_lowercase().as_str() {
"true" | "yes" | "on" | "y" => Ok(UseDomains::Boolean(true)),
"false" | "no" | "off" | "n" => Ok(UseDomains::Boolean(false)),
"route" => Ok(UseDomains::Route),
_ => Err(serde::de::Error::unknown_variant(
v,
&["true", "false", "yes", "no", "on", "off", "y", "n", "route"],
)),
}
}
}
deserializer.deserialize_any(UseDomainsVisitor)
}
}
#[cfg(feature = "schemars")]
impl schemars::JsonSchema for UseDomains {
fn schema_name() -> std::borrow::Cow<'static, str> {
"UseDomains".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"anyOf": [
{ "type": "boolean" },
{ "type": "string", "enum": ["route"] }
]
})
}
}
#[cfg(test)]
mod test {
use crate::NetplanConfig;
#[test]
fn yaml_booleans() {
let input = r#"
network:
version: 2
ethernets:
nics:
match:
name: ens*
dhcp4: on
dhcp6: N
"#;
let netplan_config: NetplanConfig = serde_yaml::from_str(&input).unwrap();
let ethernets = netplan_config.network.ethernets.unwrap();
let ethernet = ethernets.values().next().unwrap();
assert!(ethernet.common_all.is_some());
let common = ethernet.common_all.as_ref().unwrap();
assert_eq!(common.dhcp4, Some(true));
}
}