//! The config module contains the structures and methods needed to create properly formatted
//! systemd-networkd configuration files
mod netdev;
mod network;

use super::Result;
pub(crate) use netdev::{NetDevBuilder, NetDevConfig};
pub(crate) use network::{NetworkBuilder, NetworkConfig};

const NETWORKD_CONFIG_DIR: &str = "/etc/systemd/network";
const CONFIG_FILE_PREFIX: &str = "10-";

pub(crate) enum NetworkDConfigFile {
    Network(NetworkConfig),
    NetDev(NetDevConfig),
}

impl NetworkDConfigFile {
    pub(crate) fn write_config_file(&self) -> Result<()> {
        match self {
            NetworkDConfigFile::Network(network) => network.write_config_file(NETWORKD_CONFIG_DIR),
            NetworkDConfigFile::NetDev(netdev) => netdev.write_config_file(NETWORKD_CONFIG_DIR),
        }
    }
}

// This private module defines some empty traits meant to be used as type parameters for the
// networkd config builders.  The type parameters limit the methods that can be called on the
// builders so a user of this code can't inadvertently add configuration options that aren't
// applicable to a particular device.  For example, a user can't add bond monitoring options to a
// VLAN config.
//
// The following traits and enums are only meant to be used within the config module of this crate;
// putting them in a private module guarantees this behavior.  See the "sealed trait" pattern here:
// https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
mod private {
    // The following zero-variant enums represent the device types we currently support.  They
    // cannot be constructed and exist only as phantom types.
    pub enum Bond {}
    pub enum Interface {}
    pub enum Vlan {}
    pub enum BondWorker {} // interfaces that are bound to a bond

    // The devices for which we are generating a configuration file.  All device types should
    // implement this trait.
    pub trait Device {}
    impl Device for Bond {}
    impl Device for Interface {}
    impl Device for Vlan {}
    impl Device for BondWorker {}

    // Devices not bound to a bond, i.e. everything EXCEPT BondWorker(s)
    pub trait NotBonded {}
    impl NotBonded for Bond {}
    impl NotBonded for Interface {}
    impl NotBonded for Vlan {}
}

#[cfg(test)]
mod tests {
    use crate::networkd::devices::{NetworkDBond, NetworkDInterface, NetworkDVlan};
    use serde::Deserialize;
    use std::fs;
    use std::path::{Path, PathBuf};

    pub(super) const BUILDER_DATA: &str = include_str!("../../../test_data/networkd/builder.toml");

    pub(super) fn test_data() -> PathBuf {
        PathBuf::from(env!("CARGO_MANIFEST_DIR"))
            .join("test_data")
            .join("networkd")
    }

    #[derive(Debug, Deserialize)]
    pub(super) struct TestDevices {
        pub(super) interface: Vec<NetworkDInterface>,
        pub(super) bond: Vec<NetworkDBond>,
        pub(super) vlan: Vec<NetworkDVlan>,
    }
}