use figment::value::Value;
use kdl::{KdlDocument, KdlEntry, KdlNode};
use crate::dominatrix::{Config, Firewall, Handler, Server, Variant};
impl From<Config> for KdlDocument {
fn from(config: Config) -> Self {
let mut doc = Self::new();
let nodes = doc.nodes_mut();
nodes.push(config.initial_seed.to_kdl_node("initial-seed"));
nodes.push(
config
.state_directory
.display()
.to_string()
.to_kdl_node("state-directory"),
);
if let Some(id) = config.instance_id {
nodes.push(id.to_kdl_node("instance-id"));
}
for (name, server) in &config.servers {
nodes.push(server.to_kdl_node(name));
}
for (name, handler) in &config.handlers {
nodes.push(handler.to_kdl_node(name));
}
if let Some(firewall) = config.firewall {
nodes.push(firewall.to_kdl_node("firewall"));
}
doc
}
}
trait KdlNodeWriteExt {
fn to_kdl_node(&self, name: &str) -> KdlNode;
}
impl KdlNodeWriteExt for String {
fn to_kdl_node(&self, name: &str) -> KdlNode {
let mut node = KdlNode::new(name);
node.push(KdlEntry::new(self.to_owned()));
node
}
}
impl KdlNodeWriteExt for Server {
fn to_kdl_node(&self, server_name: &str) -> KdlNode {
let (name, prefix) = match &self.variant {
Variant::Http(_) => ("http-server", "http:"),
Variant::HaproxySPOA(_) => ("haproxy-spoa-server", "haproxy-spoa:"),
Variant::Prometheus(_) => ("prometheus-server", "prometheus:"),
};
let mut node = KdlNode::new(name);
node.push(KdlEntry::new(
server_name.strip_prefix(prefix).unwrap_or(server_name),
));
let children = node.ensure_children();
let child_nodes = children.nodes_mut();
let mut bind = KdlNode::new("bind");
bind.push(KdlEntry::new(self.bind.to_string()));
if let Some(socket_access) = self.unix_socket_access {
bind.insert("unix-socket-access", socket_access.to_string());
}
child_nodes.push(bind);
match &self.variant {
Variant::Http(rq) | Variant::HaproxySPOA(rq) => {
if let Some(seed) = &rq.initial_seed {
child_nodes.push(seed.to_kdl_node("initial-seed"));
}
let mut uses = KdlNode::new("use");
uses.insert("handler-from", rq.uses.handler_from.clone());
if let Some(metrics) = &rq.uses.metrics {
uses.insert(
"metrics",
metrics.strip_prefix("prometheus:").unwrap_or(metrics),
);
}
child_nodes.push(uses);
}
Variant::Prometheus(cfg) => {
if let Some(persist_path) = &cfg.persist_path {
let mut persist = KdlNode::new("persist");
persist.insert("path", persist_path.display().to_string());
persist.insert("interval", cfg.persist_interval.clone());
child_nodes.push(persist);
}
}
}
node
}
}
impl KdlNodeWriteExt for Firewall {
fn to_kdl_node(&self, _dummy: &str) -> KdlNode {
let mut node = KdlNode::new("firewall");
let children = node.ensure_children();
let child_nodes = children.nodes_mut();
let make_child = |name, value: KdlEntry| {
let mut prop = KdlNode::new(name);
prop.push(value);
prop
};
let make_bool = |name, state: bool| {
let mut prop = KdlNode::new(name);
if !state {
prop.push(state);
}
prop
};
if let Some(value) = self.enable {
child_nodes.push(make_bool("enable", value));
}
if let Some(table_name) = &self.table {
child_nodes.push(make_child("table", table_name.clone().into()));
}
if let Some(timeout) = &self.timeout {
child_nodes.push(make_child("timeout", timeout.clone().into()));
}
if let Some(gc_interval) = &self.gc_interval {
child_nodes.push(make_child("gc-interval", gc_interval.clone().into()));
}
if let Some(size) = self.size {
child_nodes.push(make_child("size", i128::from(size).into()));
}
if let Some(priority) = self.priority {
child_nodes.push(make_child("priorty", i128::from(priority).into()));
}
if let Some(value) = self.counters {
child_nodes.push(make_bool("counters", value));
}
if !self.allow.is_empty() {
let mut allows = KdlNode::new("allow");
for entry in &self.allow {
allows.push(entry.to_string());
}
child_nodes.push(allows);
}
let mut batch_entry = KdlNode::new("batch");
if let Some(batch_size) = self.batch_size {
batch_entry.push(("size", batch_size as i128));
}
if let Some(batch_flush_interval) = self.batch_flush_interval {
batch_entry.push(("flush-interval", i128::from(batch_flush_interval)));
}
if self.batch_size.is_some() || self.batch_flush_interval.is_some() {
child_nodes.push(batch_entry);
}
node
}
}
impl KdlNodeWriteExt for Handler {
fn to_kdl_node(&self, handler_name: &str) -> KdlNode {
let mut node = KdlNode::new("declare-handler");
node.push(KdlEntry::new(handler_name.to_owned()));
if let Some(path) = &self.path {
node.insert("path", path.display().to_string());
}
node.insert("language", self.language.to_string());
if let Some(compiler) = &self.compiler {
node.insert("compiler", compiler.display().to_string());
}
if let Some(config) = &self.config
&& !config.is_empty()
{
let children = node.ensure_children();
let child_nodes = children.nodes_mut();
for (name, value) in config {
let node = figment_value_to_kdl_node(name, value);
child_nodes.push(node);
}
}
node
}
}
#[allow(clippy::cast_possible_wrap)]
fn figment_value_to_kdl_node(name: &str, v: &Value) -> KdlNode {
let mut node = KdlNode::new(name.to_owned());
match v {
Value::String(_, s) => {
node.push(KdlEntry::new(s.to_owned()));
}
Value::Char(_, c) => {
node.push(KdlEntry::new(c.to_string()));
}
Value::Bool(_, b) => {
node.push(KdlEntry::new(*b));
}
Value::Num(_, n) => {
if let Some(i) = n.to_i128() {
node.push(KdlEntry::new(i));
} else if let Some(i) = n.to_u128() {
node.push(KdlEntry::new(i as i128));
}
}
Value::Empty(_, _) => {}
Value::Dict(_, d) => {
if !d.is_empty() {
let children = node.ensure_children();
let child_nodes = children.nodes_mut();
for (name, value) in d {
let node = figment_value_to_kdl_node(name, value);
child_nodes.push(node);
}
}
}
Value::Array(_, a) => {
for value in a {
let array_node = figment_value_to_kdl_node("", value);
for entry in array_node.entries() {
node.push(entry.clone());
}
}
}
}
node
}