use std::num::NonZeroU16;
use indexmap::IndexMap;
use isoloom_spec::syntax::{OsRef, PortName, PortProtocol};
use isoloom_spec::{Appliance, Tc};
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct SpecDoc {
pub(crate) version: u8,
pub(crate) name: String,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub(crate) networks: IndexMap<String, NetworkDoc>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) reach: Vec<ReachDoc>,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub(crate) machines: IndexMap<String, MachineDoc>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) provision: Vec<ProvisionDoc>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct ProvisionDoc {
pub(crate) ansible: String,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct NetworkDoc {
pub(crate) cidr: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) internet: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) gateway: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) switch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) tc: Option<Tc>,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub(crate) vlans: IndexMap<u16, VlanDoc>,
}
impl NetworkDoc {
pub(crate) fn new(cidr: String, internet: bool, tc: Option<Tc>) -> NetworkDoc {
NetworkDoc {
cidr,
internet: (!internet).then_some(false),
gateway: None,
switch: None,
tc,
vlans: IndexMap::new(),
}
}
pub(crate) fn reaches_internet(&self) -> bool {
self.internet.unwrap_or(true)
}
pub(crate) fn set_internet(&mut self, internet: bool) {
self.internet = (!internet).then_some(false);
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct VlanDoc {
pub(crate) cidr: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) internet: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct ReachDoc {
pub(crate) from: String,
pub(crate) to: String,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub(crate) struct MachineDoc {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) os: Option<OsRef>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) app: Option<AppDoc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) command: Option<Vec<String>>,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub(crate) networks: IndexMap<String, u8>,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub(crate) ports: IndexMap<PortName, PortDoc>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub(crate) access: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub(crate) struct AppDoc {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) appliance: Option<Appliance>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) config: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) firmware: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) steps: Vec<StepDoc>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum StepDoc {
Run(String),
Playbook(String),
}
impl Serialize for StepDoc {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let (key, value) = match self {
StepDoc::Run(script) => ("run", script),
StepDoc::Playbook(file) => ("playbook", file),
};
let mut map = s.serialize_map(Some(1))?;
map.serialize_entry(key, value)?;
map.end()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(untagged)]
pub(crate) enum PortDoc {
Tcp(NonZeroU16),
Speaking { port: NonZeroU16, protocol: PortProtocol },
}