#![allow(clippy::result_large_err)]
use figment::{
Error, Metadata, Profile, Provider, Source, error,
value::{Dict, Map},
};
use kdl::KdlDocument;
use std::fs;
use std::path::PathBuf;
mod format;
mod parse;
use parse::ParseExt;
mod traits;
use traits::KdlNodeExt;
use iocaine_powder::bullshit::SquashFS;
#[derive(Default)]
pub struct Cuddles {
file: Option<PathBuf>,
}
struct Snuggles(KdlDocument);
impl From<KdlDocument> for Snuggles {
fn from(k: KdlDocument) -> Self {
Self(k)
}
}
impl Provider for Cuddles {
fn metadata(&self) -> Metadata {
Metadata::named("KDL file").source(
self.file
.clone()
.map_or_else(|| Source::Custom("default".into()), Source::File),
)
}
fn data(&self) -> Result<Map<Profile, Dict>, Error> {
let data = match &self.file {
Some(f) => fs::read_to_string(f).map_err(|e| Error::from(format!("{e}")))?,
None => match SquashFS::get("/defaults/config.kdl") {
Some(v) => String::from_utf8_lossy(&v).to_string(),
None => return Err(Error::from("Unable to find default config file")),
},
};
let doc: Snuggles = data
.parse::<KdlDocument>()
.map_err(|e| Error::from(format!("{e}")))?
.into();
let map = doc.parse()?;
match &self.profile() {
Some(profile) => Ok(profile.collect(map)),
None => Ok(figment::Profile::Default.collect(map)),
}
}
}
impl Cuddles {
pub fn file(path: impl Into<PathBuf>) -> Self {
Self {
file: Some(path.into()),
}
}
}
impl Snuggles {
pub fn parse(&self) -> Result<Dict, Error> {
let mut dict = Dict::new();
let mut servers = Dict::new();
let mut handlers = Dict::new();
for node in self.0.nodes() {
match node.name().value() {
"initial-seed" | "initial-seed-file" | "instance-id" | "state-directory" => {
let value = node
.assert_no_children()?
.assert_single_argument()?
.get_first_string_argument()?;
dict.insert(node.name().value().into(), value.into());
}
"http-server" => {
let (name, server) = node.parse_request_server("http")?;
servers.insert(name, server);
}
"haproxy-spoa-server" => {
let (name, server) = node.parse_request_server("haproxy-spoa")?;
servers.insert(name, server);
}
"prometheus-server" => {
let (name, server) = node.parse_prometheus_server()?;
servers.insert(name, server);
}
"declare-handler" => {
let (name, handler) = node.parse_declare_handler()?;
handlers.insert(name.into(), handler.into());
}
"firewall" => {
dict.insert("firewall".into(), node.parse_firewall()?.into());
}
name => {
let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
return Err(Error::from(kind));
}
}
}
dict.insert("server".into(), servers.into());
dict.insert("handler".into(), handlers.into());
Ok(dict)
}
}